Security News
RubyGems.org Adds New Maintainer Role
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
bandwidth-throttle-stream
Advanced tools
A Node.js and Deno transform stream for throttling bandwidth which distributes available bandwidth evenly between all requests in a "group", accurately simulating the effect of network conditions on simultaneous overlapping requests.
Firstly, install the package using your package manager of choice.
npm install bandwidth-throttle-stream
You may then import the createBandwidthThrottleGroup()
factory function into your project.
import {createBandwidthThrottleGroup} from 'bandwidth-throttle-stream';
In Deno, all libraries are imported from URLs as ES modules. Versioned releases of bandwidth-throttle-stream
are available from the Pika CDN:
import {createBandwidthThrottleGroup} from 'https://cdn.pika.dev/bandwidth-throttle-stream/mod.ts';
The above URL will return the latest release, but it is strongly advised to lock your import to a specific version using the following syntax, where the x.y.z
semver can be any published version of the library:
import {createBandwidthThrottleGroup} from 'https://cdn.pika.dev/bandwidth-throttle-stream@x.y.z/mod.ts';
Using the imported createBandwidthThrottleGroup
factory function, we must firstly create a "bandwidth throttle group" which will be configured with a specific throughput in bytes (B) per second.
// Create a group with a configured available bandwidth in bytes (B) per second.
const bandwidthThrottleGroup = createBandwidthThrottleGroup({
bytesPerSecond: 500000 // 500KB/s
});
Typically we would create a single group only for a server running a simulation, which all incoming network requests to be throttled are routed through. However, we could also create multiple groups if we wanted to run multiple simulations with different configurations on a single server.
Once we've created a group, we can then attach individual pipeable "throttles" to it, as requests come into our server.
The most simple integration would be to insert the throttle (via .pipe
, or .pipeThrough
) between a readable stream (e.g file system readout, server-side HTTP response), and the response stream of the incoming client request to be throttled.
// Attach a throttle to a group (e.g. in response to an incoming request)
const throttle = bandwidthThrottleGroup.createBandwidthThrottle(contentLength);
// Throttle the response by piping a `stream.Readable` to a `stream.Writable`
// via the throttle
someReadableStream
.pipe(throttle)
.pipe(someWritableStream);
// Attach a throttle to a group (e.g. in response to an incoming request)
const throttle = bandwidthThrottleGroup.createBandwidthThrottle(contentLength);
// Throttle the response by piping a `ReadableStream` to a `ReadableStreamDefaultReader`:
someReadableStream
.pipeThrough(throttle)
.getReader()
Note that a number value for contentLength
(in "bytes") must be passed when creating an individual throttle. This should be the total size of data for the request being passed through the throttle, and is used to allocate memory upfront in a single Uint8Array
typed array, thus preventing expensive GC calls as backpressure builds up. When throttling HTTP requests, contentLength
can be obtained from the 'content-length'
header, once the headers of the request have arrived:
content-length
from req
headers:const contentLength = parseInt(req.get('content-length'))
content-length
from fetch
headers:const { body, headers } = await fetch(destination);
const contentLength = parseInt(headers.get("content-length"));
In most cases however, we require more granular control of data output than simply piping to a writable stream (for example when throttling an HTTP request).
In these cases, we can use any of the Node.js stream events available such as data
and end
:
end
event of a writable streamrequest
.pipe(throttle)
.on('data', chunk => response.write(chunk)
.on('end', () => {
// Set the response status of the HTTP request to 200
response.status(200);
// End the request
response.end();
});
import {readerToDenoReader} from 'https://cdn.pika.dev/bandwidth-throttle-sream@^0.2.0/mod.ts';
...
await request.respond({
status: 200
body: readerToDenoReader(reader, contentLength),
});
// request sent successfully
Note that in the Deno example, a reader may be passed directly to request.respond()
allowing real-time streaming of the throttled output. However, the Deno std
server expects a Deno.Reader
as a body
(rather than the standard ReadableStreamDefaultReader
), meaning that conversion is needed between the two.
The readerToDenoReader
util is exposed for this purpose, and must be provided with both a reference to ReadableStreamDefaultReader
(reader
), and the contentLength
of the request.
Each bandwidth throttle group accepts an optional object of configuration options:
const bandwidthThrottleGroup = createBandwidthThrottleGroup({
bytesPerSecond: 500000 // 500KB/s,
ticksPerSecond: 20 // aim to write output 20x per second
});
The following options are available.
interface IConfig {
/**
* The maximum number of bytes allowed to pass through the
* throttle, each second.
*
* @default Infinity
*/
bytesPerSecond?: number;
/**
* Defines how frequently the processing of bytes should be
* distributed across each second. Each time the internal
* scheduler "ticks", data will be processed and written out.
*
* A higher value will ensure smoother throttling for requests
* that complete within a second, but will be more expensive
* computationally and will ultimately be constrained by the
* performance of the JavaScript runtime.
*
* @default 40
*/
ticksPerSecond?: number;
}
A group can be reconfigured at any point after creation via its .configure()
method, which accepts the same configuration interface as the createBandwidthThrottleGroup()
factory.
// Create a group with no throttling
const bandwidthThrottleGroup = createBandwidthThrottleGroup();
// ... after some configuration event:
bandwidthThrottleGroup.configure({
bytesPerSecond: 6000000
})
When a client aborts a requests, its important that we also abort the throttle, ensuring the group can re-balance available bandwidth correctly, and backpressure buffer memory is released.
const throttle = bandwidthThrottleGroup.createBandwidthThrottle(contentLength);
request.on('aborted', () => {
// Client aborted request
throttle.abort();
});
request
.pipe(throttle)
.pipe(response);
const throttle = bandwidthThrottleGroup.createBandwidthThrottle(contentLength);
request
.pipeThrough(throttle)
.getReader()
try {
await request.respond({
status: 200
body: readerToDenoReader(reader, contentLength),
});
} catch(err) {
// request aborted or failed
throttle.abort();
}
FAQs
A Node.js and Deno transform stream for throttling bandwidth
The npm package bandwidth-throttle-stream receives a total of 0 weekly downloads. As such, bandwidth-throttle-stream popularity was classified as not popular.
We found that bandwidth-throttle-stream 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
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
Security News
Node.js will be enforcing stricter semver-major PR policies a month before major releases to enhance stability and ensure reliable release candidates.
Security News
Research
Socket's threat research team has detected five malicious npm packages targeting Roblox developers, deploying malware to steal credentials and personal data.