Compare commits

..

10 Commits

Author SHA1 Message Date
John Hiesey f2341d0598 All browsers again 2018-05-09 18:56:07 -07:00
John Hiesey c348b1494b All except firefox 2018-05-09 18:33:39 -07:00
John Hiesey f03225fa0c Only safari and firefox 2018-05-09 18:26:04 -07:00
John Hiesey a78b00ff6e All except safari and firefox 2018-05-09 18:12:51 -07:00
John Hiesey ebed5847c8 All except chrome 2018-05-09 17:59:27 -07:00
John Hiesey 2515f6420d Second half of browser list 2018-05-09 17:51:39 -07:00
John Hiesey 204b5abba7 Edge only 2018-05-09 17:48:53 -07:00
John Hiesey a72bc9b825 All except edge 2018-05-09 17:28:42 -07:00
John Hiesey a1a075d6ab Only android 2018-05-09 17:24:28 -07:00
John Hiesey 6c795acf4e Enable airtap debug 2018-05-09 16:45:24 -07:00
19 changed files with 7292 additions and 155 deletions
+10 -18
View File
@@ -1,31 +1,23 @@
providers:
- airtap-sauce
sauce_connect: true
browsers:
- name: chrome
version: -2..latest
version: 39..latest
- name: firefox
version: -2..latest
version: 34..latest
- name: safari
version: [8..10, 12..latest]
- name: safari
version: 11
platform: Mac 10.13 # Configure separately due to https://github.com/airtap/sauce-browsers/issues/3
version: 8..latest
- name: MicrosoftEdge
version: -2..latest
version: 13..latest
- name: ie
version: 11
version: 9..latest
- name: iphone
version: '10.3..latest'
version: '9.3..latest'
- name: android
version: '6.0..latest'
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
presets:
local:
providers: airtap-manual
browsers:
- name: manual
dedupe: false
+1 -2
View File
@@ -1,6 +1,5 @@
.airtaprc
.DS_Store
bundle.js
node_modules
npm-debug.log
package-lock.json
.airtaprc
-3
View File
@@ -1,3 +0,0 @@
.airtap.yml
.travis.yml
test/
+23 -13
View File
@@ -1,6 +1,6 @@
# stream-http [![Build Status](https://travis-ci.org/jhiesey/stream-http.svg?branch=master)](https://travis-ci.org/jhiesey/stream-http)
[![Sauce Test Status](https://app.saucelabs.com/browser-matrix/stream-http.svg)](https://app.saucelabs.com/u/stream-http)
[![Sauce Test Status](https://saucelabs.com/browser-matrix/stream-http.svg)](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
@@ -22,13 +22,23 @@ has to be held in memory at once:
* Chrome >= 43 (using the `fetch` API)
* Firefox >= 9 (using `moz-chunked-arraybuffer` responseType with xhr)
All other supported browsers support pseudo-streaming, where the data is available before
the request finishes, but the entire response must be held in memory. This works for both
text and binary data.
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
### IE note:
As of version 3.0.0, IE10 and below are no longer supported. IE11 support will remain for
now.
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?
@@ -65,7 +75,7 @@ at the expense of causing the 'content-type' response header to be incorrectly r
(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-streaming': Provide data before the request completes even if binary data (anything
* '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').
@@ -84,7 +94,7 @@ option, which applies to opening the connection.
* `http.Agent` is only a stub
* The 'socket', 'connect', 'upgrade', and 'continue' events on `http.ClientRequest`.
* Any operations, other than `request.setTimeout`, that operate directly on the underlying
* 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.
@@ -94,6 +104,8 @@ 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
@@ -120,14 +132,12 @@ that run in the browser (found in `test/browser`). Normally the browser tests ru
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 [`.airtaprc` file](https://github.com/airtap/airtap/blob/master/doc/airtaprc.md).
You will also need to run a [Sauce Connect Proxy](https://wiki.saucelabs.com/display/DOCS/Sauce+Connect+Proxy)
with the same credentials.
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
the link shown in your terminal.
`http://localhost:8080/__zuul`
## License
+168
View File
@@ -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;
};
}
+17 -3
View File
@@ -4,6 +4,12 @@ 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.
@@ -39,19 +45,27 @@ function checkTypeSupport (type) {
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 || checkTypeSupport('arraybuffer')
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 && checkTypeSupport('ms-stream')
exports.mozchunkedarraybuffer = !exports.fetch && checkTypeSupport('moz-chunked-arraybuffer')
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'
}
+26 -50
View File
@@ -2,6 +2,7 @@ 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
@@ -15,6 +16,8 @@ function decideMode (preferBinary, useFetch) {
return 'ms-stream'
} else if (capability.arraybuffer && preferBinary) {
return 'arraybuffer'
} else if (capability.vbArray && preferBinary) {
return 'text:vbarray'
} else {
return 'text'
}
@@ -28,7 +31,7 @@ var ClientRequest = module.exports = function (opts) {
self._body = []
self._headers = {}
if (opts.auth)
self.setHeader('Authorization', 'Basic ' + Buffer.from(opts.auth).toString('base64'))
self.setHeader('Authorization', 'Basic ' + new Buffer(opts.auth).toString('base64'))
Object.keys(opts.headers).forEach(function (name) {
self.setHeader(name, opts.headers[name])
})
@@ -54,8 +57,6 @@ var ClientRequest = module.exports = function (opts) {
}
self._mode = decideMode(preferBinary, useFetch)
self._fetchTimer = null
self._socketTimeout = null
self._socketTimer = null
self.on('finish', function () {
self._onFinish()
@@ -98,17 +99,22 @@ ClientRequest.prototype._onFinish = function () {
return
var opts = self._opts
if ('timeout' in opts && opts.timeout !== 0) {
self.setTimeout(opts.timeout)
}
var headersObj = self._headers
var body = null
if (opts.method !== 'GET' && opts.method !== 'HEAD') {
body = new Blob(self._body, {
type: (headersObj['content-type'] || {}).value || ''
});
}
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 = []
@@ -126,6 +132,7 @@ ClientRequest.prototype._onFinish = function () {
if (self._mode === 'fetch') {
var signal = null
var fetchTimer = null
if (capability.abortController) {
var controller = new AbortController()
signal = controller.signal
@@ -149,10 +156,9 @@ ClientRequest.prototype._onFinish = function () {
signal: signal
}).then(function (response) {
self._fetchResponse = response
self._resetTimers(false)
self._connect()
}, function (reason) {
self._resetTimers(true)
global.clearTimeout(self._fetchTimer)
if (!self._destroyed)
self.emit('error', reason)
})
@@ -169,7 +175,7 @@ ClientRequest.prototype._onFinish = function () {
// Can't set responseType on really old browsers
if ('responseType' in xhr)
xhr.responseType = self._mode
xhr.responseType = self._mode.split(':')[0]
if ('withCredentials' in xhr)
xhr.withCredentials = !!opts.withCredentials
@@ -208,7 +214,6 @@ ClientRequest.prototype._onFinish = function () {
xhr.onerror = function () {
if (self._destroyed)
return
self._resetTimers(true)
self.emit('error', new Error('XHR error'))
}
@@ -240,15 +245,13 @@ function statusValid (xhr) {
ClientRequest.prototype._onXHRProgress = function () {
var self = this
self._resetTimers(false)
if (!statusValid(self._xhr) || self._destroyed)
return
if (!self._response)
self._connect()
self._response._onXHRProgress(self._resetTimers.bind(self))
self._response._onXHRProgress()
}
ClientRequest.prototype._connect = function () {
@@ -257,7 +260,7 @@ ClientRequest.prototype._connect = function () {
if (self._destroyed)
return
self._response = new IncomingMessage(self._xhr, self._fetchResponse, self._mode, self._resetTimers.bind(self))
self._response = new IncomingMessage(self._xhr, self._fetchResponse, self._mode, self._fetchTimer)
self._response.on('error', function(err) {
self.emit('error', err)
})
@@ -272,35 +275,16 @@ ClientRequest.prototype._write = function (chunk, encoding, cb) {
cb()
}
ClientRequest.prototype._resetTimers = function (done) {
var self = this
global.clearTimeout(self._socketTimer)
self._socketTimer = null
if (done) {
global.clearTimeout(self._fetchTimer)
self._fetchTimer = null
} else if (self._socketTimeout) {
self._socketTimer = global.setTimeout(function () {
self.emit('timeout')
}, self._socketTimeout)
}
}
ClientRequest.prototype.abort = ClientRequest.prototype.destroy = function (err) {
ClientRequest.prototype.abort = ClientRequest.prototype.destroy = function () {
var self = this
self._destroyed = true
self._resetTimers(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()
if (err)
self.emit('error', err)
}
ClientRequest.prototype.end = function (data, encoding, cb) {
@@ -313,17 +297,8 @@ ClientRequest.prototype.end = function (data, encoding, cb) {
stream.Writable.prototype.end.call(self, data, encoding, cb)
}
ClientRequest.prototype.setTimeout = function (timeout, cb) {
var self = this
if (cb)
self.once('timeout', cb)
self._socketTimeout = timeout
self._resetTimers(false)
}
ClientRequest.prototype.flushHeaders = function () {}
ClientRequest.prototype.setTimeout = function () {}
ClientRequest.prototype.setNoDelay = function () {}
ClientRequest.prototype.setSocketKeepAlive = function () {}
@@ -348,5 +323,6 @@ var unsafeHeaders = [
'trailer',
'transfer-encoding',
'upgrade',
'user-agent',
'via'
]
+30 -17
View File
@@ -10,7 +10,7 @@ var rStates = exports.readyStates = {
DONE: 4
}
var IncomingMessage = exports.IncomingMessage = function (xhr, response, mode, resetTimers) {
var IncomingMessage = exports.IncomingMessage = function (xhr, response, mode, fetchTimer) {
var self = this
stream.Readable.call(self)
@@ -43,11 +43,10 @@ var IncomingMessage = exports.IncomingMessage = function (xhr, response, mode, r
if (capability.writableStream) {
var writable = new WritableStream({
write: function (chunk) {
resetTimers(false)
return new Promise(function (resolve, reject) {
if (self._destroyed) {
reject()
} else if(self.push(Buffer.from(chunk))) {
} else if(self.push(new Buffer(chunk))) {
resolve()
} else {
self._resumeFetch = resolve
@@ -55,12 +54,11 @@ var IncomingMessage = exports.IncomingMessage = function (xhr, response, mode, r
})
},
close: function () {
resetTimers(true)
global.clearTimeout(fetchTimer)
if (!self._destroyed)
self.push(null)
},
abort: function (err) {
resetTimers(true)
if (!self._destroyed)
self.emit('error', err)
}
@@ -68,7 +66,7 @@ var IncomingMessage = exports.IncomingMessage = function (xhr, response, mode, r
try {
response.body.pipeTo(writable).catch(function (err) {
resetTimers(true)
global.clearTimeout(fetchTimer)
if (!self._destroyed)
self.emit('error', err)
})
@@ -81,15 +79,15 @@ var IncomingMessage = exports.IncomingMessage = function (xhr, response, mode, r
reader.read().then(function (result) {
if (self._destroyed)
return
resetTimers(result.done)
if (result.done) {
global.clearTimeout(fetchTimer)
self.push(null)
return
}
self.push(Buffer.from(result.value))
self.push(new Buffer(result.value))
read()
}).catch(function (err) {
resetTimers(true)
global.clearTimeout(fetchTimer)
if (!self._destroyed)
self.emit('error', err)
})
@@ -148,19 +146,36 @@ IncomingMessage.prototype._read = function () {
}
}
IncomingMessage.prototype._onXHRProgress = function (resetTimers) {
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':
response = xhr.responseText
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 = Buffer.alloc(newData.length)
var buffer = new Buffer(newData.length)
for (var i = 0; i < newData.length; i++)
buffer[i] = newData.charCodeAt(i) & 0xff
@@ -175,13 +190,13 @@ IncomingMessage.prototype._onXHRProgress = function (resetTimers) {
if (xhr.readyState !== rStates.DONE || !xhr.response)
break
response = xhr.response
self.push(Buffer.from(new Uint8Array(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(Buffer.from(new Uint8Array(response)))
self.push(new Buffer(new Uint8Array(response)))
break
case 'ms-stream':
response = xhr.response
@@ -190,12 +205,11 @@ IncomingMessage.prototype._onXHRProgress = function (resetTimers) {
var reader = new global.MSStreamReader()
reader.onprogress = function () {
if (reader.result.byteLength > self._pos) {
self.push(Buffer.from(new Uint8Array(reader.result.slice(self._pos))))
self.push(new Buffer(new Uint8Array(reader.result.slice(self._pos))))
self._pos = reader.result.byteLength
}
}
reader.onload = function () {
resetTimers(true)
self.push(null)
}
// reader.onerror = ??? // TODO: this
@@ -205,7 +219,6 @@ IncomingMessage.prototype._onXHRProgress = function (resetTimers) {
// The ms-stream case handles end separately in reader.onload()
if (self._xhr.readyState === rStates.DONE && self._mode !== 'ms-stream') {
resetTimers(true)
self.push(null)
}
}
+6932
View File
File diff suppressed because it is too large Load Diff
+14 -15
View File
@@ -1,6 +1,6 @@
{
"name": "stream-http",
"version": "3.2.0",
"version": "2.8.1",
"description": "Streaming http in the browser",
"main": "index.js",
"repository": {
@@ -10,8 +10,8 @@
"scripts": {
"test": "npm run test-node && ([ -n \"${TRAVIS_PULL_REQUEST}\" -a \"${TRAVIS_PULL_REQUEST}\" != 'false' ] || npm run test-browser)",
"test-node": "tape test/node/*.js",
"test-browser": "airtap --concurrency 1 -- test/browser/*.js",
"test-browser-local": "airtap --preset local -- test/browser/*.js"
"test-browser": "DEBUG=airtap airtap --loopback airtap.local -- test/browser/*.js",
"test-browser-local": "airtap --no-instrument --local 8080 -- test/browser/*.js"
},
"author": "John Hiesey",
"license": "MIT",
@@ -28,20 +28,19 @@
],
"dependencies": {
"builtin-status-codes": "^3.0.0",
"inherits": "^2.0.4",
"readable-stream": "^3.6.0",
"xtend": "^4.0.2"
"inherits": "^2.0.1",
"readable-stream": "^2.3.6",
"to-arraybuffer": "^1.0.0",
"xtend": "^4.0.0"
},
"devDependencies": {
"airtap": "^4.0.3",
"airtap-manual": "^1.0.0",
"airtap-sauce": "^1.1.0",
"basic-auth": "^2.0.1",
"brfs": "^2.0.2",
"cookie-parser": "^1.4.5",
"express": "^4.17.1",
"tape": "^5.2.2",
"ua-parser-js": "^0.7.28",
"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"
}
}
+25 -4
View File
@@ -5,7 +5,18 @@ var UAParser = require('ua-parser-js')
var http = require('../..')
var COPIES = 20
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')
@@ -21,8 +32,15 @@ test('binary streaming', function (t) {
}, function (res) {
var buffers = []
res.on('end', function () {
t.ok(reference.equals(Buffer.concat(buffers)), 'contents match')
t.ok(buffers.length >= MIN_PIECES, 'received in multiple parts')
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()
})
@@ -39,7 +57,10 @@ test('large binary request without streaming', function (t) {
}, function (res) {
var buffers = []
res.on('end', function () {
t.ok(reference.equals(Buffer.concat(buffers)), 'contents match')
if (skipVerification)
t.skip('binary data not preserved on IE <= 8')
else
t.ok(reference.equals(Buffer.concat(buffers)), 'contents match')
t.end()
})
+10 -1
View File
@@ -5,6 +5,12 @@ 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) {
@@ -12,7 +18,10 @@ test('binary download', function (t) {
var buffers = []
res.on('end', function () {
t.ok(reference.equals(Buffer.concat(buffers)), 'contents match')
if (skipVerification)
t.skip('binary data not preserved on IE <= 8')
else
t.ok(reference.equals(Buffer.concat(buffers)), 'contents match')
t.end()
})
+13 -1
View File
@@ -5,6 +5,15 @@ 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) {
@@ -15,7 +24,10 @@ test('post binary', function (t) {
var buffers = []
res.on('end', function () {
t.ok(reference.equals(Buffer.concat(buffers)), 'echoed contents match')
if (skipVerification)
t.skip('binary data not preserved on this browser')
else
t.ok(reference.equals(Buffer.concat(buffers)), 'echoed contents match')
t.end()
})
+4 -1
View File
@@ -28,7 +28,10 @@ test('text streaming', function (t) {
var buffers = []
res.on('end', function () {
t.ok(buffers.length >= MIN_PIECES, 'received in multiple parts')
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()
})
+1
View File
@@ -15,6 +15,7 @@ var skipResponseUrl = ((browserName === 'IE') ||
(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')
+4 -24
View File
@@ -4,7 +4,7 @@ var test = require('tape')
var http = require('../..')
test('requestTimeout', function (t) {
test('timeout', function (t) {
var req = http.get({
path: '/browserify.png?copies=5',
requestTimeout: 10 // ms
@@ -16,14 +16,14 @@ test('requestTimeout', function (t) {
})
})
req.on('requestTimeout', function () {
t.pass('got requestTimeout')
t.pass('got timeout')
t.end()
})
})
// TODO: reenable this if there's a way to make it simultaneously
// fast and reliable
test.skip('no requestTimeout after success', function (t) {
test.skip('no timeout after success', function (t) {
var req = http.get({
path: '/basic.txt',
requestTimeout: 50000 // ms
@@ -38,26 +38,6 @@ test.skip('no requestTimeout after success', function (t) {
})
})
req.on('requestTimeout', function () {
t.fail('unexpected requestTimeout')
})
})
test('setTimeout', function (t) {
t.plan(2)
var req = http.get({
path: '/browserify.png?copies=5'
}, function (res) {
res.on('data', function (data) {
})
res.on('end', function () {
t.fail('request completed (should have timed out)')
})
})
req.setTimeout(10, function () {
t.pass('got setTimeout callback')
})
req.on('timeout', function () {
t.pass('got timeout')
t.fail('unexpected timeout')
})
})
+12 -2
View File
@@ -4,10 +4,20 @@ 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')
// Temporarily disabled due to https://github.com/browserify/webworkify/issues/41
test.skip('binary download in WebWorker', function (t) {
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')
+1 -1
View File
@@ -132,6 +132,6 @@ app.use(function (req, res, next) {
app.use(express.static(path.join(__dirname, 'static')))
var port = parseInt(process.env.AIRTAP_SUPPORT_PORT) || 8199
var port = parseInt(process.env.AIRTAP_PORT) || 8199
console.log('Test server listening on port', port)
server.listen(port)
+1
View File
@@ -0,0 +1 @@
../../../ie8-polyfill.js