aboutsummaryrefslogtreecommitdiff
path: root/node_modules/node-fetch/lib
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/node-fetch/lib')
-rw-r--r--node_modules/node-fetch/lib/body.js260
-rw-r--r--node_modules/node-fetch/lib/fetch-error.js34
-rw-r--r--node_modules/node-fetch/lib/headers.js141
-rw-r--r--node_modules/node-fetch/lib/request.js75
-rw-r--r--node_modules/node-fetch/lib/response.js50
5 files changed, 560 insertions, 0 deletions
diff --git a/node_modules/node-fetch/lib/body.js b/node_modules/node-fetch/lib/body.js
new file mode 100644
index 000000000..e7bbe1dac
--- /dev/null
+++ b/node_modules/node-fetch/lib/body.js
@@ -0,0 +1,260 @@
+
+/**
+ * body.js
+ *
+ * Body interface provides common methods for Request and Response
+ */
+
+var convert = require('encoding').convert;
+var bodyStream = require('is-stream');
+var PassThrough = require('stream').PassThrough;
+var FetchError = require('./fetch-error');
+
+module.exports = Body;
+
+/**
+ * Body class
+ *
+ * @param Stream body Readable stream
+ * @param Object opts Response options
+ * @return Void
+ */
+function Body(body, opts) {
+
+ opts = opts || {};
+
+ this.body = body;
+ this.bodyUsed = false;
+ this.size = opts.size || 0;
+ this.timeout = opts.timeout || 0;
+ this._raw = [];
+ this._abort = false;
+
+}
+
+/**
+ * Decode response as json
+ *
+ * @return Promise
+ */
+Body.prototype.json = function() {
+
+ // for 204 No Content response, buffer will be empty, parsing it will throw error
+ if (this.status === 204) {
+ return Body.Promise.resolve({});
+ }
+
+ return this._decode().then(function(buffer) {
+ return JSON.parse(buffer.toString());
+ });
+
+};
+
+/**
+ * Decode response as text
+ *
+ * @return Promise
+ */
+Body.prototype.text = function() {
+
+ return this._decode().then(function(buffer) {
+ return buffer.toString();
+ });
+
+};
+
+/**
+ * Decode response as buffer (non-spec api)
+ *
+ * @return Promise
+ */
+Body.prototype.buffer = function() {
+
+ return this._decode();
+
+};
+
+/**
+ * Decode buffers into utf-8 string
+ *
+ * @return Promise
+ */
+Body.prototype._decode = function() {
+
+ var self = this;
+
+ if (this.bodyUsed) {
+ return Body.Promise.reject(new Error('body used already for: ' + this.url));
+ }
+
+ this.bodyUsed = true;
+ this._bytes = 0;
+ this._abort = false;
+ this._raw = [];
+
+ return new Body.Promise(function(resolve, reject) {
+ var resTimeout;
+
+ // body is string
+ if (typeof self.body === 'string') {
+ self._bytes = self.body.length;
+ self._raw = [new Buffer(self.body)];
+ return resolve(self._convert());
+ }
+
+ // body is buffer
+ if (self.body instanceof Buffer) {
+ self._bytes = self.body.length;
+ self._raw = [self.body];
+ return resolve(self._convert());
+ }
+
+ // allow timeout on slow response body
+ if (self.timeout) {
+ resTimeout = setTimeout(function() {
+ self._abort = true;
+ reject(new FetchError('response timeout at ' + self.url + ' over limit: ' + self.timeout, 'body-timeout'));
+ }, self.timeout);
+ }
+
+ // handle stream error, such as incorrect content-encoding
+ self.body.on('error', function(err) {
+ reject(new FetchError('invalid response body at: ' + self.url + ' reason: ' + err.message, 'system', err));
+ });
+
+ // body is stream
+ self.body.on('data', function(chunk) {
+ if (self._abort || chunk === null) {
+ return;
+ }
+
+ if (self.size && self._bytes + chunk.length > self.size) {
+ self._abort = true;
+ reject(new FetchError('content size at ' + self.url + ' over limit: ' + self.size, 'max-size'));
+ return;
+ }
+
+ self._bytes += chunk.length;
+ self._raw.push(chunk);
+ });
+
+ self.body.on('end', function() {
+ if (self._abort) {
+ return;
+ }
+
+ clearTimeout(resTimeout);
+ resolve(self._convert());
+ });
+ });
+
+};
+
+/**
+ * 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 String encoding Target encoding
+ * @return String
+ */
+Body.prototype._convert = function(encoding) {
+
+ encoding = encoding || 'utf-8';
+
+ var ct = this.headers.get('content-type');
+ var charset = 'utf-8';
+ var res, str;
+
+ // header
+ if (ct) {
+ // skip encoding detection altogether if not html/xml/plain text
+ if (!/text\/html|text\/plain|\+xml|\/xml/i.test(ct)) {
+ return Buffer.concat(this._raw);
+ }
+
+ res = /charset=([^;]*)/i.exec(ct);
+ }
+
+ // no charset in content type, peek at response body for at most 1024 bytes
+ if (!res && this._raw.length > 0) {
+ for (var i = 0; i < this._raw.length; i++) {
+ str += this._raw[i].toString()
+ if (str.length > 1024) {
+ break;
+ }
+ }
+ str = str.substr(0, 1024);
+ }
+
+ // 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.concat(this._raw)
+ , encoding
+ , charset
+ );
+
+};
+
+/**
+ * Clone body given Res/Req instance
+ *
+ * @param Mixed instance Response or Request instance
+ * @return Mixed
+ */
+Body.prototype._clone = function(instance) {
+ var p1, p2;
+ var 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 (bodyStream(body) && 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;
+}
+
+// expose Promise
+Body.Promise = global.Promise;
diff --git a/node_modules/node-fetch/lib/fetch-error.js b/node_modules/node-fetch/lib/fetch-error.js
new file mode 100644
index 000000000..7cabfb3ce
--- /dev/null
+++ b/node_modules/node-fetch/lib/fetch-error.js
@@ -0,0 +1,34 @@
+
+/**
+ * fetch-error.js
+ *
+ * FetchError interface for operational errors
+ */
+
+module.exports = FetchError;
+
+/**
+ * Create FetchError instance
+ *
+ * @param String message Error message for human
+ * @param String type Error type for machine
+ * @param String systemError For Node.js system error
+ * @return FetchError
+ */
+function FetchError(message, type, systemError) {
+
+ // hide custom error implementation details from end-users
+ Error.captureStackTrace(this, this.constructor);
+
+ this.name = this.constructor.name;
+ this.message = message;
+ this.type = type;
+
+ // when err.type is `system`, err.code contains system error code
+ if (systemError) {
+ this.code = this.errno = systemError.code;
+ }
+
+}
+
+require('util').inherits(FetchError, Error);
diff --git a/node_modules/node-fetch/lib/headers.js b/node_modules/node-fetch/lib/headers.js
new file mode 100644
index 000000000..fd7a14eaa
--- /dev/null
+++ b/node_modules/node-fetch/lib/headers.js
@@ -0,0 +1,141 @@
+
+/**
+ * headers.js
+ *
+ * Headers class offers convenient helpers
+ */
+
+module.exports = Headers;
+
+/**
+ * Headers class
+ *
+ * @param Object headers Response headers
+ * @return Void
+ */
+function Headers(headers) {
+
+ var self = this;
+ this._headers = {};
+
+ // Headers
+ if (headers instanceof Headers) {
+ headers = headers.raw();
+ }
+
+ // plain object
+ for (var prop in headers) {
+ if (!headers.hasOwnProperty(prop)) {
+ continue;
+ }
+
+ if (typeof headers[prop] === 'string') {
+ this.set(prop, headers[prop]);
+
+ } else if (typeof headers[prop] === 'number' && !isNaN(headers[prop])) {
+ this.set(prop, headers[prop].toString());
+
+ } else if (headers[prop] instanceof Array) {
+ headers[prop].forEach(function(item) {
+ self.append(prop, item.toString());
+ });
+ }
+ }
+
+}
+
+/**
+ * Return first header value given name
+ *
+ * @param String name Header name
+ * @return Mixed
+ */
+Headers.prototype.get = function(name) {
+ var list = this._headers[name.toLowerCase()];
+ return list ? list[0] : null;
+};
+
+/**
+ * Return all header values given name
+ *
+ * @param String name Header name
+ * @return Array
+ */
+Headers.prototype.getAll = function(name) {
+ if (!this.has(name)) {
+ return [];
+ }
+
+ return this._headers[name.toLowerCase()];
+};
+
+/**
+ * Iterate over all headers
+ *
+ * @param Function callback Executed for each item with parameters (value, name, thisArg)
+ * @param Boolean thisArg `this` context for callback function
+ * @return Void
+ */
+Headers.prototype.forEach = function(callback, thisArg) {
+ Object.getOwnPropertyNames(this._headers).forEach(function(name) {
+ this._headers[name].forEach(function(value) {
+ callback.call(thisArg, value, name, this)
+ }, this)
+ }, this)
+}
+
+/**
+ * Overwrite header values given name
+ *
+ * @param String name Header name
+ * @param String value Header value
+ * @return Void
+ */
+Headers.prototype.set = function(name, value) {
+ this._headers[name.toLowerCase()] = [value];
+};
+
+/**
+ * Append a value onto existing header
+ *
+ * @param String name Header name
+ * @param String value Header value
+ * @return Void
+ */
+Headers.prototype.append = function(name, value) {
+ if (!this.has(name)) {
+ this.set(name, value);
+ return;
+ }
+
+ this._headers[name.toLowerCase()].push(value);
+};
+
+/**
+ * Check for header name existence
+ *
+ * @param String name Header name
+ * @return Boolean
+ */
+Headers.prototype.has = function(name) {
+ return this._headers.hasOwnProperty(name.toLowerCase());
+};
+
+/**
+ * Delete all header values given name
+ *
+ * @param String name Header name
+ * @return Void
+ */
+Headers.prototype['delete'] = function(name) {
+ delete this._headers[name.toLowerCase()];
+};
+
+/**
+ * Return raw headers (non-spec api)
+ *
+ * @return Object
+ */
+Headers.prototype.raw = function() {
+ return this._headers;
+};
diff --git a/node_modules/node-fetch/lib/request.js b/node_modules/node-fetch/lib/request.js
new file mode 100644
index 000000000..1a29c29c6
--- /dev/null
+++ b/node_modules/node-fetch/lib/request.js
@@ -0,0 +1,75 @@
+
+/**
+ * request.js
+ *
+ * Request class contains server only options
+ */
+
+var parse_url = require('url').parse;
+var Headers = require('./headers');
+var Body = require('./body');
+
+module.exports = Request;
+
+/**
+ * Request class
+ *
+ * @param Mixed input Url or Request instance
+ * @param Object init Custom options
+ * @return Void
+ */
+function Request(input, init) {
+ var url, url_parsed;
+
+ // normalize input
+ if (!(input instanceof Request)) {
+ url = input;
+ url_parsed = parse_url(url);
+ input = {};
+ } else {
+ url = input.url;
+ url_parsed = parse_url(url);
+ }
+
+ // normalize init
+ init = init || {};
+
+ // fetch spec options
+ this.method = init.method || input.method || 'GET';
+ this.redirect = init.redirect || input.redirect || 'follow';
+ this.headers = new Headers(init.headers || input.headers || {});
+ this.url = url;
+
+ // server only options
+ this.follow = init.follow !== undefined ?
+ init.follow : input.follow !== undefined ?
+ input.follow : 20;
+ this.compress = init.compress !== undefined ?
+ init.compress : input.compress !== undefined ?
+ input.compress : true;
+ this.counter = init.counter || input.counter || 0;
+ this.agent = init.agent || input.agent;
+
+ Body.call(this, init.body || this._clone(input), {
+ timeout: init.timeout || input.timeout || 0,
+ size: init.size || input.size || 0
+ });
+
+ // server request options
+ this.protocol = url_parsed.protocol;
+ this.hostname = url_parsed.hostname;
+ this.port = url_parsed.port;
+ this.path = url_parsed.path;
+ this.auth = url_parsed.auth;
+}
+
+Request.prototype = Object.create(Body.prototype);
+
+/**
+ * Clone this request
+ *
+ * @return Request
+ */
+Request.prototype.clone = function() {
+ return new Request(this);
+};
diff --git a/node_modules/node-fetch/lib/response.js b/node_modules/node-fetch/lib/response.js
new file mode 100644
index 000000000..f96aa85e8
--- /dev/null
+++ b/node_modules/node-fetch/lib/response.js
@@ -0,0 +1,50 @@
+
+/**
+ * response.js
+ *
+ * Response class provides content decoding
+ */
+
+var http = require('http');
+var Headers = require('./headers');
+var Body = require('./body');
+
+module.exports = Response;
+
+/**
+ * Response class
+ *
+ * @param Stream body Readable stream
+ * @param Object opts Response options
+ * @return Void
+ */
+function Response(body, opts) {
+
+ opts = opts || {};
+
+ this.url = opts.url;
+ this.status = opts.status || 200;
+ this.statusText = opts.statusText || http.STATUS_CODES[this.status];
+ this.headers = new Headers(opts.headers);
+ this.ok = this.status >= 200 && this.status < 300;
+
+ Body.call(this, body, opts);
+
+}
+
+Response.prototype = Object.create(Body.prototype);
+
+/**
+ * Clone this response
+ *
+ * @return Response
+ */
+Response.prototype.clone = function() {
+ return new Response(this._clone(this), {
+ url: this.url
+ , status: this.status
+ , statusText: this.statusText
+ , headers: this.headers
+ , ok: this.ok
+ });
+};