
Security News
GitHub Actions Pricing Whiplash: Self-Hosted Actions Billing Change Postponed
GitHub postponed a new billing model for self-hosted Actions after developer pushback, but moved forward with hosted runner price cuts on January 1.
A sane HTTP client library for Node.js with Streams2 support.
There are already a few commonly used HTTP client libraries for Node.js, but all of them have issues:
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.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.hyperquest (mostly) solves the 'agent' problem correctly, but has a very spartan API. Making non-GET requests is more complex than it should be.All these issues (and more) are solved in bhttp. It offers the following:
multipart/form-data (eg. file uploads), with support for Streams2, and support for legacy streams.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.
WTFPL or CC0, whichever you prefer. A donation and/or attribution are appreciated, but not required.
My income consists entirely of donations for my projects. If this module is useful to you, consider making a donation!
You can donate using Bitcoin, PayPal, Gratipay, Flattr, cash-in-mail, SEPA transfers, and pretty much anything else.
Pull requests welcome. Please make sure your modifications are in line with the overall code style, and ensure that you're editing the src/ files, not the lib/ files.
Build tool of choice is babel; simply run npm run build while developing, and it will watch for changes.
Be aware that by making a pull request, you agree to release your modifications under the licenses stated above.
A simple example:
var Promise = require("bluebird");
var bhttp = require("bhttp");
Promise.try(function() {
return bhttp.get("http://icanhazip.com/");
}).then(function(response) {
console.log("Your IP is:", response.body.toString());
});
... or, using nodebacks:
var bhttp = require("bhttp");
bhttp.get("http://icanhazip.com/", {}, function(err, response) {
console.log("Your IP is:", response.body.toString());
});
Demonstrating both streaming responses and using a stream in form data for a request:
var Promise = require("bluebird");
var bhttp = require("bhttp");
Promise.try(function() {
return bhttp.get("http://somesite.com/bigfile.mp4", {stream: true});
}).then(function(response) {
return bhttp.post("http://somehostingservice.com/upload", {
fileOne: response,
fileTwo: fs.createReadStream("./otherbigfile.mkv")
});
}).then(function(response) {
console.log("Response from hosting service:", response.body.toString());
});
... or, using nodebacks:
var bhttp = require("bhttp");
bhttp.get("http://somesite.com/bigfile.mp4", {stream: true}, function(err, responseOne) {
var payload = {
fileOne: responseOne,
fileTwo: fs.createReadStream("./otherbigfile.mkv")
};
bhttp.post("http://somehostingservice.com/upload", payload, {}, function(err, responseTwo) {
console.log("Response from hosting service:", responseTwo.body.toString());
})
})
Upload progress events:
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:
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"));
});
var Promise = require("bluebird");
var bhttp = require("bhttp");
var session = bhttp.session({ headers: {"user-agent": "MyCustomUserAgent/2.0"} });
// Our new session now automatically has a cookie jar, and also uses our preset option(s).
Promise.try(function(){
return session.get("http://hypotheticalsite.com/cookietest"); // Assume that this site now sets a cookie
}).then(function(response){
return session.get("http://hypotheticalsite.com/other-endpoint"); // This now sends along the cookie!
});
The various error types are documented at the bottom of this README.
Convenience methods that pre-set the request method, and automatically send along the payload using the correct options for bhttp.request.
bhttp.request method below.The data payload can be one of the following things:
Further documentation for these methods, such as the response attributes, can be found in the below section for bhttp.request.
Makes a request, and returns the response object asynchronously. The response object is a standard http.IncomingMessages with a few additional properties (documented below the argument list).
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.
options: Optional. Extra options for the request. Any other options not listed here will be passed on directly to the http or https module.
false. Whether the response is meant to be streamed. If true, the response body won't be parsed, an unread response stream is returned, and the request is kept out of the 'agent' pool.true. Whether to automatically follow redirects or not (the redirect history is available as the redirectHistory property on the response).10. The maximum amount of redirects to follow before erroring out, to prevent redirect loops.false. Ensures that mulipart/form-data encoding is used, no matter what the payload contents are.false. When set to true, the request payload will be encoded as JSON. This cannot be used if you are using any streams in your payload.false. When set to true, the response will always be decoded as JSON, no matter what the Content-Type says. You'll probably want to keep this set to false - most APIs send the correct Content-Type headers, and in those cases bhttp will automatically decode the response as JSON.false. Never decode the response, even if the Content-Type says that it's JSON.multipart/form-data encoding.bhttp.session() instead.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.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.false. Whether to keep the response streams of redirects. You probably don't need this. When enabling this, you must explicitly read out every single redirect response, or you will experience memory leaks!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.(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.(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!callback: Optional. When using the nodeback API, the callback to use. If not specified, a Promise will be returned.
A few extra properties are set on the response object (which is a http.IncomingMessage):
stream is set to false (the default), this will contain the response body. This can be either a Buffer or, in the case of a JSON response, a decoded JSON object.keepRedirectResponses option.bhttp. You probably don't need this.bhttp. You probably don't need this.Additionally, there's an extra event on the response object:
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:
fs streamshttp and bhttp responsesrequest requestscombined-stream streams (assuming all the underlying streams are of one of the types listed here)If you are using a different type of stream, you can wrap the stream using bhttp.wrapStream to manually specify the needed metadata.
This will create a new session. The defaultOptions will be deep-merged with the options specified for each request (where the request-specific options have priority).
A new cookie jar is automatically created, unless you either specify a custom cookieJar option or set the cookieJar option to false (in which case no cookie jar is used).
This will return a 'stream wrapper' containing explicit metadata for a stream. You'll need to use it when passing an unsupported type of stream to a data parameter or formFields/files option.
The resulting wrapper can be passed on to the bhttp methods as if it were a regular stream.
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.
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.
The base class for all errors generated by bhttp. You usually don't need this.
You have specified two or more request options that cannot be used together.
The error message will contain more details.
You tried to load a URL that isn't using either the HTTP or HTTPS protocol. Only HTTP and HTTPS are currently supported.
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.
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.
The connection timed out.
The connection timeout is defined by the operating system, and cannot currently be overridden.
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.
FAQs
A sane HTTP client library for Node.js with Streams2 support.
The npm package bhttp receives a total of 9,708 weekly downloads. As such, bhttp popularity was classified as popular.
We found that bhttp demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
GitHub postponed a new billing model for self-hosted Actions after developer pushback, but moved forward with hosted runner price cuts on January 1.

Research
Destructive malware is rising across open source registries, using delays and kill switches to wipe code, break builds, and disrupt CI/CD.

Security News
Socket CTO Ahmad Nassri shares practical AI coding techniques, tools, and team workflows, plus what still feels noisy and why shipping remains human-led.