diff options
Diffstat (limited to 'deps/npm/node_modules/npm-profile/node_modules/make-fetch-happen/node_modules/node-fetch-npm/src/body.js')
-rw-r--r-- | deps/npm/node_modules/npm-profile/node_modules/make-fetch-happen/node_modules/node-fetch-npm/src/body.js | 411 |
1 files changed, 411 insertions, 0 deletions
diff --git a/deps/npm/node_modules/npm-profile/node_modules/make-fetch-happen/node_modules/node-fetch-npm/src/body.js b/deps/npm/node_modules/npm-profile/node_modules/make-fetch-happen/node_modules/node-fetch-npm/src/body.js new file mode 100644 index 0000000000..9e7481857a --- /dev/null +++ b/deps/npm/node_modules/npm-profile/node_modules/make-fetch-happen/node_modules/node-fetch-npm/src/body.js @@ -0,0 +1,411 @@ +'use strict' + +/** + * body.js + * + * Body interface provides common methods for Request and Response + */ + +const Buffer = require('safe-buffer').Buffer + +const Blob = require('./blob.js') +const BUFFER = Blob.BUFFER +const convert = require('encoding').convert +const parseJson = require('json-parse-better-errors') +const FetchError = require('./fetch-error.js') +const Stream = require('stream') + +const PassThrough = Stream.PassThrough +const DISTURBED = Symbol('disturbed') + +/** + * Body class + * + * Cannot use ES6 class because Body must be called with .call(). + * + * @param Stream body Readable stream + * @param Object opts Response options + * @return Void + */ +exports = module.exports = Body + +function Body (body, opts) { + if (!opts) opts = {} + const size = opts.size == null ? 0 : opts.size + const timeout = opts.timeout == null ? 0 : opts.timeout + if (body == null) { + // body is undefined or null + body = null + } else if (typeof body === 'string') { + // body is string + } else if (body instanceof Blob) { + // body is blob + } else if (Buffer.isBuffer(body)) { + // body is buffer + } else if (body instanceof Stream) { + // body is stream + } else { + // none of the above + // coerce to string + body = String(body) + } + this.body = body + this[DISTURBED] = false + this.size = size + this.timeout = timeout +} + +Body.prototype = { + get bodyUsed () { + return this[DISTURBED] + }, + + /** + * Decode response as ArrayBuffer + * + * @return Promise + */ + arrayBuffer () { + return consumeBody.call(this).then(buf => buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength)) + }, + + /** + * Return raw response as Blob + * + * @return Promise + */ + blob () { + let ct = (this.headers && this.headers.get('content-type')) || '' + return consumeBody.call(this).then(buf => Object.assign( + // Prevent copying + new Blob([], { + type: ct.toLowerCase() + }), + { + [BUFFER]: buf + } + )) + }, + + /** + * Decode response as json + * + * @return Promise + */ + json () { + return consumeBody.call(this).then(buffer => parseJson(buffer.toString())) + }, + + /** + * Decode response as text + * + * @return Promise + */ + text () { + return consumeBody.call(this).then(buffer => buffer.toString()) + }, + + /** + * Decode response as buffer (non-spec api) + * + * @return Promise + */ + buffer () { + return consumeBody.call(this) + }, + + /** + * Decode response as text, while automatically detecting the encoding and + * trying to decode to UTF-8 (non-spec api) + * + * @return Promise + */ + textConverted () { + return consumeBody.call(this).then(buffer => convertBody(buffer, this.headers)) + } + +} + +Body.mixIn = function (proto) { + for (const name of Object.getOwnPropertyNames(Body.prototype)) { + // istanbul ignore else: future proof + if (!(name in proto)) { + const desc = Object.getOwnPropertyDescriptor(Body.prototype, name) + Object.defineProperty(proto, name, desc) + } + } +} + +/** + * Decode buffers into utf-8 string + * + * @return Promise + */ +function consumeBody (body) { + if (this[DISTURBED]) { + return Body.Promise.reject(new Error(`body used already for: ${this.url}`)) + } + + this[DISTURBED] = true + + // body is null + if (this.body === null) { + return Body.Promise.resolve(Buffer.alloc(0)) + } + + // body is string + if (typeof this.body === 'string') { + return Body.Promise.resolve(Buffer.from(this.body)) + } + + // body is blob + if (this.body instanceof Blob) { + return Body.Promise.resolve(this.body[BUFFER]) + } + + // body is buffer + if (Buffer.isBuffer(this.body)) { + return Body.Promise.resolve(this.body) + } + + // istanbul ignore if: should never happen + if (!(this.body instanceof Stream)) { + return Body.Promise.resolve(Buffer.alloc(0)) + } + + // body is stream + // get ready to actually consume the body + let accum = [] + let accumBytes = 0 + let abort = false + + return new Body.Promise((resolve, reject) => { + let resTimeout + + // allow timeout on slow response body + if (this.timeout) { + resTimeout = setTimeout(() => { + abort = true + reject(new FetchError(`Response timeout while trying to fetch ${this.url} (over ${this.timeout}ms)`, 'body-timeout')) + }, this.timeout) + } + + // handle stream error, such as incorrect content-encoding + this.body.on('error', err => { + reject(new FetchError(`Invalid response body while trying to fetch ${this.url}: ${err.message}`, 'system', err)) + }) + + this.body.on('data', chunk => { + if (abort || chunk === null) { + return + } + + if (this.size && accumBytes + chunk.length > this.size) { + abort = true + reject(new FetchError(`content size at ${this.url} over limit: ${this.size}`, 'max-size')) + return + } + + accumBytes += chunk.length + accum.push(chunk) + }) + + this.body.on('end', () => { + if (abort) { + return + } + + clearTimeout(resTimeout) + resolve(Buffer.concat(accum)) + }) + }) +} + +/** + * Detect buffer encoding and convert to target encoding + * ref: http://www.w3.org/TR/2011/WD-html5-20110113/parsing.html#determining-the-character-encoding + * + * @param Buffer buffer Incoming buffer + * @param String encoding Target encoding + * @return String + */ +function convertBody (buffer, headers) { + const ct = headers.get('content-type') + let charset = 'utf-8' + let res, str + + // header + if (ct) { + res = /charset=([^;]*)/i.exec(ct) + } + + // no charset in content type, peek at response body for at most 1024 bytes + str = buffer.slice(0, 1024).toString() + + // html5 + if (!res && str) { + res = /<meta.+?charset=(['"])(.+?)\1/i.exec(str) + } + + // html4 + if (!res && str) { + res = /<meta[\s]+?http-equiv=(['"])content-type\1[\s]+?content=(['"])(.+?)\2/i.exec(str) + + if (res) { + res = /charset=(.*)/i.exec(res.pop()) + } + } + + // xml + if (!res && str) { + res = /<\?xml.+?encoding=(['"])(.+?)\1/i.exec(str) + } + + // found charset + if (res) { + charset = res.pop() + + // prevent decode issues when sites use incorrect encoding + // ref: https://hsivonen.fi/encoding-menu/ + if (charset === 'gb2312' || charset === 'gbk') { + charset = 'gb18030' + } + } + + // turn raw buffers into a single utf-8 buffer + return convert( + buffer + , 'UTF-8' + , charset + ).toString() +} + +/** + * Clone body given Res/Req instance + * + * @param Mixed instance Response or Request instance + * @return Mixed + */ +exports.clone = function clone (instance) { + let p1, p2 + let body = instance.body + + // don't allow cloning a used body + if (instance.bodyUsed) { + throw new Error('cannot clone body after it is used') + } + + // check that body is a stream and not form-data object + // note: we can't clone the form-data object without having it as a dependency + if ((body instanceof Stream) && (typeof body.getBoundary !== 'function')) { + // tee instance body + p1 = new PassThrough() + p2 = new PassThrough() + body.pipe(p1) + body.pipe(p2) + // set instance body to teed body and return the other teed body + instance.body = p1 + body = p2 + } + + return body +} + +/** + * Performs the operation "extract a `Content-Type` value from |object|" as + * specified in the specification: + * https://fetch.spec.whatwg.org/#concept-bodyinit-extract + * + * This function assumes that instance.body is present and non-null. + * + * @param Mixed instance Response or Request instance + */ +exports.extractContentType = function extractContentType (instance) { + const body = instance.body + + // istanbul ignore if: Currently, because of a guard in Request, body + // can never be null. Included here for completeness. + if (body === null) { + // body is null + return null + } else if (typeof body === 'string') { + // body is string + return 'text/plain;charset=UTF-8' + } else if (body instanceof Blob) { + // body is blob + return body.type || null + } else if (Buffer.isBuffer(body)) { + // body is buffer + return null + } else if (typeof body.getBoundary === 'function') { + // detect form data input from form-data module + return `multipart/form-data;boundary=${body.getBoundary()}` + } else { + // body is stream + // can't really do much about this + return null + } +} + +exports.getTotalBytes = function getTotalBytes (instance) { + const body = instance.body + + // istanbul ignore if: included for completion + if (body === null) { + // body is null + return 0 + } else if (typeof body === 'string') { + // body is string + return Buffer.byteLength(body) + } else if (body instanceof Blob) { + // body is blob + return body.size + } else if (Buffer.isBuffer(body)) { + // body is buffer + return body.length + } else if (body && typeof body.getLengthSync === 'function') { + // detect form data input from form-data module + if (( + // 1.x + body._lengthRetrievers && + body._lengthRetrievers.length === 0 + ) || ( + // 2.x + body.hasKnownLength && body.hasKnownLength() + )) { + return body.getLengthSync() + } + return null + } else { + // body is stream + // can't really do much about this + return null + } +} + +exports.writeToStream = function writeToStream (dest, instance) { + const body = instance.body + + if (body === null) { + // body is null + dest.end() + } else if (typeof body === 'string') { + // body is string + dest.write(body) + dest.end() + } else if (body instanceof Blob) { + // body is blob + dest.write(body[BUFFER]) + dest.end() + } else if (Buffer.isBuffer(body)) { + // body is buffer + dest.write(body) + dest.end() + } else { + // body is stream + body.pipe(dest) + } +} + +// expose Promise +Body.Promise = global.Promise |