Comparing version 1.0.4 to 1.1.0
195
lib/bhttp.js
@@ -1,2 +0,2 @@ | ||
var Promise, S, addErrorData, bhttpAPI, concatStream, createCookieJar, debug, doPayloadRequest, doRedirect, errors, formData, formFixArray, http, https, isStream, makeRequest, ofTypes, packageConfig, prepareCleanup, prepareDefaults, prepareOptions, preparePayload, prepareProtocol, prepareRequest, prepareSession, prepareUrl, processResponse, querystring, redirectGet, redirectUnchanged, stream, streamLength, toughCookie, urlUtil, util, _; | ||
var Promise, S, addErrorData, bhttpAPI, bhttpErrors, concatStream, createCookieJar, debug, debugRequest, debugResponse, doPayloadRequest, doRedirect, errors, extend, formData, formFixArray, http, https, isStream, makeRequest, ofTypes, packageConfig, prepareCleanup, prepareDefaults, prepareOptions, preparePayload, prepareProtocol, prepareRequest, prepareSession, prepareUrl, processResponse, querystring, redirectGet, redirectUnchanged, sink, spy, stream, streamLength, toughCookie, urlUtil, util, _; | ||
@@ -25,4 +25,10 @@ urlUtil = require("url"); | ||
debug = require("debug")("bhttp"); | ||
debug = require("debug"); | ||
debugRequest = debug("bhttp:request"); | ||
debugResponse = debug("bhttp:response"); | ||
extend = require("extend"); | ||
formData = require("form-data2"); | ||
@@ -36,6 +42,13 @@ | ||
sink = require("through2-sink"); | ||
spy = require("through2-spy"); | ||
packageConfig = require("../package.json"); | ||
bhttpErrors = {}; | ||
errors.create({ | ||
name: "bhttpError" | ||
name: "bhttpError", | ||
scope: bhttpErrors | ||
}); | ||
@@ -45,3 +58,4 @@ | ||
name: "ConflictingOptionsError", | ||
parents: errors.bhttpError | ||
parents: bhttpErrors.bhttpError, | ||
scope: bhttpErrors | ||
}); | ||
@@ -51,3 +65,4 @@ | ||
name: "UnsupportedProtocolError", | ||
parents: errors.bhttpError | ||
parents: bhttpErrors.bhttpError, | ||
scope: bhttpErrors | ||
}); | ||
@@ -57,3 +72,4 @@ | ||
name: "RedirectError", | ||
parents: errors.bhttpError | ||
parents: bhttpErrors.bhttpError, | ||
scope: bhttpErrors | ||
}); | ||
@@ -63,5 +79,18 @@ | ||
name: "MultipartError", | ||
parents: errors.bhttpError | ||
parents: bhttpErrors.bhttpError, | ||
scope: bhttpErrors | ||
}); | ||
errors.create({ | ||
name: "ConnectionTimeoutError", | ||
parents: bhttpErrors.bhttpError, | ||
scope: bhttpErrors | ||
}); | ||
errors.create({ | ||
name: "ResponseTimeoutError", | ||
parents: bhttpErrors.bhttpError, | ||
scope: bhttpErrors | ||
}); | ||
ofTypes = function(obj, types) { | ||
@@ -89,3 +118,3 @@ var match, type, _i, _len; | ||
prepareSession = function(request, response, requestState) { | ||
debug("preparing session"); | ||
debugRequest("preparing session"); | ||
return Promise["try"](function() { | ||
@@ -106,3 +135,3 @@ if (requestState.sessionOptions != null) { | ||
}).then(function(cookieString) { | ||
debug("sending cookie string: %s", cookieString); | ||
debugRequest("sending cookie string: %s", cookieString); | ||
request.options.headers["cookie"] = cookieString; | ||
@@ -118,3 +147,3 @@ return Promise.resolve([request, response, requestState]); | ||
prepareDefaults = function(request, response, requestState) { | ||
debug("preparing defaults"); | ||
debugRequest("preparing defaults"); | ||
return Promise["try"](function() { | ||
@@ -130,3 +159,5 @@ var _base, _base1, _base2, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7; | ||
justPrepare: (_ref6 = request.options.justPrepare) != null ? _ref6 : false, | ||
redirectLimit: (_ref7 = request.options.redirectLimit) != null ? _ref7 : 10 | ||
redirectLimit: (_ref7 = request.options.redirectLimit) != null ? _ref7 : 10, | ||
onDownloadProgress: request.options.onDownloadProgress, | ||
responseTimeout: request.options.responseTimeout | ||
}; | ||
@@ -148,3 +179,3 @@ if ((_base = request.options).allowChunkedMultipart == null) { | ||
prepareUrl = function(request, response, requestState) { | ||
debug("preparing URL"); | ||
debugRequest("preparing URL"); | ||
return Promise["try"](function() { | ||
@@ -167,3 +198,3 @@ var urlOptions, _ref; | ||
prepareProtocol = function(request, response, requestState) { | ||
debug("preparing protocol"); | ||
debugRequest("preparing protocol"); | ||
return Promise["try"](function() { | ||
@@ -182,3 +213,3 @@ var _base; | ||
if (request.protocolModule == null) { | ||
return Promise.reject()(new errors.UnsupportedProtocolError("The protocol specified (" + protocol + ") is not currently supported by this module.")); | ||
return Promise.reject()(new bhttpErrors.UnsupportedProtocolError("The protocol specified (" + protocol + ") is not currently supported by this module.")); | ||
} | ||
@@ -200,10 +231,10 @@ if ((_base = request.options).port == null) { | ||
prepareOptions = function(request, response, requestState) { | ||
debug("preparing options"); | ||
debugRequest("preparing options"); | ||
return Promise["try"](function() { | ||
var _base; | ||
if (((request.options.formFields != null) || (request.options.files != null)) && ((request.options.inputStream != null) || (request.options.inputBuffer != null))) { | ||
return Promise.reject(addErrorData(new errors.ConflictingOptionsError("You cannot define both formFields/files and a raw inputStream or inputBuffer."), request, response, requestState)); | ||
return Promise.reject(addErrorData(new bhttpErrors.ConflictingOptionsError("You cannot define both formFields/files and a raw inputStream or inputBuffer."), request, response, requestState)); | ||
} | ||
if (request.options.encodeJSON && ((request.options.inputStream != null) || (request.options.inputBuffer != null))) { | ||
return Promise.reject(addErrorData(new errors.ConflictingOptionsError("You cannot use both encodeJSON and a raw inputStream or inputBuffer.", void 0, "If you meant to JSON-encode the stream, you will currently have to do so manually."), request, response, requestState)); | ||
return Promise.reject(addErrorData(new bhttpErrors.ConflictingOptionsError("You cannot use both encodeJSON and a raw inputStream or inputBuffer.", void 0, "If you meant to JSON-encode the stream, you will currently have to do so manually."), request, response, requestState)); | ||
} | ||
@@ -220,5 +251,6 @@ if (request.responseOptions.stream) { | ||
preparePayload = function(request, response, requestState) { | ||
debug("preparing payload"); | ||
debugRequest("preparing payload"); | ||
return Promise["try"](function() { | ||
var containsStreams, fieldName, fieldValue, formDataObject, multipart, streamOptions, valueElement, _i, _len, _ref, _ref1, _ref2; | ||
request.onUploadProgress = request.options.onUploadProgress; | ||
multipart = request.options.forceMultipart || (request.options.files != null); | ||
@@ -233,7 +265,7 @@ multipart = multipart || _.any(request.options.formFields, function(item) { | ||
if (request.options.encodeJSON && containsStreams) { | ||
return Promise.reject()(new errors.ConflictingOptionsError("Sending a JSON-encoded payload containing data from a stream is not currently supported.", void 0, "Either don't use encodeJSON, or read your stream into a string or Buffer.")); | ||
return Promise.reject()(new bhttpErrors.ConflictingOptionsError("Sending a JSON-encoded payload containing data from a stream is not currently supported.", void 0, "Either don't use encodeJSON, or read your stream into a string or Buffer.")); | ||
} | ||
if ((_ref = request.options.method) === "post" || _ref === "put" || _ref === "patch") { | ||
if ((request.options.encodeJSON || (request.options.formFields != null)) && !multipart) { | ||
debug("got url-encodable form-data"); | ||
debugRequest("got url-encodable form-data"); | ||
request.options.headers["content-type"] = "application/x-www-form-urlencoded"; | ||
@@ -250,3 +282,3 @@ if (request.options.encodeJSON) { | ||
} else if ((request.options.formFields != null) && multipart) { | ||
debug("got multipart form-data"); | ||
debugRequest("got multipart form-data"); | ||
formDataObject = new formData(); | ||
@@ -282,3 +314,3 @@ _ref2 = formFixArray(request.options.formFields); | ||
} else if (request.options.inputStream != null) { | ||
debug("got inputStream"); | ||
debugRequest("got inputStream"); | ||
return Promise["try"](function() { | ||
@@ -293,10 +325,10 @@ var _ref3; | ||
}).then(function(length) { | ||
debug("length for inputStream is %s", length); | ||
debugRequest("length for inputStream is %s", length); | ||
return request.options.headers["content-length"] = length; | ||
})["catch"](function(err) { | ||
debug("unable to determine inputStream length, switching to chunked transfer encoding"); | ||
debugRequest("unable to determine inputStream length, switching to chunked transfer encoding"); | ||
return request.options.headers["content-transfer-encoding"] = "chunked"; | ||
}); | ||
} else if (request.options.inputBuffer != null) { | ||
debug("got inputBuffer"); | ||
debugRequest("got inputBuffer"); | ||
if (typeof request.options.inputBuffer === "string") { | ||
@@ -307,3 +339,3 @@ request.payload = new Buffer(request.options.inputBuffer); | ||
} | ||
debug("length for inputBuffer is %s", request.payload.length); | ||
debugRequest("length for inputBuffer is %s", request.payload.length); | ||
request.options.headers["content-length"] = request.payload.length; | ||
@@ -321,6 +353,6 @@ return Promise.resolve(); | ||
prepareCleanup = function(request, response, requestState) { | ||
debug("preparing cleanup"); | ||
debugRequest("preparing cleanup"); | ||
return Promise["try"](function() { | ||
var fixedHeaders, key, value, _i, _len, _ref, _ref1; | ||
_ref = ["query", "formFields", "files", "encodeJSON", "inputStream", "inputBuffer", "discardResponse", "keepRedirectResponses", "followRedirects", "noDecode", "decodeJSON", "allowChunkedMultipart", "forceMultipart"]; | ||
_ref = ["query", "formFields", "files", "encodeJSON", "inputStream", "inputBuffer", "discardResponse", "keepRedirectResponses", "followRedirects", "noDecode", "decodeJSON", "allowChunkedMultipart", "forceMultipart", "onUploadProgress", "onDownloadProgress"]; | ||
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | ||
@@ -342,3 +374,3 @@ key = _ref[_i]; | ||
prepareRequest = function(request, response, requestState) { | ||
debug("preparing request"); | ||
debugRequest("preparing request"); | ||
return Promise["try"](function() { | ||
@@ -358,26 +390,61 @@ var middlewareFunctions, promiseChain; | ||
makeRequest = function(request, response, requestState) { | ||
debug("making %s request to %s", request.options.method.toUpperCase(), request.url); | ||
debugRequest("making %s request to %s", request.options.method.toUpperCase(), request.url); | ||
return Promise["try"](function() { | ||
var req; | ||
var req, timeoutTimer; | ||
req = request.protocolModule.request(request.options); | ||
if (request.payload != null) { | ||
debug("sending payload"); | ||
req.write(request.payload); | ||
req.end(); | ||
} else if (request.payloadStream != null) { | ||
debug("piping payloadStream"); | ||
if (request.payloadStream._bhttpStreamWrapper != null) { | ||
request.payloadStream.stream.pipe(req); | ||
timeoutTimer = null; | ||
return new Promise(function(resolve, reject) { | ||
var completedBytes, progressStream, totalBytes; | ||
if (request.responseOptions.responseTimeout != null) { | ||
debugRequest("setting response timeout timer to " + request.responseOptions.responseTimeout + "ms..."); | ||
req.on("socket", function(socket) { | ||
var timeoutHandler; | ||
timeoutHandler = function() { | ||
debugRequest("a response timeout occurred!"); | ||
req.abort(); | ||
return reject(addErrorData(new bhttpErrors.ResponseTimeoutError("The response timed out."))); | ||
}; | ||
return timeoutTimer = setTimeout(timeoutHandler, request.responseOptions.responseTimeout); | ||
}); | ||
} | ||
totalBytes = request.options.headers["content-length"]; | ||
completedBytes = 0; | ||
progressStream = spy(function(chunk) { | ||
completedBytes += chunk.length; | ||
return req.emit("progress", completedBytes, totalBytes); | ||
}); | ||
if (request.onUploadProgress != null) { | ||
req.on("progress", function(completedBytes, totalBytes) { | ||
return request.onUploadProgress(completedBytes, totalBytes, req); | ||
}); | ||
} | ||
if (request.payload != null) { | ||
debugRequest("sending payload"); | ||
req.emit("progress", request.payload.length, request.payload.length); | ||
req.write(request.payload); | ||
req.end(); | ||
} else if (request.payloadStream != null) { | ||
debugRequest("piping payloadStream"); | ||
if (request.payloadStream._bhttpStreamWrapper != null) { | ||
request.payloadStream.stream.pipe(progressStream).pipe(req); | ||
} else { | ||
request.payloadStream.pipe(progressStream).pipe(req); | ||
} | ||
} else { | ||
request.payloadStream.pipe(req); | ||
debugRequest("closing request without payload"); | ||
req.end(); | ||
} | ||
} else { | ||
debug("closing request without payload"); | ||
req.end(); | ||
} | ||
return new Promise(function(resolve, reject) { | ||
req.on("error", function(err) { | ||
return reject(err); | ||
if (err.code === "ETIMEDOUT") { | ||
debugRequest("a connection timeout occurred!"); | ||
return reject(addErrorData(new bhttpErrors.ConnectionTimeoutError("The connection timed out."))); | ||
} else { | ||
return reject(err); | ||
} | ||
}); | ||
return req.on("response", function(res) { | ||
if (timeoutTimer != null) { | ||
debugResponse("got response in time, clearing response timeout timer"); | ||
clearTimeout(timeoutTimer); | ||
} | ||
return resolve(res); | ||
@@ -392,3 +459,3 @@ }); | ||
processResponse = function(request, response, requestState) { | ||
debug("processing response, got status code %s", response.statusCode); | ||
debugResponse("processing response, got status code %s", response.statusCode); | ||
return Promise["try"](function() { | ||
@@ -403,3 +470,3 @@ var cookieHeader, promises; | ||
cookieHeader = _ref[_i]; | ||
debug("storing cookie: %s", cookieHeader); | ||
debugResponse("storing cookie: %s", cookieHeader); | ||
_results.push(request.cookieJar.set(cookieHeader, request.url)); | ||
@@ -414,3 +481,3 @@ } | ||
}).then(function() { | ||
var _ref, _ref1; | ||
var completedBytes, progressStream, totalBytes, _ref, _ref1; | ||
response.request = request; | ||
@@ -421,3 +488,3 @@ response.requestState = requestState; | ||
if (requestState.redirectHistory.length >= (request.responseOptions.redirectLimit - 1)) { | ||
return Promise.reject(addErrorData(new errors.RedirectError("The maximum amount of redirects ({request.responseOptions.redirectLimit}) was reached."))); | ||
return Promise.reject(addErrorData(new bhttpErrors.RedirectError("The maximum amount of redirects ({request.responseOptions.redirectLimit}) was reached."))); | ||
} | ||
@@ -434,5 +501,5 @@ switch (response.statusCode) { | ||
case "delete": | ||
return Promise.reject(addErrorData(new errors.RedirectError("Encountered a 301 redirect for POST, PUT, PATCH or DELETE. RFC says we can't automatically continue."), request, response, requestState)); | ||
return Promise.reject(addErrorData(new bhttpErrors.RedirectError("Encountered a 301 redirect for POST, PUT, PATCH or DELETE. RFC says we can't automatically continue."), request, response, requestState)); | ||
default: | ||
return Promise.reject(addErrorData(new errors.RedirectError("Encountered a 301 redirect, but not sure how to proceed for the " + (request.options.method.toUpperCase()) + " method."))); | ||
return Promise.reject(addErrorData(new bhttpErrors.RedirectError("Encountered a 301 redirect, but not sure how to proceed for the " + (request.options.method.toUpperCase()) + " method."))); | ||
} | ||
@@ -445,3 +512,3 @@ break; | ||
if (request.containsStreams && ((_ref1 = request.options.method) !== "get" && _ref1 !== "head")) { | ||
return Promise.reject(addErrorData(new errors.RedirectError("Encountered a 307 redirect for POST, PUT or DELETE, but your payload contained (single-use) streams. We therefore can't automatically follow the redirect."), request, response, requestState)); | ||
return Promise.reject(addErrorData(new bhttpErrors.RedirectError("Encountered a 307 redirect for POST, PUT or DELETE, but your payload contained (single-use) streams. We therefore can't automatically follow the redirect."), request, response, requestState)); | ||
} else { | ||
@@ -455,3 +522,19 @@ return redirectUnchanged(request, response, requestState); | ||
} else { | ||
totalBytes = response.headers["content-length"]; | ||
if (totalBytes != null) { | ||
totalBytes = parseInt(totalBytes); | ||
} | ||
completedBytes = 0; | ||
progressStream = sink(function(chunk) { | ||
completedBytes += chunk.length; | ||
return response.emit("progress", completedBytes, totalBytes); | ||
}); | ||
if (request.responseOptions.onDownloadProgress != null) { | ||
response.on("progress", function(completedBytes, totalBytes) { | ||
return request.responseOptions.onDownloadProgress(completedBytes, totalBytes, response); | ||
}); | ||
} | ||
return new Promise(function(resolve, reject) { | ||
response.pipe(progressStream); | ||
response.pause(); | ||
if (request.responseOptions.stream) { | ||
@@ -497,3 +580,3 @@ return resolve(response); | ||
redirectGet = function(request, response, requestState) { | ||
debug("following forced-GET redirect to %s", response.headers["location"]); | ||
debugResponse("following forced-GET redirect to %s", response.headers["location"]); | ||
return Promise["try"](function() { | ||
@@ -513,3 +596,3 @@ var key, options, _i, _len, _ref; | ||
redirectUnchanged = function(request, response, requestState) { | ||
debug("following same-method redirect to %s", response.headers["location"]); | ||
debugResponse("following same-method redirect to %s", response.headers["location"]); | ||
return Promise["try"](function() { | ||
@@ -687,2 +770,4 @@ var options; | ||
extend(bhttpAPI, bhttpErrors); | ||
module.exports = bhttpAPI; |
{ | ||
"name": "bhttp", | ||
"version": "1.0.4", | ||
"version": "1.1.0", | ||
"description": "A sane HTTP client library for Node.js with Streams2 support.", | ||
@@ -29,2 +29,3 @@ "main": "index.js", | ||
"errors": "^0.2.0", | ||
"extend": "^2.0.0", | ||
"form-data2": "^1.0.0", | ||
@@ -35,2 +36,4 @@ "form-fix-array": "^1.0.0", | ||
"string": "^3.0.0", | ||
"through2-sink": "^1.0.0", | ||
"through2-spy": "^1.2.0", | ||
"tough-cookie": "^0.12.1" | ||
@@ -37,0 +40,0 @@ }, |
109
README.md
@@ -5,2 +5,4 @@ # bhttp | ||
![](//img.shields.io/gratipay/joepie91.svg) | ||
## Why bhttp? | ||
@@ -10,3 +12,3 @@ | ||
* The core `http` module is rather low-level, and even relatively simple requests take a lot of work to make correctly. It also automatically uses an agent for HTTP requests, which slows down concurrent HTTP requests when you're streaming the responses somewhere. | ||
* The core `http` module is rather low-level, and even relatively simple requests take a lot of work to make correctly. It also automatically uses a limited amount of agents for HTTP requests (in Node.js 0.10), which slows down concurrent HTTP requests when you're streaming the responses somewhere. | ||
* `request` is buggy, only supports old-style streams, has the same 'agent' problem as `http`, the documentation is poor, and the API is not very intuitive. | ||
@@ -26,7 +28,10 @@ * `needle` is a lot simpler, but suffers from the same 'agent' problem, and the API can be a bit annoying in some ways. It also doesn't have a proper session API. | ||
* Optionally, a Promises API (you can also use nodebacks). | ||
* Progress events! For both uploading and downloading. | ||
## Caveats | ||
* `bhttp` does not yet use a HTTPS-capable agent. This means that all SSL-related options are currently ignored (per Node.js `http` documentation). If you need secure HTTPS requests, make sure to specify a custom agent! | ||
`bhttp` does not yet use a HTTPS-capable agent. This means that all SSL-related options are currently ignored by default (per Node.js `http` documentation). | ||
__This does *not* mean that you cannot use `bhttp` for HTTPS requests!__ If you need secure HTTPS requests, just make sure to specify a [custom `https` agent](https://nodejs.org/api/https.html#https_class_https_agent). | ||
## License | ||
@@ -112,2 +117,40 @@ | ||
### Progress events | ||
Upload progress events: | ||
```javascript | ||
var Promise = require("bluebird"); | ||
var bhttp = require("bhttp"); | ||
Promise.try(function() { | ||
return bhttp.post("http://somehostingservice.com/upload", { | ||
file: fs.createReadStream("./bigfile.mkv") | ||
}, { | ||
onUploadProgress: function(completedBytes, totalBytes) { | ||
console.log("Upload progress:", (completedBytes / totalBytes * 100), "%"); | ||
} | ||
}); | ||
}).then(function(response) { | ||
console.log("Response from hosting service:", response.body.toString()); | ||
}); | ||
``` | ||
Download progress events: | ||
```javascript | ||
var Promise = require("bluebird"); | ||
var bhttp = require("bhttp"); | ||
Promise.try(function() { | ||
return bhttp.get("http://somehostingservice.com/bigfile.mkv", {stream: true}); | ||
}).then(function(response) { | ||
response.on("progress", function(completedBytes, totalBytes) { | ||
console.log("Download progress:", (completedBytes / totalBytes * 100), "%"); | ||
}); | ||
response.pipe(fs.createWriteStream("./bigfile.mkv")); | ||
}); | ||
``` | ||
### Sessions | ||
@@ -132,2 +175,4 @@ | ||
The various error types are documented at the bottom of this README. | ||
### bhttp.head(url, [options, [callback]]) | ||
@@ -157,2 +202,4 @@ ### bhttp.get(url, [options, [callback]]) | ||
Note that (progress) event handlers must be specified in the `options` or (in the case of download progress events) as an event listener on the response object - as `bhttp` uses Promises, it is not technically possible to return an EventEmitter. | ||
* __url__: The URL to request, with protocol. When using HTTPS, please be sure to read the 'Caveats' section. | ||
@@ -178,2 +225,3 @@ * __options__: *Optional.* Extra options for the request. Any other options not listed here will be passed on directly to the `http` or `https` module. | ||
* __cookieJar__: A custom cookie jar to use. You'll probably want to use `bhttp.session()` instead. | ||
* __responseTimeout__: The timeout, in milliseconds, after which the request should be considered to have failed if no response is received yet. Note that this measures from the start of the request to the start of the response, and is *not* a connection timeout. If a timeout occurs, a ResponseTimeoutError will be thrown asynchronously (see error documentation below). | ||
* __allowChunkedMultipart__: *Defaults to `false`.* Many servers don't support `multipart/form-data` when it is transmitted with chunked transfer encoding (eg. when the stream length is unknown), and silently fail with an empty request payload - this is why `bhttp` disallows it by default. If you are *absolutely certain* that the endpoint supports this functionality, you can override the behaviour by setting this to `true`. | ||
@@ -183,2 +231,5 @@ * __discardResponse__: *Defaults to `false`.* Whether to throw away the response without reading it. Only really useful for fire-and-forget calls. This is almost never what you want. | ||
* __justPrepare__: *Defaults to `false`.* When set to `true`, bhttp just prepares the request, and doesn't actually carry it out; useful if you want to make some manual modifications. Instead of a response, the method will asynchronously return an array with the signature `[request, response, requestState]` that you will need to pass into the `bhttp.makeRequest()` method. | ||
* __Event handlers__ | ||
* __onUploadProgress__: A callback to call for upload progress events (this covers both input streams and form data). The callback signature is `(completedBytes, totalBytes, request)`. If the total size is not known, `totalBytes` will be `undefined`. The `request` variable will hold the request object that the progress event applies to - this is relevant when dealing with automatic redirect following, where multiple requests may occur. | ||
* __onDownloadProgress__: A callback to call for download progress events. The callback signature is `(completedBytes, totalBytes, response)`. If the total size is not known, `totalBytes` will be `undefined`. The `response` variable will hold the response object that the progress event applies to - this is relevant when dealing with automatic redirect following, where multiple responses may occur. *Note that using the `progress` event on a response object is usually a more practical option!* | ||
@@ -194,2 +245,6 @@ * __callback__: *Optional.* When using the nodeback API, the callback to use. If not specified, a Promise will be returned. | ||
Additionally, there's an extra event on the `response` object: | ||
* __'progress' (completedBytes, totalBytes)__: The 'download progress' for the response body. This works the same as the `onDownloadProgress` option, except the event will be specific to this response, and it allows for somewhat nicer syntax. Make sure to attach this handler *before* you start reading the response stream! | ||
`bhttp` can automatically parse the metadata for the following types of streams: | ||
@@ -202,3 +257,3 @@ | ||
If you are using a different type of streams, you can wrap the stream using `bhttp.wrapStream` to manually specify the needed metadata. | ||
If you are using a different type of stream, you can wrap the stream using `bhttp.wrapStream` to manually specify the needed metadata. | ||
@@ -226,1 +281,49 @@ ### bhttp.session([defaultOptions]) | ||
When using the `justPrepare` option, you can use this method to proceed with the request after manual modifications. The function signature is identical to the signature of the array returned when using `justPrepare`. `response` will usually be `null`, but must be passed on as is, to account for future API changes. | ||
## Error types | ||
All these correctly extend the `Error` class - this means that you can use them as a `.catch` predicate when using Promises, and that you can use `instanceof` on them when using the nodeback API. | ||
### bhttp.bhttpError | ||
The base class for all errors generated by `bhttp`. You usually don't need this. | ||
### bhttp.ConflictingOptionsError | ||
You have specified two or more request options that cannot be used together. | ||
The error message will contain more details. | ||
### bhttp.UnsupportedProtocolError | ||
You tried to load a URL that isn't using either the HTTP or HTTPS protocol. Only HTTP and HTTPS are currently supported. | ||
### bhttp.RedirectError | ||
A redirect was encountered that could not be followed. | ||
This could be because the redirect limit was reached, or because the HTTP specification doesn't allow automatic following of the redirect that was encountered. | ||
The error message will contain more details. | ||
### bhttp.MultipartError | ||
Something went wrong while generating the multipart/form-data stream. | ||
Currently, this will only be thrown if you try to use chunked transfer encoding for a multipart stream - a common situation where this can occur, is when you pass in streams with an unknown length. | ||
To resolve this error, you must either explicitly specify the length of the streams using `bhttp.wrapStream` or, if the target server supports it, enable the `allowChunkedMultipart` option. | ||
### bhttp.ConnectionTimeoutError | ||
The connection timed out. | ||
The connection timeout is defined by the operating system, and cannot currently be overridden. | ||
### bhttp.ResponseTimeoutError | ||
The response timed out. | ||
The response timeout can be specified using the `responseTimeout` option, and it is measured from the start of the request to the start of the response. If no response is received within the `responseTimeout`, a `ResponseTimeoutError` will be thrown asynchronously, and the request will be aborted. | ||
__You should not set a `responseTimeout` for requests that involve large file uploads!__ Because a response can only be received *after* the request has completed, any file/stream upload that takes longer than the `responseTimeout`, will result in a `ResponseTimeoutError`. |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
3147441
15
725
319
13
+ Addedextend@^2.0.0
+ Addedthrough2-sink@^1.0.0
+ Addedthrough2-spy@^1.2.0
+ Addedextend@2.0.2(transitive)
+ Addedisarray@0.0.1(transitive)
+ Addedreadable-stream@1.0.34(transitive)
+ Addedstring_decoder@0.10.31(transitive)
+ Addedthrough2@0.5.1(transitive)
+ Addedthrough2-sink@1.0.0(transitive)
+ Addedthrough2-spy@1.2.0(transitive)
+ Addedxtend@3.0.0(transitive)