Compare commits

...

12 Commits

Author SHA1 Message Date
John Hiesey ae48c2c71c 2.0.3 2016-01-08 22:31:45 +01:00
John Hiesey 570f85ec18 Check xhr.status is non-zero to avoid running onXHRProgress on error
Previously, onXHRProgress was being run when readyState was being
set to DONE on error, which was causing exceptions.
2015-10-13 15:46:01 -07:00
John Hiesey 85381f0e3d 2.0.2 2015-10-01 23:01:00 -07:00
John Hiesey 3bea2a493f Don't turn exceptions thrown in the callback into 'error' events
When using the fetch api, exceptions thrown inside
ClientRequest._onFinish, which also calls the user callback,
were being caught and turned into 'error' events. This is
not the correct behavior; these exceptions should remain
uncaught to let the browser print them to the console.
2015-10-01 22:26:59 -07:00
John Hiesey 016f4be96f 2.0.1 2015-09-30 13:23:16 -07:00
John Hiesey 9e5e75b5ff Merge pull request #27 from mbcrute/patch-body
Don't ignore PATCH request bodies
2015-09-30 13:10:09 -07:00
Matt Crute 031b8aab89 Stop ignoring PATCH request bodies 2015-09-30 11:01:26 -07:00
John Hiesey dc421eb355 2.0.0 2015-09-15 17:24:23 -07:00
John Hiesey 7d1dfd2ab1 Merge pull request #25 from jhiesey/polyfills
Require polyfills for IE8 support to reduce bundle size
2015-09-15 17:23:40 -07:00
John Hiesey 75817ba930 Fix tests 2015-09-15 16:54:17 -07:00
John Hiesey 2152af414a Remove implementations of ES5 functions not provided in ie8
From now on, IE8 is only supported if polyfills for `Object.keys`,
`Array.prototype.forEach`, and `Array.prototype.indexOf` are
provided. The suggested polyfills from MDN are provided in
ie8-polyfill.js, or you can use https://github.com/es-shims/es5-shim
for a more complete set of polyfills.
2015-09-15 16:54:17 -07:00
John Hiesey a10d19b3b2 Fix occasional test errors on Chrome 45
If multiple chunks of data got queued up, the abort on data test
would erroneously fail. Wait till the end of the tick before
considering 'data' events as errors.
2015-09-15 16:52:11 -07:00
10 changed files with 204 additions and 36 deletions
+2 -1
View File
@@ -16,4 +16,5 @@ browsers:
version: 4.0..latest
server: ./test/server/index.js
scripts:
- "/polyfill.js"
- "/ie8-polyfill.js"
- "/test-polyfill.js"
+7
View File
@@ -29,6 +29,13 @@ All browsers newer than IE8 support binary responses. All of the above browsers
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
+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;
};
}
+11 -12
View File
@@ -1,9 +1,6 @@
// var Base64 = require('Base64')
var capability = require('./capability')
var foreach = require('foreach')
var indexOf = require('indexof')
var inherits = require('inherits')
var keys = require('object-keys')
var response = require('./response')
var stream = require('stream')
@@ -35,7 +32,7 @@ var ClientRequest = module.exports = function (opts) {
self._headers = {}
if (opts.auth)
self.setHeader('Authorization', 'Basic ' + new Buffer(opts.auth).toString('base64'))
foreach(keys(opts.headers), function (name) {
Object.keys(opts.headers).forEach(function (name) {
self.setHeader(name, opts.headers[name])
})
@@ -68,7 +65,7 @@ ClientRequest.prototype.setHeader = function (name, value) {
// 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 (indexOf(unsafeHeaders, lowerName) !== -1)
if (unsafeHeaders.indexOf(lowerName) !== -1)
return
self._headers[lowerName] = {
@@ -96,7 +93,7 @@ ClientRequest.prototype._onFinish = function () {
var headersObj = self._headers
var body
if (opts.method === 'POST' || opts.method === 'PUT') {
if (opts.method === 'POST' || opts.method === 'PUT' || opts.method === 'PATCH') {
if (capability.blobConstructor) {
body = new global.Blob(self._body.map(function (buffer) {
return buffer.toArrayBuffer()
@@ -110,7 +107,7 @@ ClientRequest.prototype._onFinish = function () {
}
if (self._mode === 'fetch') {
var headers = keys(headersObj).map(function (name) {
var headers = Object.keys(headersObj).map(function (name) {
return [headersObj[name].name, headersObj[name].value]
})
@@ -123,7 +120,7 @@ ClientRequest.prototype._onFinish = function () {
}).then(function (response) {
self._fetchResponse = response
self._connect()
}).then(undefined, function (reason) {
}, function (reason) {
self.emit('error', reason)
})
} else {
@@ -147,7 +144,7 @@ ClientRequest.prototype._onFinish = function () {
if (self._mode === 'text' && 'overrideMimeType' in xhr)
xhr.overrideMimeType('text/plain; charset=x-user-defined')
foreach(keys(headersObj), function (name) {
Object.keys(headersObj).forEach(function (name) {
xhr.setRequestHeader(headersObj[name].name, headersObj[name].value)
})
@@ -186,12 +183,14 @@ ClientRequest.prototype._onFinish = function () {
}
/**
* Checks if xhr.status is readable. Even though the spec says it should
* be available in readyState 3, accessing it throws an exception in IE8
* 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 {
return (xhr.status !== null)
var status = xhr.status
return (status !== null && status !== 0)
} catch (e) {
return false
}
+1 -2
View File
@@ -1,5 +1,4 @@
var capability = require('./capability')
var foreach = require('foreach')
var inherits = require('inherits')
var stream = require('stream')
@@ -64,7 +63,7 @@ var IncomingMessage = exports.IncomingMessage = function (xhr, response, mode) {
self.statusCode = xhr.status
self.statusMessage = xhr.statusText
var headers = xhr.getAllResponseHeaders().split(/\r?\n/)
foreach(headers, function (header) {
headers.forEach(function (header) {
var matches = header.match(/^([^:]+):\s*(.*)/)
if (matches) {
var key = matches[1].toLowerCase()
+1 -4
View File
@@ -1,6 +1,6 @@
{
"name": "stream-http",
"version": "1.7.1",
"version": "2.0.3",
"description": "Streaming http in the browser",
"main": "index.js",
"repository": {
@@ -24,10 +24,7 @@
],
"dependencies": {
"builtin-status-codes": "^1.0.0",
"foreach": "^2.0.5",
"indexof": "0.0.1",
"inherits": "^2.0.1",
"object-keys": "^1.0.4",
"xtend": "^4.0.0"
},
"devDependencies": {
+10 -3
View File
@@ -30,18 +30,25 @@ test('abort on response', function (t) {
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 (firstData) {
if (failOnData)
t.fail('unexpected data')
else if (firstData) {
firstData = false
req.abort()
t.end()
} else {
t.fail('unexpected data')
process.nextTick(function () {
// Wait for any data that may have been queued
// in the stream before considering data events
// as errors
failOnData = true
})
}
})
})
+3 -13
View File
@@ -1,20 +1,10 @@
var Buffer = require('buffer').Buffer
var fs = require('fs')
var keys = require('object-keys')
var test = require('tape')
var UAParser = require('ua-parser-js')
var http = require('../..')
function indexOf (arr, item) {
var len = arr.length
for (var i = 0; i < len; i++) {
if (arr[i] === item)
return i
}
return -1
}
test('headers', function (t) {
http.get({
path: '/testHeaders?Response-Header=bar&Response-Header-2=BAR2',
@@ -29,10 +19,10 @@ test('headers', function (t) {
if (lowerKey.indexOf('test-') === 0)
rawHeaders.push(lowerKey, res.rawHeaders[i + 1])
}
var header1Pos = indexOf(rawHeaders, 'test-response-header')
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 = indexOf(rawHeaders, 'test-response-header-2')
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')
@@ -46,7 +36,7 @@ test('headers', function (t) {
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(keys(body).length, 2, 'correct number of request headers')
t.equal(Object.keys(body).length, 2, 'correct number of request headers')
t.end()
})
+1
View File
@@ -0,0 +1 @@
../../../ie8-polyfill.js
@@ -1,4 +1,3 @@
// TODO: send a PR to url to remove .trim() so this isn't necessary
if (!String.prototype.trim) {
(function() {
// Make sure we trim BOM and NBSP