diff options
author | 2020-11-16 00:10:28 +0100 | |
---|---|---|
committer | 2020-11-16 00:10:28 +0100 | |
commit | e06ec920f7a5d784e674c4c4b4e6d1da3dc7391d (patch) | |
tree | 55713f725f77b44ebfec86e4eec3ce33e71458ca /node_modules/stream-http | |
download | website_creator-e06ec920f7a5d784e674c4c4b4e6d1da3dc7391d.tar.gz website_creator-e06ec920f7a5d784e674c4c4b4e6d1da3dc7391d.tar.bz2 website_creator-e06ec920f7a5d784e674c4c4b4e6d1da3dc7391d.zip |
api, login, auth
Diffstat (limited to 'node_modules/stream-http')
32 files changed, 2133 insertions, 0 deletions
diff --git a/node_modules/stream-http/.airtap.yml b/node_modules/stream-http/.airtap.yml new file mode 100644 index 0000000..64a114c --- /dev/null +++ b/node_modules/stream-http/.airtap.yml @@ -0,0 +1,23 @@ +sauce_connect: true +browsers: + - name: chrome + version: 39..latest + - name: firefox + version: 34..latest + - name: safari + version: 8..latest + - name: MicrosoftEdge + version: 13..latest + - name: ie + version: 9..latest + - name: iphone + version: '9.3..latest' + - name: android + version: '4.4..6.0' # TODO: change this back to latest once https://github.com/airtap/browsers/issues/3 is fixed +server: ./test/server/index.js +scripts: + - "/ie8-polyfill.js" + - "/test-polyfill.js" +browserify: + - options: + dedupe: false
\ No newline at end of file diff --git a/node_modules/stream-http/.travis.yml b/node_modules/stream-http/.travis.yml new file mode 100644 index 0000000..6ebc1ac --- /dev/null +++ b/node_modules/stream-http/.travis.yml @@ -0,0 +1,7 @@ +language: node_js +node_js: + - "node" +addons: + sauce_connect: true + hosts: + - airtap.local
\ No newline at end of file diff --git a/node_modules/stream-http/LICENSE b/node_modules/stream-http/LICENSE new file mode 100644 index 0000000..7267465 --- /dev/null +++ b/node_modules/stream-http/LICENSE @@ -0,0 +1,24 @@ +The MIT License + +Copyright (c) 2015 John Hiesey + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file diff --git a/node_modules/stream-http/README.md b/node_modules/stream-http/README.md new file mode 100644 index 0000000..94b6a50 --- /dev/null +++ b/node_modules/stream-http/README.md @@ -0,0 +1,144 @@ +# stream-http [](https://travis-ci.org/jhiesey/stream-http) + +[](https://saucelabs.com/u/stream-http) + +This module is an implementation of Node's native `http` module for the browser. +It tries to match Node's API and behavior as closely as possible, but some features +aren't available, since browsers don't give nearly as much control over requests. + +This is heavily inspired by, and intended to replace, [http-browserify](https://github.com/substack/http-browserify). + +## What does it do? + +In accordance with its name, `stream-http` tries to provide data to its caller before +the request has completed whenever possible. + +Backpressure, allowing the browser to only pull data from the server as fast as it is +consumed, is supported in: +* Chrome >= 58 (using `fetch` and `WritableStream`) + +The following browsers support true streaming, where only a small amount of the request +has to be held in memory at once: +* Chrome >= 43 (using the `fetch` API) +* Firefox >= 9 (using `moz-chunked-arraybuffer` responseType with xhr) + +The following browsers support pseudo-streaming, where the data is available before the +request finishes, but the entire response must be held in memory: +* Chrome +* Safari >= 5, and maybe older +* IE >= 10 +* Most other Webkit-based browsers, including the default Android browser + +All browsers newer than IE8 support binary responses. All of the above browsers that +support true streaming or pseudo-streaming support that for binary data as well +except for IE10. Old (presto-based) Opera also does not support binary streaming either. + +### IE8 note: +As of version 2.0.0, IE8 support requires the user to supply polyfills for +`Object.keys`, `Array.prototype.forEach`, and `Array.prototype.indexOf`. Example +implementations are provided in [ie8-polyfill.js](ie8-polyfill.js); alternately, +you may want to consider using [es5-shim](https://github.com/es-shims/es5-shim). +All browsers with full ES5 support shouldn't require any polyfills. + +## How do you use it? + +The intent is to have the same API as the client part of the +[Node HTTP module](https://nodejs.org/api/http.html). The interfaces are the same wherever +practical, although limitations in browsers make an exact clone of the Node API impossible. + +This module implements `http.request`, `http.get`, and most of `http.ClientRequest` +and `http.IncomingMessage` in addition to `http.METHODS` and `http.STATUS_CODES`. See the +Node docs for how these work. + +### Extra features compared to Node + +* The `message.url` property provides access to the final URL after all redirects. This +is useful since the browser follows all redirects silently, unlike Node. It is available +in Chrome 37 and newer, Firefox 32 and newer, and Safari 9 and newer. + +* The `options.withCredentials` boolean flag, used to indicate if the browser should send +cookies or authentication information with a CORS request. Default false. + +This module has to make some tradeoffs to support binary data and/or streaming. Generally, +the module can make a fairly good decision about which underlying browser features to use, +but sometimes it helps to get a little input from the developer. + +* The `options.mode` field passed into `http.request` or `http.get` can take on one of the +following values: + * 'default' (or any falsy value, including `undefined`): Try to provide partial data before +the request completes, but not at the cost of correctness for binary data or correctness of +the 'content-type' response header. This mode will also avoid slower code paths whenever +possible, which is particularly useful when making large requests in a browser like Safari +that has a weaker JavaScript engine. + * 'allow-wrong-content-type': Provides partial data in more cases than 'default', but +at the expense of causing the 'content-type' response header to be incorrectly reported +(as 'text/plain; charset=x-user-defined') in some browsers, notably Safari and Chrome 42 +and older. Preserves binary data whenever possible. In some cases the implementation may +also be a bit slow. This was the default in versions of this module before 1.5. + * 'prefer-stream': Provide data before the request completes even if binary data (anything +that isn't a single-byte ASCII or UTF8 character) will be corrupted. Of course, this option +is only safe for text data. May also cause the 'content-type' response header to be +incorrectly reported (as 'text/plain; charset=x-user-defined'). + * 'disable-fetch': Force the use of plain XHR regardless of the browser declaring a fetch +capability. Preserves the correctness of binary data and the 'content-type' response header. + * 'prefer-fast': Deprecated; now a synonym for 'default', which has the same performance +characteristics as this mode did in versions before 1.5. + +* `options.requestTimeout` allows setting a timeout in millisecionds for XHR and fetch (if +supported by the browser). This is a limit on how long the entire process takes from +beginning to end. Note that this is not the same as the node `setTimeout` functions, +which apply to pauses in data transfer over the underlying socket, or the node `timeout` +option, which applies to opening the connection. + +### Features missing compared to Node + +* `http.Agent` is only a stub +* The 'socket', 'connect', 'upgrade', and 'continue' events on `http.ClientRequest`. +* Any operations, including `request.setTimeout`, that operate directly on the underlying +socket. +* Any options that are disallowed for security reasons. This includes setting or getting +certain headers. +* `message.httpVersion` +* `message.rawHeaders` is modified by the browser, and may not quite match what is sent by +the server. +* `message.trailers` and `message.rawTrailers` will remain empty. +* Redirects are followed silently by the browser, so it isn't possible to access the 301/302 +redirect pages. +* The `timeout` event/option and `setTimeout` functions, which operate on the underlying +socket, are not available. However, see `options.requestTimeout` above. + +## Example + +``` js +http.get('/bundle.js', function (res) { + var div = document.getElementById('result'); + div.innerHTML += 'GET /beep<br>'; + + res.on('data', function (buf) { + div.innerHTML += buf; + }); + + res.on('end', function () { + div.innerHTML += '<br>__END__'; + }); +}) +``` + +## Running tests + +There are two sets of tests: the tests that run in Node (found in `test/node`) and the tests +that run in the browser (found in `test/browser`). Normally the browser tests run on +[Sauce Labs](http://saucelabs.com/). + +Running `npm test` will run both sets of tests, but in order for the Sauce Labs tests to run +you will need to sign up for an account (free for open source projects) and put the +credentials in a [`.zuulrc` file](https://github.com/defunctzombie/zuul/wiki/zuulrc). + +To run just the Node tests, run `npm run test-node`. + +To run the browser tests locally, run `npm run test-browser-local` and point your browser to +`http://localhost:8080/__zuul` + +## License + +MIT. Copyright (C) John Hiesey and other contributors. diff --git a/node_modules/stream-http/ie8-polyfill.js b/node_modules/stream-http/ie8-polyfill.js new file mode 100644 index 0000000..adea0fa --- /dev/null +++ b/node_modules/stream-http/ie8-polyfill.js @@ -0,0 +1,168 @@ +// These polyfills taken from MDN (developer.mozilla.org) + +// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys +if (!Object.keys) { + Object.keys = (function() { + 'use strict'; + var hasOwnProperty = Object.prototype.hasOwnProperty, + hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'), + dontEnums = [ + 'toString', + 'toLocaleString', + 'valueOf', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'constructor' + ], + dontEnumsLength = dontEnums.length; + + return function(obj) { + if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { + throw new TypeError('Object.keys called on non-object'); + } + + var result = [], prop, i; + + for (prop in obj) { + if (hasOwnProperty.call(obj, prop)) { + result.push(prop); + } + } + + if (hasDontEnumBug) { + for (i = 0; i < dontEnumsLength; i++) { + if (hasOwnProperty.call(obj, dontEnums[i])) { + result.push(dontEnums[i]); + } + } + } + return result; + }; + }()); +} + +// Production steps of ECMA-262, Edition 5, 15.4.4.18 +// Reference: http://es5.github.io/#x15.4.4.18 +if (!Array.prototype.forEach) { + + Array.prototype.forEach = function(callback, thisArg) { + + var T, k; + + if (this == null) { + throw new TypeError(' this is null or not defined'); + } + + // 1. Let O be the result of calling ToObject passing the |this| value as the argument. + var O = Object(this); + + // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length". + // 3. Let len be ToUint32(lenValue). + var len = O.length >>> 0; + + // 4. If IsCallable(callback) is false, throw a TypeError exception. + // See: http://es5.github.com/#x9.11 + if (typeof callback !== "function") { + throw new TypeError(callback + ' is not a function'); + } + + // 5. If thisArg was supplied, let T be thisArg; else let T be undefined. + if (arguments.length > 1) { + T = thisArg; + } + + // 6. Let k be 0 + k = 0; + + // 7. Repeat, while k < len + while (k < len) { + + var kValue; + + // a. Let Pk be ToString(k). + // This is implicit for LHS operands of the in operator + // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk. + // This step can be combined with c + // c. If kPresent is true, then + if (k in O) { + + // i. Let kValue be the result of calling the Get internal method of O with argument Pk. + kValue = O[k]; + + // ii. Call the Call internal method of callback with T as the this value and + // argument list containing kValue, k, and O. + callback.call(T, kValue, k, O); + } + // d. Increase k by 1. + k++; + } + // 8. return undefined + }; +} + +// Production steps of ECMA-262, Edition 5, 15.4.4.14 +// Reference: http://es5.github.io/#x15.4.4.14 +if (!Array.prototype.indexOf) { + Array.prototype.indexOf = function(searchElement, fromIndex) { + + var k; + + // 1. Let O be the result of calling ToObject passing + // the this value as the argument. + if (this == null) { + throw new TypeError('"this" is null or not defined'); + } + + var O = Object(this); + + // 2. Let lenValue be the result of calling the Get + // internal method of O with the argument "length". + // 3. Let len be ToUint32(lenValue). + var len = O.length >>> 0; + + // 4. If len is 0, return -1. + if (len === 0) { + return -1; + } + + // 5. If argument fromIndex was passed let n be + // ToInteger(fromIndex); else let n be 0. + var n = +fromIndex || 0; + + if (Math.abs(n) === Infinity) { + n = 0; + } + + // 6. If n >= len, return -1. + if (n >= len) { + return -1; + } + + // 7. If n >= 0, then Let k be n. + // 8. Else, n<0, Let k be len - abs(n). + // If k is less than 0, then let k be 0. + k = Math.max(n >= 0 ? n : len - Math.abs(n), 0); + + // 9. Repeat, while k < len + while (k < len) { + // a. Let Pk be ToString(k). + // This is implicit for LHS operands of the in operator + // b. Let kPresent be the result of calling the + // HasProperty internal method of O with argument Pk. + // This step can be combined with c + // c. If kPresent is true, then + // i. Let elementK be the result of calling the Get + // internal method of O with the argument ToString(k). + // ii. Let same be the result of applying the + // Strict Equality Comparison Algorithm to + // searchElement and elementK. + // iii. If same is true, return k. + if (k in O && O[k] === searchElement) { + return k; + } + k++; + } + return -1; + }; +}
\ No newline at end of file diff --git a/node_modules/stream-http/index.js b/node_modules/stream-http/index.js new file mode 100644 index 0000000..84bfe51 --- /dev/null +++ b/node_modules/stream-http/index.js @@ -0,0 +1,85 @@ +var ClientRequest = require('./lib/request') +var response = require('./lib/response') +var extend = require('xtend') +var statusCodes = require('builtin-status-codes') +var url = require('url') + +var http = exports + +http.request = function (opts, cb) { + if (typeof opts === 'string') + opts = url.parse(opts) + else + opts = extend(opts) + + // Normally, the page is loaded from http or https, so not specifying a protocol + // will result in a (valid) protocol-relative url. However, this won't work if + // the protocol is something else, like 'file:' + var defaultProtocol = global.location.protocol.search(/^https?:$/) === -1 ? 'http:' : '' + + var protocol = opts.protocol || defaultProtocol + var host = opts.hostname || opts.host + var port = opts.port + var path = opts.path || '/' + + // Necessary for IPv6 addresses + if (host && host.indexOf(':') !== -1) + host = '[' + host + ']' + + // This may be a relative url. The browser should always be able to interpret it correctly. + opts.url = (host ? (protocol + '//' + host) : '') + (port ? ':' + port : '') + path + opts.method = (opts.method || 'GET').toUpperCase() + opts.headers = opts.headers || {} + + // Also valid opts.auth, opts.mode + + var req = new ClientRequest(opts) + if (cb) + req.on('response', cb) + return req +} + +http.get = function get (opts, cb) { + var req = http.request(opts, cb) + req.end() + return req +} + +http.ClientRequest = ClientRequest +http.IncomingMessage = response.IncomingMessage + +http.Agent = function () {} +http.Agent.defaultMaxSockets = 4 + +http.globalAgent = new http.Agent() + +http.STATUS_CODES = statusCodes + +http.METHODS = [ + 'CHECKOUT', + 'CONNECT', + 'COPY', + 'DELETE', + 'GET', + 'HEAD', + 'LOCK', + 'M-SEARCH', + 'MERGE', + 'MKACTIVITY', + 'MKCOL', + 'MOVE', + 'NOTIFY', + 'OPTIONS', + 'PATCH', + 'POST', + 'PROPFIND', + 'PROPPATCH', + 'PURGE', + 'PUT', + 'REPORT', + 'SEARCH', + 'SUBSCRIBE', + 'TRACE', + 'UNLOCK', + 'UNSUBSCRIBE' +]
\ No newline at end of file diff --git a/node_modules/stream-http/lib/capability.js b/node_modules/stream-http/lib/capability.js new file mode 100644 index 0000000..3a17334 --- /dev/null +++ b/node_modules/stream-http/lib/capability.js @@ -0,0 +1,73 @@ +exports.fetch = isFunction(global.fetch) && isFunction(global.ReadableStream) + +exports.writableStream = isFunction(global.WritableStream) + +exports.abortController = isFunction(global.AbortController) + +exports.blobConstructor = false +try { + new Blob([new ArrayBuffer(1)]) + exports.blobConstructor = true +} catch (e) {} + +// The xhr request to example.com may violate some restrictive CSP configurations, +// so if we're running in a browser that supports `fetch`, avoid calling getXHR() +// and assume support for certain features below. +var xhr +function getXHR () { + // Cache the xhr value + if (xhr !== undefined) return xhr + + if (global.XMLHttpRequest) { + xhr = new global.XMLHttpRequest() + // If XDomainRequest is available (ie only, where xhr might not work + // cross domain), use the page location. Otherwise use example.com + // Note: this doesn't actually make an http request. + try { + xhr.open('GET', global.XDomainRequest ? '/' : 'https://example.com') + } catch(e) { + xhr = null + } + } else { + // Service workers don't have XHR + xhr = null + } + return xhr +} + +function checkTypeSupport (type) { + var xhr = getXHR() + if (!xhr) return false + try { + xhr.responseType = type + return xhr.responseType === type + } catch (e) {} + return false +} + +// For some strange reason, Safari 7.0 reports typeof global.ArrayBuffer === 'object'. +// Safari 7.1 appears to have fixed this bug. +var haveArrayBuffer = typeof global.ArrayBuffer !== 'undefined' +var haveSlice = haveArrayBuffer && isFunction(global.ArrayBuffer.prototype.slice) + +// If fetch is supported, then arraybuffer will be supported too. Skip calling +// checkTypeSupport(), since that calls getXHR(). +exports.arraybuffer = exports.fetch || (haveArrayBuffer && checkTypeSupport('arraybuffer')) + +// These next two tests unavoidably show warnings in Chrome. Since fetch will always +// be used if it's available, just return false for these to avoid the warnings. +exports.msstream = !exports.fetch && haveSlice && checkTypeSupport('ms-stream') +exports.mozchunkedarraybuffer = !exports.fetch && haveArrayBuffer && + checkTypeSupport('moz-chunked-arraybuffer') + +// If fetch is supported, then overrideMimeType will be supported too. Skip calling +// getXHR(). +exports.overrideMimeType = exports.fetch || (getXHR() ? isFunction(getXHR().overrideMimeType) : false) + +exports.vbArray = isFunction(global.VBArray) + +function isFunction (value) { + return typeof value === 'function' +} + +xhr = null // Help gc diff --git a/node_modules/stream-http/lib/request.js b/node_modules/stream-http/lib/request.js new file mode 100644 index 0000000..4f097df --- /dev/null +++ b/node_modules/stream-http/lib/request.js @@ -0,0 +1,327 @@ +var capability = require('./capability') +var inherits = require('inherits') +var response = require('./response') +var stream = require('readable-stream') +var toArrayBuffer = require('to-arraybuffer') + +var IncomingMessage = response.IncomingMessage +var rStates = response.readyStates + +function decideMode (preferBinary, useFetch) { + if (capability.fetch && useFetch) { + return 'fetch' + } else if (capability.mozchunkedarraybuffer) { + return 'moz-chunked-arraybuffer' + } else if (capability.msstream) { + return 'ms-stream' + } else if (capability.arraybuffer && preferBinary) { + return 'arraybuffer' + } else if (capability.vbArray && preferBinary) { + return 'text:vbarray' + } else { + return 'text' + } +} + +var ClientRequest = module.exports = function (opts) { + var self = this + stream.Writable.call(self) + + self._opts = opts + self._body = [] + self._headers = {} + if (opts.auth) + self.setHeader('Authorization', 'Basic ' + new Buffer(opts.auth).toString('base64')) + Object.keys(opts.headers).forEach(function (name) { + self.setHeader(name, opts.headers[name]) + }) + + var preferBinary + var useFetch = true + if (opts.mode === 'disable-fetch' || ('requestTimeout' in opts && !capability.abortController)) { + // If the use of XHR should be preferred. Not typically needed. + useFetch = false + preferBinary = true + } else if (opts.mode === 'prefer-streaming') { + // If streaming is a high priority but binary compatibility and + // the accuracy of the 'content-type' header aren't + preferBinary = false + } else if (opts.mode === 'allow-wrong-content-type') { + // If streaming is more important than preserving the 'content-type' header + preferBinary = !capability.overrideMimeType + } else if (!opts.mode || opts.mode === 'default' || opts.mode === 'prefer-fast') { + // Use binary if text streaming may corrupt data or the content-type header, or for speed + preferBinary = true + } else { + throw new Error('Invalid value for opts.mode') + } + self._mode = decideMode(preferBinary, useFetch) + self._fetchTimer = null + + self.on('finish', function () { + self._onFinish() + }) +} + +inherits(ClientRequest, stream.Writable) + +ClientRequest.prototype.setHeader = function (name, value) { + var self = this + var lowerName = name.toLowerCase() + // This check is not necessary, but it prevents warnings from browsers about setting unsafe + // headers. To be honest I'm not entirely sure hiding these warnings is a good thing, but + // http-browserify did it, so I will too. + if (unsafeHeaders.indexOf(lowerName) !== -1) + return + + self._headers[lowerName] = { + name: name, + value: value + } +} + +ClientRequest.prototype.getHeader = function (name) { + var header = this._headers[name.toLowerCase()] + if (header) + return header.value + return null +} + +ClientRequest.prototype.removeHeader = function (name) { + var self = this + delete self._headers[name.toLowerCase()] +} + +ClientRequest.prototype._onFinish = function () { + var self = this + + if (self._destroyed) + return + var opts = self._opts + + var headersObj = self._headers + var body = null + if (opts.method !== 'GET' && opts.method !== 'HEAD') { + if (capability.arraybuffer) { + body = toArrayBuffer(Buffer.concat(self._body)) + } else if (capability.blobConstructor) { + body = new global.Blob(self._body.map(function (buffer) { + return toArrayBuffer(buffer) + }), { + type: (headersObj['content-type'] || {}).value || '' + }) + } else { + // get utf8 string + body = Buffer.concat(self._body).toString() + } + } + + // create flattened list of headers + var headersList = [] + Object.keys(headersObj).forEach(function (keyName) { + var name = headersObj[keyName].name + var value = headersObj[keyName].value + if (Array.isArray(value)) { + value.forEach(function (v) { + headersList.push([name, v]) + }) + } else { + headersList.push([name, value]) + } + }) + + if (self._mode === 'fetch') { + var signal = null + var fetchTimer = null + if (capability.abortController) { + var controller = new AbortController() + signal = controller.signal + self._fetchAbortController = controller + + if ('requestTimeout' in opts && opts.requestTimeout !== 0) { + self._fetchTimer = global.setTimeout(function () { + self.emit('requestTimeout') + if (self._fetchAbortController) + self._fetchAbortController.abort() + }, opts.requestTimeout) + } + } + + global.fetch(self._opts.url, { + method: self._opts.method, + headers: headersList, + body: body || undefined, + mode: 'cors', + credentials: opts.withCredentials ? 'include' : 'same-origin', + signal: signal + }).then(function (response) { + self._fetchResponse = response + self._connect() + }, function (reason) { + global.clearTimeout(self._fetchTimer) + if (!self._destroyed) + self.emit('error', reason) + }) + } else { + var xhr = self._xhr = new global.XMLHttpRequest() + try { + xhr.open(self._opts.method, self._opts.url, true) + } catch (err) { + process.nextTick(function () { + self.emit('error', err) + }) + return + } + + // Can't set responseType on really old browsers + if ('responseType' in xhr) + xhr.responseType = self._mode.split(':')[0] + + if ('withCredentials' in xhr) + xhr.withCredentials = !!opts.withCredentials + + if (self._mode === 'text' && 'overrideMimeType' in xhr) + xhr.overrideMimeType('text/plain; charset=x-user-defined') + + if ('requestTimeout' in opts) { + xhr.timeout = opts.requestTimeout + xhr.ontimeout = function () { + self.emit('requestTimeout') + } + } + + headersList.forEach(function (header) { + xhr.setRequestHeader(header[0], header[1]) + }) + + self._response = null + xhr.onreadystatechange = function () { + switch (xhr.readyState) { + case rStates.LOADING: + case rStates.DONE: + self._onXHRProgress() + break + } + } + // Necessary for streaming in Firefox, since xhr.response is ONLY defined + // in onprogress, not in onreadystatechange with xhr.readyState = 3 + if (self._mode === 'moz-chunked-arraybuffer') { + xhr.onprogress = function () { + self._onXHRProgress() + } + } + + xhr.onerror = function () { + if (self._destroyed) + return + self.emit('error', new Error('XHR error')) + } + + try { + xhr.send(body) + } catch (err) { + process.nextTick(function () { + self.emit('error', err) + }) + return + } + } +} + +/** + * Checks if xhr.status is readable and non-zero, indicating no error. + * Even though the spec says it should be available in readyState 3, + * accessing it throws an exception in IE8 + */ +function statusValid (xhr) { + try { + var status = xhr.status + return (status !== null && status !== 0) + } catch (e) { + return false + } +} + +ClientRequest.prototype._onXHRProgress = function () { + var self = this + + if (!statusValid(self._xhr) || self._destroyed) + return + + if (!self._response) + self._connect() + + self._response._onXHRProgress() +} + +ClientRequest.prototype._connect = function () { + var self = this + + if (self._destroyed) + return + + self._response = new IncomingMessage(self._xhr, self._fetchResponse, self._mode, self._fetchTimer) + self._response.on('error', function(err) { + self.emit('error', err) + }) + + self.emit('response', self._response) +} + +ClientRequest.prototype._write = function (chunk, encoding, cb) { + var self = this + + self._body.push(chunk) + cb() +} + +ClientRequest.prototype.abort = ClientRequest.prototype.destroy = function () { + var self = this + self._destroyed = true + global.clearTimeout(self._fetchTimer) + if (self._response) + self._response._destroyed = true + if (self._xhr) + self._xhr.abort() + else if (self._fetchAbortController) + self._fetchAbortController.abort() +} + +ClientRequest.prototype.end = function (data, encoding, cb) { + var self = this + if (typeof data === 'function') { + cb = data + data = undefined + } + + stream.Writable.prototype.end.call(self, data, encoding, cb) +} + +ClientRequest.prototype.flushHeaders = function () {} +ClientRequest.prototype.setTimeout = function () {} +ClientRequest.prototype.setNoDelay = function () {} +ClientRequest.prototype.setSocketKeepAlive = function () {} + +// Taken from http://www.w3.org/TR/XMLHttpRequest/#the-setrequestheader%28%29-method +var unsafeHeaders = [ + 'accept-charset', + 'accept-encoding', + 'access-control-request-headers', + 'access-control-request-method', + 'connection', + 'content-length', + 'cookie', + 'cookie2', + 'date', + 'dnt', + 'expect', + 'host', + 'keep-alive', + 'origin', + 'referer', + 'te', + 'trailer', + 'transfer-encoding', + 'upgrade', + 'via' +] diff --git a/node_modules/stream-http/lib/response.js b/node_modules/stream-http/lib/response.js new file mode 100644 index 0000000..17d1fb7 --- /dev/null +++ b/node_modules/stream-http/lib/response.js @@ -0,0 +1,224 @@ +var capability = require('./capability') +var inherits = require('inherits') +var stream = require('readable-stream') + +var rStates = exports.readyStates = { + UNSENT: 0, + OPENED: 1, + HEADERS_RECEIVED: 2, + LOADING: 3, + DONE: 4 +} + +var IncomingMessage = exports.IncomingMessage = function (xhr, response, mode, fetchTimer) { + var self = this + stream.Readable.call(self) + + self._mode = mode + self.headers = {} + self.rawHeaders = [] + self.trailers = {} + self.rawTrailers = [] + + // Fake the 'close' event, but only once 'end' fires + self.on('end', function () { + // The nextTick is necessary to prevent the 'request' module from causing an infinite loop + process.nextTick(function () { + self.emit('close') + }) + }) + + if (mode === 'fetch') { + self._fetchResponse = response + + self.url = response.url + self.statusCode = response.status + self.statusMessage = response.statusText + + response.headers.forEach(function (header, key){ + self.headers[key.toLowerCase()] = header + self.rawHeaders.push(key, header) + }) + + if (capability.writableStream) { + var writable = new WritableStream({ + write: function (chunk) { + return new Promise(function (resolve, reject) { + if (self._destroyed) { + reject() + } else if(self.push(new Buffer(chunk))) { + resolve() + } else { + self._resumeFetch = resolve + } + }) + }, + close: function () { + global.clearTimeout(fetchTimer) + if (!self._destroyed) + self.push(null) + }, + abort: function (err) { + if (!self._destroyed) + self.emit('error', err) + } + }) + + try { + response.body.pipeTo(writable).catch(function (err) { + global.clearTimeout(fetchTimer) + if (!self._destroyed) + self.emit('error', err) + }) + return + } catch (e) {} // pipeTo method isn't defined. Can't find a better way to feature test this + } + // fallback for when writableStream or pipeTo aren't available + var reader = response.body.getReader() + function read () { + reader.read().then(function (result) { + if (self._destroyed) + return + if (result.done) { + global.clearTimeout(fetchTimer) + self.push(null) + return + } + self.push(new Buffer(result.value)) + read() + }).catch(function (err) { + global.clearTimeout(fetchTimer) + if (!self._destroyed) + self.emit('error', err) + }) + } + read() + } else { + self._xhr = xhr + self._pos = 0 + + self.url = xhr.responseURL + self.statusCode = xhr.status + self.statusMessage = xhr.statusText + var headers = xhr.getAllResponseHeaders().split(/\r?\n/) + headers.forEach(function (header) { + var matches = header.match(/^([^:]+):\s*(.*)/) + if (matches) { + var key = matches[1].toLowerCase() + if (key === 'set-cookie') { + if (self.headers[key] === undefined) { + self.headers[key] = [] + } + self.headers[key].push(matches[2]) + } else if (self.headers[key] !== undefined) { + self.headers[key] += ', ' + matches[2] + } else { + self.headers[key] = matches[2] + } + self.rawHeaders.push(matches[1], matches[2]) + } + }) + + self._charset = 'x-user-defined' + if (!capability.overrideMimeType) { + var mimeType = self.rawHeaders['mime-type'] + if (mimeType) { + var charsetMatch = mimeType.match(/;\s*charset=([^;])(;|$)/) + if (charsetMatch) { + self._charset = charsetMatch[1].toLowerCase() + } + } + if (!self._charset) + self._charset = 'utf-8' // best guess + } + } +} + +inherits(IncomingMessage, stream.Readable) + +IncomingMessage.prototype._read = function () { + var self = this + + var resolve = self._resumeFetch + if (resolve) { + self._resumeFetch = null + resolve() + } +} + +IncomingMessage.prototype._onXHRProgress = function () { + var self = this + + var xhr = self._xhr + + var response = null + switch (self._mode) { + case 'text:vbarray': // For IE9 + if (xhr.readyState !== rStates.DONE) + break + try { + // This fails in IE8 + response = new global.VBArray(xhr.responseBody).toArray() + } catch (e) {} + if (response !== null) { + self.push(new Buffer(response)) + break + } + // Falls through in IE8 + case 'text': + try { // This will fail when readyState = 3 in IE9. Switch mode and wait for readyState = 4 + response = xhr.responseText + } catch (e) { + self._mode = 'text:vbarray' + break + } + if (response.length > self._pos) { + var newData = response.substr(self._pos) + if (self._charset === 'x-user-defined') { + var buffer = new Buffer(newData.length) + for (var i = 0; i < newData.length; i++) + buffer[i] = newData.charCodeAt(i) & 0xff + + self.push(buffer) + } else { + self.push(newData, self._charset) + } + self._pos = response.length + } + break + case 'arraybuffer': + if (xhr.readyState !== rStates.DONE || !xhr.response) + break + response = xhr.response + self.push(new Buffer(new Uint8Array(response))) + break + case 'moz-chunked-arraybuffer': // take whole + response = xhr.response + if (xhr.readyState !== rStates.LOADING || !response) + break + self.push(new Buffer(new Uint8Array(response))) + break + case 'ms-stream': + response = xhr.response + if (xhr.readyState !== rStates.LOADING) + break + var reader = new global.MSStreamReader() + reader.onprogress = function () { + if (reader.result.byteLength > self._pos) { + self.push(new Buffer(new Uint8Array(reader.result.slice(self._pos)))) + self._pos = reader.result.byteLength + } + } + reader.onload = function () { + self.push(null) + } + // reader.onerror = ??? // TODO: this + reader.readAsArrayBuffer(response) + break + } + + // The ms-stream case handles end separately in reader.onload() + if (self._xhr.readyState === rStates.DONE && self._mode !== 'ms-stream') { + self.push(null) + } +} diff --git a/node_modules/stream-http/package.json b/node_modules/stream-http/package.json new file mode 100644 index 0000000..ea4cf00 --- /dev/null +++ b/node_modules/stream-http/package.json @@ -0,0 +1,73 @@ +{ + "_from": "stream-http@^2.7.2", + "_id": "stream-http@2.8.3", + "_inBundle": false, + "_integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "_location": "/stream-http", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "stream-http@^2.7.2", + "name": "stream-http", + "escapedName": "stream-http", + "rawSpec": "^2.7.2", + "saveSpec": null, + "fetchSpec": "^2.7.2" + }, + "_requiredBy": [ + "/node-libs-browser" + ], + "_resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "_shasum": "b2d242469288a5a27ec4fe8933acf623de6514fc", + "_spec": "stream-http@^2.7.2", + "_where": "/home/pruss/Dev/3-minute-website/node_modules/node-libs-browser", + "author": { + "name": "John Hiesey" + }, + "bugs": { + "url": "https://github.com/jhiesey/stream-http/issues" + }, + "bundleDependencies": false, + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + }, + "deprecated": false, + "description": "Streaming http in the browser", + "devDependencies": { + "airtap": "^0.0.5", + "basic-auth": "^2.0.0", + "brfs": "^1.6.1", + "cookie-parser": "^1.4.3", + "express": "^4.16.3", + "tape": "^4.9.0", + "ua-parser-js": "^0.7.18", + "webworkify": "^1.5.0" + }, + "homepage": "https://github.com/jhiesey/stream-http#readme", + "keywords": [ + "http", + "stream", + "streaming", + "xhr", + "http-browserify" + ], + "license": "MIT", + "main": "index.js", + "name": "stream-http", + "repository": { + "type": "git", + "url": "git://github.com/jhiesey/stream-http.git" + }, + "scripts": { + "test": "npm run test-node && ([ -n \"${TRAVIS_PULL_REQUEST}\" -a \"${TRAVIS_PULL_REQUEST}\" != 'false' ] || npm run test-browser)", + "test-browser": "airtap --loopback airtap.local -- test/browser/*.js", + "test-browser-local": "airtap --no-instrument --local 8080 -- test/browser/*.js", + "test-node": "tape test/node/*.js" + }, + "version": "2.8.3" +} diff --git a/node_modules/stream-http/test/browser/abort.js b/node_modules/stream-http/test/browser/abort.js new file mode 100644 index 0000000..2cf43e9 --- /dev/null +++ b/node_modules/stream-http/test/browser/abort.js @@ -0,0 +1,55 @@ +var Buffer = require('buffer').Buffer +var fs = require('fs') +var test = require('tape') + +var http = require('../..') + +test('abort before response', function (t) { + var req = http.get('/basic.txt', function (res) { + t.fail('unexpected response') + }) + req.abort() + t.end() +}) + +test('abort on response', function (t) { + var req = http.get('/basic.txt', function (res) { + req.abort() + t.end() + + res.on('end', function () { + t.fail('unexpected end') + }) + + res.on('data', function (data) { + t.fail('unexpected data') + }) + }) +}) + +test('abort on data', function (t) { + var req = http.get('/browserify.png?copies=5', function (res) { + var firstData = true + var failOnData = false + + res.on('end', function () { + t.fail('unexpected end') + }) + + res.on('data', function (data) { + if (failOnData) + t.fail('unexpected data') + else if (firstData) { + firstData = false + req.abort() + t.end() + process.nextTick(function () { + // Wait for any data that may have been queued + // in the stream before considering data events + // as errors + failOnData = true + }) + } + }) + }) +})
\ No newline at end of file diff --git a/node_modules/stream-http/test/browser/auth.js b/node_modules/stream-http/test/browser/auth.js new file mode 100644 index 0000000..5c73115 --- /dev/null +++ b/node_modules/stream-http/test/browser/auth.js @@ -0,0 +1,22 @@ +var Buffer = require('buffer').Buffer +var test = require('tape') + +var http = require('../..') + +test('authentication', function (t) { + http.get({ + path: '/auth', + auth: 'TestUser:trustno1' + }, function (res) { + var buffers = [] + + res.on('end', function () { + t.ok(new Buffer('You\'re in!').equals(Buffer.concat(buffers)), 'authentication succeeded') + t.end() + }) + + res.on('data', function (data) { + buffers.push(data) + }) + }) +})
\ No newline at end of file diff --git a/node_modules/stream-http/test/browser/binary-streaming.js b/node_modules/stream-http/test/browser/binary-streaming.js new file mode 100644 index 0000000..d1221df --- /dev/null +++ b/node_modules/stream-http/test/browser/binary-streaming.js @@ -0,0 +1,71 @@ +var Buffer = require('buffer').Buffer +var fs = require('fs') +var test = require('tape') +var UAParser = require('ua-parser-js') + +var http = require('../..') + +var browser = (new UAParser()).setUA(navigator.userAgent).getBrowser() +var browserName = browser.name +var browserVersion = browser.major +// Binary streaming doesn't work in IE10 or below +var skipStreamingCheck = (browserName === 'IE' && browserVersion <= 10) + +// Binary data gets corrupted in IE8 or below +var skipVerification = (browserName === 'IE' && browserVersion <= 8) + +// IE8 tends to throw up modal dialogs complaining about scripts running too long +// Since streaming doesn't actually work there anyway, just use one copy +var COPIES = skipVerification ? 1 : 20 +var MIN_PIECES = 2 + +var referenceOnce = fs.readFileSync(__dirname + '/../server/static/browserify.png') +var reference = new Buffer(referenceOnce.length * COPIES) +for(var i = 0; i < COPIES; i++) { + referenceOnce.copy(reference, referenceOnce.length * i) +} + +test('binary streaming', function (t) { + http.get({ + path: '/browserify.png?copies=' + COPIES, + mode: 'allow-wrong-content-type' + }, function (res) { + var buffers = [] + res.on('end', function () { + if (skipVerification) + t.skip('binary data not preserved on IE <= 8') + else + t.ok(reference.equals(Buffer.concat(buffers)), 'contents match') + + if (skipStreamingCheck) + t.skip('streaming not available on IE <= 8') + else + t.ok(buffers.length >= MIN_PIECES, 'received in multiple parts') + t.end() + }) + + res.on('data', function (data) { + buffers.push(data) + }) + }) +}) + +test('large binary request without streaming', function (t) { + http.get({ + path: '/browserify.png?copies=' + COPIES, + mode: 'default', + }, function (res) { + var buffers = [] + res.on('end', function () { + if (skipVerification) + t.skip('binary data not preserved on IE <= 8') + else + t.ok(reference.equals(Buffer.concat(buffers)), 'contents match') + t.end() + }) + + res.on('data', function (data) { + buffers.push(data) + }) + }) +})
\ No newline at end of file diff --git a/node_modules/stream-http/test/browser/binary.js b/node_modules/stream-http/test/browser/binary.js new file mode 100644 index 0000000..e116b8d --- /dev/null +++ b/node_modules/stream-http/test/browser/binary.js @@ -0,0 +1,32 @@ +var Buffer = require('buffer').Buffer +var fs = require('fs') +var test = require('tape') +var UAParser = require('ua-parser-js') + +var http = require('../..') + +var browser = (new UAParser()).setUA(navigator.userAgent).getBrowser() +var browserName = browser.name +var browserVersion = browser.major +// Binary data gets corrupted in IE8 or below +var skipVerification = (browserName === 'IE' && browserVersion <= 8) + +var reference = fs.readFileSync(__dirname + '/../server/static/browserify.png') + +test('binary download', function (t) { + http.get('/browserify.png', function (res) { + var buffers = [] + + res.on('end', function () { + if (skipVerification) + t.skip('binary data not preserved on IE <= 8') + else + t.ok(reference.equals(Buffer.concat(buffers)), 'contents match') + t.end() + }) + + res.on('data', function (data) { + buffers.push(data) + }) + }) +})
\ No newline at end of file diff --git a/node_modules/stream-http/test/browser/body-empty.js b/node_modules/stream-http/test/browser/body-empty.js new file mode 100644 index 0000000..8e99bb7 --- /dev/null +++ b/node_modules/stream-http/test/browser/body-empty.js @@ -0,0 +1,29 @@ +var Buffer = require('buffer').Buffer +var fs = require('fs') +var test = require('tape') + +var http = require('../..') + +var reference = fs.readFileSync(__dirname + '/../server/static/basic.txt') + +test('get body empty', function (t) { + var req = http.request({ + path: '/verifyEmpty', + method: 'GET' + }, function (res) { + var buffers = [] + + res.on('end', function () { + console.log(Buffer.concat(buffers).toString('utf8')) + t.ok(Buffer.from('empty').equals(Buffer.concat(buffers)), 'response body indicates request body was empty') + t.end() + }) + + res.on('data', function (data) { + buffers.push(data) + }) + }) + + req.write(reference) + req.end() +}) diff --git a/node_modules/stream-http/test/browser/cookie.js b/node_modules/stream-http/test/browser/cookie.js new file mode 100644 index 0000000..114c687 --- /dev/null +++ b/node_modules/stream-http/test/browser/cookie.js @@ -0,0 +1,25 @@ +var Buffer = require('buffer').Buffer +var test = require('tape') + +var http = require('../..') + +test('cookie', function (t) { + var cookie = 'hello=world' + window.document.cookie = cookie + + http.get({ + path: '/cookie', + withCredentials: false + }, function (res) { + var buffers = [] + + res.on('end', function () { + t.ok(new Buffer(cookie).equals(Buffer.concat(buffers)), 'hello cookie echoed') + t.end() + }) + + res.on('data', function (data) { + buffers.push(data) + }) + }) +}) diff --git a/node_modules/stream-http/test/browser/disable-fetch.js b/node_modules/stream-http/test/browser/disable-fetch.js new file mode 100644 index 0000000..b50aa71 --- /dev/null +++ b/node_modules/stream-http/test/browser/disable-fetch.js @@ -0,0 +1,37 @@ +var Buffer = require('buffer').Buffer +var test = require('tape') + +var http = require('../..') + +test('disable fetch', function (t) { + var originalFetch + if (typeof fetch === 'function') { + originalFetch = fetch + } + + var fetchCalled = false + fetch = function (input, options) { + fetchCalled = true + if (originalFetch) { + return originalFetch(input, options) + } + } + + http.get({ + path: '/browserify.png', + mode: 'disable-fetch' + }, function (res) { + t.ok(!fetchCalled, 'fetch was not called') + + if (originalFetch) { + fetch = originalFetch + } + + res.on('end', function () { + t.ok(res.headers['content-type'] === 'image/png', 'content-type was set correctly') + t.end() + }) + + res.on('data', function () {}) + }) +}) diff --git a/node_modules/stream-http/test/browser/error.js.disabled b/node_modules/stream-http/test/browser/error.js.disabled new file mode 100644 index 0000000..4a328d6 --- /dev/null +++ b/node_modules/stream-http/test/browser/error.js.disabled @@ -0,0 +1,12 @@ +var Buffer = require('buffer').Buffer +var test = require('tape') + +var http = require('../..') + +test('error handling', function (t) { + var req = http.get('https://0.0.0.0:0/fail.txt') + req.on('error', function (err) { + t.ok(err && ('message' in err), 'got error') + t.end() + }) +})
\ No newline at end of file diff --git a/node_modules/stream-http/test/browser/headers.js b/node_modules/stream-http/test/browser/headers.js new file mode 100644 index 0000000..9d0c77c --- /dev/null +++ b/node_modules/stream-http/test/browser/headers.js @@ -0,0 +1,116 @@ +var Buffer = require('buffer').Buffer +var fs = require('fs') +var test = require('tape') +var UAParser = require('ua-parser-js') + +var http = require('../..') + +test('headers', function (t) { + http.get({ + path: '/testHeaders?Response-Header=bar&Response-Header-2=BAR2', + headers: { + 'Test-Request-Header': 'foo', + 'Test-Request-Header-2': 'FOO2' + } + }, function (res) { + var rawHeaders = [] + for (var i = 0; i < res.rawHeaders.length; i += 2) { + var lowerKey = res.rawHeaders[i].toLowerCase() + if (lowerKey.indexOf('test-') === 0) + rawHeaders.push(lowerKey, res.rawHeaders[i + 1]) + } + var header1Pos = rawHeaders.indexOf('test-response-header') + t.ok(header1Pos >= 0, 'raw response header 1 present') + t.equal(rawHeaders[header1Pos + 1], 'bar', 'raw response header value 1') + var header2Pos = rawHeaders.indexOf('test-response-header-2') + t.ok(header2Pos >= 0, 'raw response header 2 present') + t.equal(rawHeaders[header2Pos + 1], 'BAR2', 'raw response header value 2') + t.equal(rawHeaders.length, 4, 'correct number of raw headers') + + t.equal(res.headers['test-response-header'], 'bar', 'response header 1') + t.equal(res.headers['test-response-header-2'], 'BAR2', 'response header 2') + + var buffers = [] + + res.on('end', function () { + var body = JSON.parse(Buffer.concat(buffers).toString()) + t.equal(body['test-request-header'], 'foo', 'request header 1') + t.equal(body['test-request-header-2'], 'FOO2', 'request header 2') + t.equal(Object.keys(body).length, 2, 'correct number of request headers') + t.end() + }) + + res.on('data', function (data) { + buffers.push(data) + }) + }) +}) + +test('arrays of headers', function (t) { + http.get({ + path: '/testHeaders?Response-Header=bar&Response-Header=BAR2', + headers: { + 'Test-Request-Header': ['foo', 'FOO2'] + } + }, function (res) { + var rawHeaders = [] + for (var i = 0; i < res.rawHeaders.length; i += 2) { + var lowerKey = res.rawHeaders[i].toLowerCase() + if (lowerKey.indexOf('test-') === 0) + rawHeaders.push(lowerKey, res.rawHeaders[i + 1]) + } + t.equal(rawHeaders[0], 'test-response-header', 'raw response header present') + t.equal(rawHeaders[1], 'bar, BAR2', 'raw response header value') + t.equal(rawHeaders.length, 2, 'correct number of raw headers') + + t.equal(res.headers['test-response-header'], 'bar, BAR2', 'response header') + + var buffers = [] + + res.on('end', function () { + var body = JSON.parse(Buffer.concat(buffers).toString()) + t.equal(body['test-request-header'], 'foo,FOO2', 'request headers') + t.equal(Object.keys(body).length, 1, 'correct number of request headers') + t.end() + }) + + res.on('data', function (data) { + buffers.push(data) + }) + }) +}) + +test('content-type response header', function (t) { + http.get('/testHeaders', function (res) { + t.equal(res.headers['content-type'], 'application/json', 'content-type preserved') + t.end() + }) +}) + +var browser = (new UAParser()).setUA(navigator.userAgent).getBrowser() +var browserName = browser.name +var browserVersion = browser.major +var browserMinorVersion = browser.minor || 0 +// The content-type header is broken when 'prefer-streaming' or 'allow-wrong-content-type' +// is passed in browsers that rely on xhr.overrideMimeType(), namely older chrome, safari 6-10.0, and the stock Android browser +// Note that Safari 10.0 on iOS 10.3 doesn't need to override the mime type, so the content-type is preserved. +var wrongMimeType = ((browserName === 'Chrome' && browserVersion <= 42) || + ((browserName === 'Safari' || browserName === 'Mobile Safari') && browserVersion >= 6 && (browserVersion < 10 || (browserVersion == 10 && browserMinorVersion == 0))) + || (browserName === 'Android Browser')) + +test('content-type response header with forced streaming', function (t) { + http.get({ + path: '/testHeaders', + mode: 'prefer-streaming' + }, function (res) { + if (wrongMimeType) { + // allow both the 'wrong' and correct mime type, since sometimes it's impossible to tell which to expect + // from the browser version alone (e.g. Safari 10.0 on iOS 10.2 vs iOS 10.3) + var contentType = res.headers['content-type'] + var correct = (contentType === 'text/plain; charset=x-user-defined') || (contentType === 'application/json') + t.ok(correct, 'content-type either preserved or overridden') + } else + t.equal(res.headers['content-type'], 'application/json', 'content-type preserved') + t.end() + }) +})
\ No newline at end of file diff --git a/node_modules/stream-http/test/browser/lib/webworker-worker.js b/node_modules/stream-http/test/browser/lib/webworker-worker.js new file mode 100644 index 0000000..c3c8b91 --- /dev/null +++ b/node_modules/stream-http/test/browser/lib/webworker-worker.js @@ -0,0 +1,20 @@ +var Buffer = require('buffer').Buffer + +var http = require('../../..') + +module.exports = function (self) { + self.addEventListener('message', function (ev) { + var url = ev.data + http.get(url, function (res) { + var buffers = [] + + res.on('end', function () { + self.postMessage(Buffer.concat(buffers).buffer) + }) + + res.on('data', function (data) { + buffers.push(data) + }) + }) + }) +}
\ No newline at end of file diff --git a/node_modules/stream-http/test/browser/package.json b/node_modules/stream-http/test/browser/package.json new file mode 100644 index 0000000..f4f7b5e --- /dev/null +++ b/node_modules/stream-http/test/browser/package.json @@ -0,0 +1,5 @@ +{ + "browserify": { + "transform": [ "brfs" ] + } +} diff --git a/node_modules/stream-http/test/browser/post-binary.js b/node_modules/stream-http/test/browser/post-binary.js new file mode 100644 index 0000000..52f3d7e --- /dev/null +++ b/node_modules/stream-http/test/browser/post-binary.js @@ -0,0 +1,41 @@ +var Buffer = require('buffer').Buffer +var fs = require('fs') +var test = require('tape') +var UAParser = require('ua-parser-js') + +var http = require('../..') + +var browser = (new UAParser()).setUA(navigator.userAgent).getBrowser() +var browserName = browser.name +var browserVersion = browser.major +// Binary request bodies don't work in a bunch of browsers +var skipVerification = ((browserName === 'IE' && browserVersion <= 10) || + (browserName === 'Safari' && browserVersion <= 5) || + (browserName === 'WebKit' && browserVersion <= 534) || // Old mobile safari + (browserName === 'Android Browser' && browserVersion <= 4)) + +var reference = fs.readFileSync(__dirname + '/../server/static/browserify.png') + +test('post binary', function (t) { + var req = http.request({ + path: '/echo', + method: 'POST' + }, function (res) { + var buffers = [] + + res.on('end', function () { + if (skipVerification) + t.skip('binary data not preserved on this browser') + else + t.ok(reference.equals(Buffer.concat(buffers)), 'echoed contents match') + t.end() + }) + + res.on('data', function (data) { + buffers.push(data) + }) + }) + + req.write(reference) + req.end() +})
\ No newline at end of file diff --git a/node_modules/stream-http/test/browser/post-text.js b/node_modules/stream-http/test/browser/post-text.js new file mode 100644 index 0000000..1cea8d7 --- /dev/null +++ b/node_modules/stream-http/test/browser/post-text.js @@ -0,0 +1,48 @@ +var Buffer = require('buffer').Buffer +var fs = require('fs') +var test = require('tape') + +var http = require('../..') + +var reference = fs.readFileSync(__dirname + '/../server/static/basic.txt') + +test('post text', function (t) { + var req = http.request({ + path: '/echo', + method: 'POST' + }, function (res) { + var buffers = [] + + res.on('end', function () { + t.ok(reference.equals(Buffer.concat(buffers)), 'echoed contents match') + t.end() + }) + + res.on('data', function (data) { + buffers.push(data) + }) + }) + + req.write(reference) + req.end() +}) + +test('post text with data in end()', function (t) { + var req = http.request({ + path: '/echo', + method: 'POST' + }, function (res) { + var buffers = [] + + res.on('end', function () { + t.ok(reference.equals(Buffer.concat(buffers)), 'echoed contents match') + t.end() + }) + + res.on('data', function (data) { + buffers.push(data) + }) + }) + + req.end(reference) +})
\ No newline at end of file diff --git a/node_modules/stream-http/test/browser/text-streaming.js b/node_modules/stream-http/test/browser/text-streaming.js new file mode 100644 index 0000000..21693af --- /dev/null +++ b/node_modules/stream-http/test/browser/text-streaming.js @@ -0,0 +1,43 @@ +var Buffer = require('buffer').Buffer +var fs = require('fs') +var test = require('tape') +var UAParser = require('ua-parser-js') + +var http = require('../..') + +var browser = (new UAParser()).setUA(navigator.userAgent).getBrowser() +var browserName = browser.name +var browserVersion = browser.major +// Streaming doesn't work in IE9 or below +var skipStreamingCheck = (browserName === 'IE' && browserVersion <= 9) + +var COPIES = 1000 +var MIN_PIECES = 5 + +var referenceOnce = fs.readFileSync(__dirname + '/../server/static/basic.txt') +var reference = new Buffer(referenceOnce.length * COPIES) +for(var i = 0; i < COPIES; i++) { + referenceOnce.copy(reference, referenceOnce.length * i) +} + +test('text streaming', function (t) { + http.get({ + path: '/basic.txt?copies=' + COPIES, + mode: 'prefer-streaming' + }, function (res) { + var buffers = [] + + res.on('end', function () { + if (skipStreamingCheck) + t.skip('streaming not available on IE <= 8') + else + t.ok(buffers.length >= MIN_PIECES, 'received in multiple parts') + t.ok(reference.equals(Buffer.concat(buffers)), 'contents match') + t.end() + }) + + res.on('data', function (data) { + buffers.push(data) + }) + }) +})
\ No newline at end of file diff --git a/node_modules/stream-http/test/browser/text.js b/node_modules/stream-http/test/browser/text.js new file mode 100644 index 0000000..568380c --- /dev/null +++ b/node_modules/stream-http/test/browser/text.js @@ -0,0 +1,43 @@ +var Buffer = require('buffer').Buffer +var fs = require('fs') +var test = require('tape') +var UAParser = require('ua-parser-js') +var url = require('url') + +var http = require('../..') + +var browser = (new UAParser()).setUA(navigator.userAgent).getBrowser() +var browserName = browser.name +var browserVersion = browser.major +// Response urls don't work on many browsers +var skipResponseUrl = ((browserName === 'IE') || + (browserName === 'Edge') || + (browserName === 'Chrome' && browserVersion <= 36) || + (browserName === 'Firefox' && browserVersion <= 31) || + ((browserName === 'Safari' || browserName === 'Mobile Safari') && browserVersion <= 8) || + (browserName === 'WebKit') || // Old mobile safari + (browserName === 'Android Browser' && browserVersion <= 4)) + +var reference = fs.readFileSync(__dirname + '/../server/static/basic.txt') + +test('basic functionality', function (t) { + http.get('/basic.txt', function (res) { + if (!skipResponseUrl) { + var testUrl = url.resolve(global.location.href, '/basic.txt') + // Redirects aren't tested, but presumably only browser bugs + // would cause this to fail only after redirects. + t.equals(res.url, testUrl, 'response url correct') + } + + var buffers = [] + + res.on('end', function () { + t.ok(reference.equals(Buffer.concat(buffers)), 'contents match') + t.end() + }) + + res.on('data', function (data) { + buffers.push(data) + }) + }) +})
\ No newline at end of file diff --git a/node_modules/stream-http/test/browser/timeout.js b/node_modules/stream-http/test/browser/timeout.js new file mode 100644 index 0000000..5e94c48 --- /dev/null +++ b/node_modules/stream-http/test/browser/timeout.js @@ -0,0 +1,43 @@ +var Buffer = require('buffer').Buffer +var fs = require('fs') +var test = require('tape') + +var http = require('../..') + +test('timeout', function (t) { + var req = http.get({ + path: '/browserify.png?copies=5', + requestTimeout: 10 // ms + }, function (res) { + res.on('data', function (data) { + }) + res.on('end', function () { + t.fail('request completed (should have timed out)') + }) + }) + req.on('requestTimeout', function () { + t.pass('got timeout') + t.end() + }) +}) + +// TODO: reenable this if there's a way to make it simultaneously +// fast and reliable +test.skip('no timeout after success', function (t) { + var req = http.get({ + path: '/basic.txt', + requestTimeout: 50000 // ms + }, function (res) { + res.on('data', function (data) { + }) + res.on('end', function () { + t.pass('success') + global.setTimeout(function () { + t.end() + }, 50000) + }) + }) + req.on('requestTimeout', function () { + t.fail('unexpected timeout') + }) +})
\ No newline at end of file diff --git a/node_modules/stream-http/test/browser/webworker.js b/node_modules/stream-http/test/browser/webworker.js new file mode 100644 index 0000000..063706c --- /dev/null +++ b/node_modules/stream-http/test/browser/webworker.js @@ -0,0 +1,31 @@ +var fs = require('fs') +var test = require('tape') +var UAParser = require('ua-parser-js') +var url = require('url') +var work = require('webworkify') + +var browser = (new UAParser()).setUA(navigator.userAgent).getBrowser() +var browserName = browser.name +var browserVersion = browser.major +// Skip browsers with poor or nonexistant WebWorker support +var skip = ((browserName === 'IE' && browserVersion <= 10) || + (browserName === 'Safari' && browserVersion <= 5) || + (browserName === 'WebKit' && browserVersion <= 534) || // Old mobile safari + (browserName === 'Android Browser' && browserVersion <= 4)) + +var reference = fs.readFileSync(__dirname + '/../server/static/browserify.png') + +test('binary download in WebWorker', { + skip: skip +}, function (t) { + // We have to use a global url, since webworkify puts the worker in a Blob, + // which doesn't have a proper location + var testUrl = url.resolve(global.location.href, '/browserify.png') + var worker = work(require('./lib/webworker-worker.js')) + worker.addEventListener('message', function (ev) { + var data = new Buffer(new Uint8Array(ev.data)) + t.ok(reference.equals(data), 'contents match') + t.end() + }) + worker.postMessage(testUrl) +})
\ No newline at end of file diff --git a/node_modules/stream-http/test/node/http-browserify.js b/node_modules/stream-http/test/node/http-browserify.js new file mode 100644 index 0000000..42a718f --- /dev/null +++ b/node_modules/stream-http/test/node/http-browserify.js @@ -0,0 +1,147 @@ +// These tests are taken from http-browserify to ensure compatibility with +// that module +var test = require('tape') +var url = require('url') + +var location = 'http://localhost:8081/foo/123' + +var noop = function() {} +global.location = url.parse(location) +global.XMLHttpRequest = function() { + this.open = noop + this.send = noop + this.withCredentials = false +} + +var moduleName = require.resolve('../../') +delete require.cache[moduleName] +var http = require('../../') + +test('Make sure http object has correct properties', function (t) { + t.ok(http.Agent, 'Agent defined') + t.ok(http.ClientRequest, 'ClientRequest defined') + t.ok(http.ClientRequest.prototype, 'ClientRequest.prototype defined') + t.ok(http.IncomingMessage, 'IncomingMessage defined') + t.ok(http.IncomingMessage.prototype, 'IncomingMessage.prototype defined') + t.ok(http.METHODS, 'METHODS defined') + t.ok(http.STATUS_CODES, 'STATUS_CODES defined') + t.ok(http.get, 'get defined') + t.ok(http.globalAgent, 'globalAgent defined') + t.ok(http.request, 'request defined') + t.end() +}) + +test('Test simple url string', function(t) { + var testUrl = { path: '/api/foo' } + var request = http.get(testUrl, noop) + + var resolved = url.resolve(location, request._opts.url) + t.equal(resolved, 'http://localhost:8081/api/foo', 'Url should be correct') + t.end() + +}) + +test('Test full url object', function(t) { + var testUrl = { + host: "localhost:8081", + hostname: "localhost", + href: "http://localhost:8081/api/foo?bar=baz", + method: "GET", + path: "/api/foo?bar=baz", + pathname: "/api/foo", + port: "8081", + protocol: "http:", + query: "bar=baz", + search: "?bar=baz", + slashes: true + } + + var request = http.get(testUrl, noop) + + var resolved = url.resolve(location, request._opts.url) + t.equal(resolved, 'http://localhost:8081/api/foo?bar=baz', 'Url should be correct') + t.end() +}) + +test('Test alt protocol', function(t) { + var params = { + protocol: "foo:", + hostname: "localhost", + port: "3000", + path: "/bar" + } + + var request = http.get(params, noop) + + var resolved = url.resolve(location, request._opts.url) + t.equal(resolved, 'foo://localhost:3000/bar', 'Url should be correct') + t.end() +}) + +test('Test page with \'file:\' protocol', function (t) { + var params = { + hostname: 'localhost', + port: 3000, + path: '/bar' + } + + var fileLocation = 'file:///home/me/stuff/index.html' + + var normalLocation = global.location + global.location = url.parse(fileLocation) // Temporarily change the location + var request = http.get(params, noop) + global.location = normalLocation // Reset the location + + var resolved = url.resolve(fileLocation, request._opts.url) + t.equal(resolved, 'http://localhost:3000/bar', 'Url should be correct') + t.end() +}) + +test('Test string as parameters', function(t) { + var testUrl = '/api/foo' + var request = http.get(testUrl, noop) + + var resolved = url.resolve(location, request._opts.url) + t.equal(resolved, 'http://localhost:8081/api/foo', 'Url should be correct') + t.end() +}) + +test('Test withCredentials param', function(t) { + var url = '/api/foo' + + var request = http.get({ url: url, withCredentials: false }, noop) + t.equal(request._xhr.withCredentials, false, 'xhr.withCredentials should be false') + + var request = http.get({ url: url, withCredentials: true }, noop) + t.equal(request._xhr.withCredentials, true, 'xhr.withCredentials should be true') + + var request = http.get({ url: url }, noop) + t.equal(request._xhr.withCredentials, false, 'xhr.withCredentials should be false') + + t.end() +}) + +test('Test ipv6 address', function(t) { + var testUrl = 'http://[::1]:80/foo' + var request = http.get(testUrl, noop) + + var resolved = url.resolve(location, request._opts.url) + t.equal(resolved, 'http://[::1]:80/foo', 'Url should be correct') + t.end() +}) + +test('Test relative path in url', function(t) { + var params = { path: './bar' } + var request = http.get(params, noop) + + var resolved = url.resolve(location, request._opts.url) + t.equal(resolved, 'http://localhost:8081/foo/bar', 'Url should be correct') + t.end() +}) + +test('Cleanup', function (t) { + delete global.location + delete global.XMLHttpRequest + delete require.cache[moduleName] + t.end() +}) diff --git a/node_modules/stream-http/test/server/index.js b/node_modules/stream-http/test/server/index.js new file mode 100644 index 0000000..0a74aed --- /dev/null +++ b/node_modules/stream-http/test/server/index.js @@ -0,0 +1,137 @@ +var cookieParser = require('cookie-parser') +var basicAuth = require('basic-auth') +var express = require('express') +var fs = require('fs') +var http = require('http') +var path = require('path') +var url = require('url') + +var app = express() +var server = http.createServer(app) + +// Otherwise, use 'application/octet-stream' +var copiesMimeTypes = { + '/basic.txt': 'text/plain' +} + +var maxDelay = 5000 // ms + +// This should make sure bodies aren't cached +// so the streaming tests always pass +app.use(function (req, res, next) { + res.setHeader('Cache-Control', 'no-store') + next() +}) + +app.get('/testHeaders', function (req, res) { + var parsed = url.parse(req.url, true) + + // Values in query parameters are sent as response headers + Object.keys(parsed.query).forEach(function (key) { + res.setHeader('Test-' + key, parsed.query[key]) + }) + + res.setHeader('Content-Type', 'application/json') + res.setHeader('Cache-Control', 'no-cache') + + // Request headers are sent in the body as json + var reqHeaders = {} + Object.keys(req.headers).forEach(function (key) { + key = key.toLowerCase() + if (key.indexOf('test-') === 0) { + // different browsers format request headers with multiple values + // slightly differently, so normalize + reqHeaders[key] = req.headers[key].replace(', ', ',') + } + }) + + var body = JSON.stringify(reqHeaders) + res.setHeader('Content-Length', body.length) + res.write(body) + res.end() +}) + +app.get('/cookie', cookieParser(), function (req, res) { + res.setHeader('Content-Type', 'text/plain') + res.write('hello=' + req.cookies.hello) + res.end() +}) + +app.get('/auth', function (req, res) { + var user = basicAuth(req) + + if (!user || user.name !== 'TestUser' || user.pass !== 'trustno1') { + res.setHeader('WWW-Authenticate', 'Basic realm="example"') + res.end('Access denied') + } else { + res.setHeader('Content-Type', 'text/plain') + res.write('You\'re in!') + res.end() + } +}) + +app.post('/echo', function (req, res) { + res.setHeader('Content-Type', 'application/octet-stream') + req.pipe(res) +}) + +app.use('/verifyEmpty', function (req, res) { + var empty = true + req.on('data', function (buf) { + if (buf.length > 0) { + empty = false + } + }) + req.on('end', function () { + res.setHeader('Content-Type', 'text/plain') + + if (empty) { + res.end('empty') + } else { + res.end('not empty') + } + }) +}) + +app.use(function (req, res, next) { + var parsed = url.parse(req.url, true) + + if ('copies' in parsed.query) { + var totalCopies = parseInt(parsed.query.copies, 10) + function fail () { + res.statusCode = 500 + res.end() + } + fs.readFile(path.join(__dirname, 'static', parsed.pathname), function (err, data) { + if (err) + return fail() + + var mimeType = copiesMimeTypes[parsed.pathname] || 'application/octet-stream' + res.setHeader('Content-Type', mimeType) + res.setHeader('Content-Length', data.length * totalCopies) + var pieceDelay = maxDelay / totalCopies + if (pieceDelay > 100) + pieceDelay = 100 + + function write (copies) { + if (copies === 0) + return res.end() + + res.write(data, function (err) { + if (err) + return fail() + setTimeout(write.bind(null, copies - 1), pieceDelay) + }) + } + write(totalCopies) + }) + return + } + next() +}) + +app.use(express.static(path.join(__dirname, 'static'))) + +var port = parseInt(process.env.AIRTAP_PORT) || 8199 +console.log('Test server listening on port', port) +server.listen(port) diff --git a/node_modules/stream-http/test/server/static/basic.txt b/node_modules/stream-http/test/server/static/basic.txt new file mode 100644 index 0000000..aa7a0cc --- /dev/null +++ b/node_modules/stream-http/test/server/static/basic.txt @@ -0,0 +1,19 @@ +Mary had a little lamb, +His fleece was white as snow, +And everywhere that Mary went, +The lamb was sure to go. + +He followed her to school one day, +Which was against the rule, +It made the children laugh and play +To see a lamb at school. + +And so the teacher turned it out, +But still it lingered near, +And waited patiently about, +Till Mary did appear. + +"Why does the lamb love Mary so?" +The eager children cry. +"Why, Mary loves the lamb, you know." +The teacher did reply. diff --git a/node_modules/stream-http/test/server/static/browserify.png b/node_modules/stream-http/test/server/static/browserify.png Binary files differnew file mode 100644 index 0000000..98d6bf5 --- /dev/null +++ b/node_modules/stream-http/test/server/static/browserify.png diff --git a/node_modules/stream-http/test/server/static/test-polyfill.js b/node_modules/stream-http/test/server/static/test-polyfill.js new file mode 100644 index 0000000..f6a1a9d --- /dev/null +++ b/node_modules/stream-http/test/server/static/test-polyfill.js @@ -0,0 +1,9 @@ +if (!String.prototype.trim) { + (function() { + // Make sure we trim BOM and NBSP + var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g; + String.prototype.trim = function() { + return this.replace(rtrim, ''); + }; + })(); +} |