Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c1417c5dfa | |||
| 8df10e0d65 | |||
| 5ee30ad5cc | |||
| 9321e56abd | |||
| 881e7c8f3d | |||
| 43761f2799 | |||
| 70f8c7d0b7 | |||
| f304ef13a2 | |||
| e8ef98b286 | |||
| 3af33f862f | |||
| 138d6c1d29 | |||
| f97dd885ea | |||
| f9fb1c724c | |||
| 1b077bf249 | |||
| e342cdbb10 | |||
| 0a04a09f68 | |||
| 57d0ab245f |
@@ -94,6 +94,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` options in the `request` method is non-operational in Safari <= 5 and
|
||||
Opera <= 12.
|
||||
|
||||
## Example
|
||||
|
||||
@@ -101,11 +103,11 @@ redirect pages.
|
||||
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__';
|
||||
});
|
||||
|
||||
+36
-7
@@ -6,12 +6,34 @@ try {
|
||||
exports.blobConstructor = true
|
||||
} catch (e) {}
|
||||
|
||||
var 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
|
||||
xhr.open('GET', global.XDomainRequest ? '/' : 'https://example.com')
|
||||
// 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
|
||||
@@ -24,17 +46,24 @@ function checkTypeSupport (type) {
|
||||
var haveArrayBuffer = typeof global.ArrayBuffer !== 'undefined'
|
||||
var haveSlice = haveArrayBuffer && isFunction(global.ArrayBuffer.prototype.slice)
|
||||
|
||||
exports.arraybuffer = haveArrayBuffer && checkTypeSupport('arraybuffer')
|
||||
// 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')
|
||||
exports.overrideMimeType = isFunction(xhr.overrideMimeType)
|
||||
|
||||
// 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'
|
||||
return typeof value === 'function'
|
||||
}
|
||||
|
||||
xhr = null // Help gc
|
||||
|
||||
+15
-3
@@ -38,8 +38,9 @@ var ClientRequest = module.exports = function (opts) {
|
||||
|
||||
var preferBinary
|
||||
var useFetch = true
|
||||
if (opts.mode === 'disable-fetch') {
|
||||
// If the use of XHR should be preferred and includes preserving the 'content-type' header
|
||||
if (opts.mode === 'disable-fetch' || 'timeout' in opts) {
|
||||
// If the use of XHR should be preferred and includes preserving the 'content-type' header.
|
||||
// Force XHR to be used since the Fetch API does not yet support timeouts.
|
||||
useFetch = false
|
||||
preferBinary = true
|
||||
} else if (opts.mode === 'prefer-streaming') {
|
||||
@@ -97,7 +98,7 @@ ClientRequest.prototype._onFinish = function () {
|
||||
var opts = self._opts
|
||||
|
||||
var headersObj = self._headers
|
||||
var body
|
||||
var body = null
|
||||
if (opts.method === 'POST' || opts.method === 'PUT' || opts.method === 'PATCH' || opts.method === 'MERGE') {
|
||||
if (capability.blobConstructor) {
|
||||
body = new global.Blob(self._body.map(function (buffer) {
|
||||
@@ -149,6 +150,13 @@ ClientRequest.prototype._onFinish = function () {
|
||||
if (self._mode === 'text' && 'overrideMimeType' in xhr)
|
||||
xhr.overrideMimeType('text/plain; charset=x-user-defined')
|
||||
|
||||
if ('timeout' in opts) {
|
||||
xhr.timeout = opts.timeout
|
||||
xhr.ontimeout = function () {
|
||||
self.emit('timeout')
|
||||
}
|
||||
}
|
||||
|
||||
Object.keys(headersObj).forEach(function (name) {
|
||||
xhr.setRequestHeader(headersObj[name].name, headersObj[name].value)
|
||||
})
|
||||
@@ -220,6 +228,10 @@ ClientRequest.prototype._connect = function () {
|
||||
return
|
||||
|
||||
self._response = new IncomingMessage(self._xhr, self._fetchResponse, self._mode)
|
||||
self._response.on('error', function(err) {
|
||||
self.emit('error', err)
|
||||
})
|
||||
|
||||
self.emit('response', self._response)
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,8 @@ var IncomingMessage = exports.IncomingMessage = function (xhr, response, mode) {
|
||||
}
|
||||
self.push(new Buffer(result.value))
|
||||
read()
|
||||
}).catch(function(err) {
|
||||
self.emit('error', err)
|
||||
})
|
||||
}
|
||||
read()
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "stream-http",
|
||||
"version": "2.5.0",
|
||||
"version": "2.6.2",
|
||||
"description": "Streaming http in the browser",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -27,7 +27,7 @@
|
||||
"http-browserify"
|
||||
],
|
||||
"dependencies": {
|
||||
"builtin-status-codes": "^2.0.0",
|
||||
"builtin-status-codes": "^3.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
"readable-stream": "^2.1.0",
|
||||
"to-arraybuffer": "^1.0.0",
|
||||
|
||||
@@ -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('delete empty', function (t) {
|
||||
var req = http.request({
|
||||
path: '/verifyEmpty',
|
||||
method: 'DELETE'
|
||||
}, 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()
|
||||
})
|
||||
@@ -0,0 +1,32 @@
|
||||
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
|
||||
|
||||
var skipTimeout = ((browserName === 'Opera' && browserVersion <= 12) ||
|
||||
(browserName === 'Safari' && browserVersion <= 5))
|
||||
|
||||
|
||||
test('emits timeout events', function (t) {
|
||||
if (skipTimeout) {
|
||||
return t.skip('Browser does not support setting timeouts')
|
||||
}
|
||||
|
||||
var req = http.request({
|
||||
path: '/basic.txt',
|
||||
timeout: 1
|
||||
})
|
||||
|
||||
req.on('timeout', function () {
|
||||
t.end() // the test will timeout if this does not happen
|
||||
})
|
||||
|
||||
req.end()
|
||||
})
|
||||
@@ -71,6 +71,24 @@ app.post('/echo', function (req, res) {
|
||||
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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user