Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,15 @@ This is useful if you test a local web server or run [WebPageReplay](https://git
throttle --rtt 200 --localhost
```

On Linux you can also set a download limit, or use a profile:

```
throttle --rtt 200 --down 5000 --localhost
throttle --profile cable --localhost
```

The browser and the server both send on the loopback interface, so the limit is for all traffic on it. Requests and ACKs are small, so it works as a download limit. Upload limits are not supported on localhost, and a profile only uses its download and RTT values.

On Linux, throttle also sets the MTU of the loopback interface to 1500, the packet size of real networks. With the loopback default (65536), the receive window of the browser limits the transfer, and the number of round trips changes from run to run. Stop sets the MTU back to 65536. If you do this on a host and not in a container, all local traffic uses the smaller packets until you stop.

## Stop adding delay on localhost
Expand Down
10 changes: 8 additions & 2 deletions bin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,9 @@ async function run(argv) {
if (argv.profile in profiles || argv._[0] in profiles) {
options = profiles[argv.profile || argv._[0]];

if (argv.packetLoss) {
if (argv.localhost) {
options = { localhost: true, down: options.down, rtt: options.rtt };
} else if (argv.packetLoss) {
options.packetLoss = argv.packetLoss;
}

Expand All @@ -150,7 +152,11 @@ async function run(argv) {
try {
await start(options);
if (options.localhost) {
console.log(`Started throttler on localhost RTT:${options.rtt}ms `);
const down =
options.down === undefined ? '' : ` Down:${options.down}kbit/s`;
console.log(
`Started throttler on localhost RTT:${options.rtt}ms${down}`
);
} else {
let message = 'Started throttler:';
if (options.down !== undefined) {
Expand Down
11 changes: 10 additions & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ function verify(options) {
if (!Number.isInteger(options.rtt)) {
throw new TypeError('You need to set rtt as an integer for localhost');
}
if (options.down !== undefined && !Number.isInteger(options.down)) {
throw new TypeError('down needs to be an integer');
}
if (options.up !== undefined) {
throw new Error('Upload is not supported on localhost, use down');
}
} else if (
(options.up && !Number.isInteger(options.up)) ||
(options.down && !Number.isInteger(options.down)) ||
Expand All @@ -32,6 +38,9 @@ export async function start(options = {}) {
switch (platform()) {
case 'darwin': {
if (options.localhost) {
if (options.down !== undefined) {
throw new Error('Download on localhost is only supported on Linux');
}
return startPfctlLocalhost(options.rtt);
}
return startPfctl(
Expand All @@ -44,7 +53,7 @@ export async function start(options = {}) {

case 'linux': {
return options.localhost
? startTcLocalhost(options.rtt)
? startTcLocalhost(options.rtt, options.down)
: startTc(options.up, options.down, options.rtt, options.packetLoss);
}

Expand Down
8 changes: 6 additions & 2 deletions lib/localHostTc.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ import sudo from './sudo.js';
const NETWORK_MTU = '1500';
const LOOPBACK_DEFAULT_MTU = '65536';

export async function start(delay) {
export async function start(delay, down) {
const halfWayDelay = delay / 2;
// Both directions share loopback, so the rate is for all traffic. Requests
// and ACKs are small, so it works as a download limit.
const rate = down ? ['rate', `${down}kbit`] : [];

try {
await stop();
Expand All @@ -26,7 +29,8 @@ export async function start(delay) {
'1:0',
'netem',
'delay',
`${halfWayDelay}ms`
`${halfWayDelay}ms`,
...rate
);
await sudo('ip', 'link', 'set', 'dev', 'lo', 'mtu', NETWORK_MTU);
}
Expand Down
12 changes: 12 additions & 0 deletions test/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,15 @@ bin/index.js --rtt 200 --localhost
ip link show lo | grep -q 'mtu 1500'
bin/index.js --stop --localhost
ip link show lo | grep -q 'mtu 65536'

## Localhost with a download limit, from flags and from a profile
bin/index.js --rtt 200 --down 5000 --localhost
tc qdisc show dev lo | grep -q 'delay 100ms.*rate 5Mbit'
bin/index.js --stop --localhost
bin/index.js --profile cable --localhost
tc qdisc show dev lo | grep -q 'delay 7ms.*rate 5Mbit'
bin/index.js --stop --localhost
if tc qdisc show dev "$INTERFACE" | grep -q netem; then
echo "a profile with --localhost throttled $INTERFACE" >&2
exit 1
fi
Loading