Skip to content

Conversation

@fisherdarling
Copy link
Collaborator

@fisherdarling fisherdarling commented Nov 17, 2025

This PR adds a new command mach saturate <direction> which saturates the network in the given direction.

$ mach saturate --help
Saturate the network in some direction and report maximum goodput. The direction, `up`, `down`, `both` must be specified. By default, the command runs for 20s

Usage: mach saturate [OPTIONS] <COMMAND>

Commands:
  down  Saturate the download (ingress) side of the network
  up    Saturate the upload (egress) side of the network
  both  Saturate both the download (ingress) and upload (egress) side of the network
  help  Print this message or the help of the given subcommand(s)

Options:
  -t, --conn-type <CONN_TYPE>  The connection type to use [default: h1-clear-text] [possible values: h1-clear-text, h1, h2, h3]
  -v, --verbose...             Increase logging verbosity
  -q, --quiet...               Decrease logging verbosity
      --duration <DURATION>    The duration in seconds to saturate the network for. Defaults to 20s [default: 20]
  -h, --help                   Print help

When both is requested, the download and upload directions of the network are tested simultaneously.

The output is a simple JSON which describes the capacity of the network:

$ mach saturate --duration 2 both
2025-11-17T16:43:16.185042Z  INFO mach::saturate: running parallel download/upload (ingress/egress) network saturation test download_url=http://speed.cloudflare.com/__down?bytes=1000000000 upload_url=http://speed.cloudflare.com/__up
2025-11-17T16:43:17.363982Z ERROR nq_core::connection::http: error running h1 connection error=connection closed before message completed
{
  "download": {
    "capacity": 44377614,
    "dur": 2.001
  },
  "upload": {
    "capacity": 1572864,
    "dur": 2.001
  }
}

Implementation

The implementation is actually a small modification to the responsiveness (RPM) test. We simply add a flag to the RPM test which disables sending of the first probe. The RPM test will find the network capacity and break after the maximum duration has been reached.

Also, we add support for plaintext HTTP/1.1 connections to the given origin. This should remove any overhead from TLS and means the calculated goodput should be closer to the true bandwidth.

Finally, we modernize the repo a bit and fix a bug around upload body counting.

/// Saturate the upload (egress) side of the network.
Up {
/// The URL to upload data to.
#[clap(short, long, default_value = "http://speed.cloudflare.com/__up")]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested this out and was having issues withup reporting a capacity of 0. I was getting it with h1-clear-text (default) and h2:

[root@BenMTestSpeedtest networkquality-rs]# ./target/release/mach saturate --conn-type h2 up
2026-01-02T17:29:06.075011Z  INFO mach::saturate: running upload (egress) network saturation test upload_url=http://speed.cloudflare.com/__up
... (channel closed error a bunch of times)
2026-01-02T17:29:25.096166Z ERROR nq_rpm: error: channel closed
{
  "download": null,
  "upload": {
    "capacity": 0,
    "dur": 20.001
  }
}

I remembered this when I was testing my change before that the upload endpoint didn't seem to like cleartext.

I made a code change to try tweaking this to https:// and now I get good measurements.

[root@BenMTestSpeedtest networkquality-rs]# ./target/release/mach saturate up
2026-01-02T18:20:17.649918Z  INFO mach::saturate: running upload (egress) network saturation test upload_url=https://speed.cloudflare.com/__up
{
  "download": null,
  "upload": {
    "capacity": 11931746304,
    "dur": 20.001
  }
}

Based on a quick pcap, it does seem to follow the url (port 80 with http on download and port 443 with https on upload). Both with default conn-type which should be h1-clear-text.

Do you know why the upload endpoint seems to not like http?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants