Skip to main content

SpeedTestServer

SpeedTestServer is the chain-end peer for SpeedTestClient. It accepts speed-test frames from upstream, validates upload data, optionally sends download data back downstream, and returns final sender/receiver reports to the client.

It is a normal connection-oriented tunnel. It does not create lines, it does not own listener-created line destruction, and it is not a packet-line tunnel.

Typical placement

SpeedTestServer must be the last node in its chain.

TcpListener -> SpeedTestServer
UdpListener -> SpeedTestServer
TcpListener -> TlsServer -> SpeedTestServer
TcpListener -> EncryptionServer -> SpeedTestServer

The matching client side normally starts with SpeedTestClient:

SpeedTestClient -> TcpConnector
SpeedTestClient -> UdpConnector
SpeedTestClient -> TlsClient -> TcpConnector
SpeedTestClient -> EncryptionClient -> TcpConnector

TCP example

Server side:

[
{
"name": "speedtest-listener",
"type": "TcpListener",
"settings": {
"address": "0.0.0.0",
"port": 9000,
"nodelay": true
},
"next": "speedtest-server"
},
{
"name": "speedtest-server",
"type": "SpeedTestServer",
"settings": {
"report-interval-ms": 1000,
"json-summary": true,
"quiet": false
}
}
]

Matching client side:

[
{
"name": "speedtest-client",
"type": "SpeedTestClient",
"settings": {
"mode": "tcp",
"direction": "bidirectional",
"duration-ms": 10000,
"warmup-ms": 1000,
"connection-count": 4,
"payload-size": 131072
},
"next": "speedtest-out"
},
{
"name": "speedtest-out",
"type": "TcpConnector",
"settings": {
"address": "198.51.100.10",
"port": 9000,
"nodelay": true
}
}
]

UDP example

Server side:

[
{
"name": "speedtest-udp-listener",
"type": "UdpListener",
"settings": {
"address": "0.0.0.0",
"port": 9001
},
"next": "speedtest-server"
},
{
"name": "speedtest-server",
"type": "SpeedTestServer",
"settings": {
"report-interval-ms": 1000
}
}
]

Matching client side:

[
{
"name": "speedtest-client",
"type": "SpeedTestClient",
"settings": {
"mode": "udp",
"direction": "upload",
"duration-ms": 10000,
"warmup-ms": 1000,
"payload-size": 3800,
"udp-target-bits-per-sec": 10000000
},
"next": "speedtest-udp-out"
},
{
"name": "speedtest-udp-out",
"type": "UdpConnector",
"settings": {
"address": "198.51.100.10",
"port": 9001
}
}
]

Required fields

SpeedTestServer is a chain end. It must not have a next node.

settings is optional. If it is absent or empty, defaults are used.

Optional settings

FieldDefaultDescription
report-interval-ms1000Fallback interval log cadence before a client HELLO is received. After HELLO, the client's requested interval is used for that line. Must be positive.
json-summaryfalseDefault JSON-style per-stream summary setting. A client HELLO can also request JSON summaries per line.
quietfalseSuppresses runtime logs such as accepted-stream, interval, final summary, and per-stream failure messages. Fatal setup diagnostics may still appear.

Init and establishment

On upstream Init, the server:

  1. initializes per-line state
  2. creates a receive stream buffer for TCP-mode framing
  3. immediately sends downstream Est toward the previous tunnel

That downstream Est is the signal that lets SpeedTestClient start sending its HELLO.

HELLO and ACK

The first client frame must be HELLO. It configures the server's per-line behavior:

  • tcp or udp mode
  • upload, download, or bidirectional direction
  • duration and warmup
  • report interval
  • payload size
  • target bandwidth
  • total stream count
  • stream id

After accepting HELLO, the server sends ACK downstream. In UDP mode, duplicate HELLO frames receive another ACK so a lost ACK does not leave the client waiting forever.

The server validates that duration-ms, report-interval-ms, payload-size, and total stream count are usable. UDP payloads must fit the UDP maximum of 64952 payload bytes.

Upload receive path

When upload is enabled, upstream DATA frames are validated using the deterministic pattern generated by SpeedTestClient.

The server tracks:

  • bytes
  • packets
  • valid packets
  • lost packets
  • duplicate packets
  • out-of-order packets
  • validation errors
  • jitter estimate

When the client sends upload END, the server finalizes receiver statistics and sends a receiver REPORT downstream. In UDP mode, duplicate final END frames are ignored after the receiver has already finished.

Download send path

When download is enabled, the server schedules downstream DATA frames after HELLO is accepted. It uses the payload size, warmup, duration, and optional target bandwidth requested by the client.

At the end of the download interval, the server sends:

  • download END
  • sender REPORT

In UDP mode, final END and REPORT frames are repeated three times.

Stream and datagram framing

In TCP mode, incoming bytes are accumulated in a buffer_stream_t and decoded as speed-test frames.

In UDP mode, each Waterwall payload buffer must contain exactly one speed-test frame. A first packet carrying a UDP flagged HELLO switches that line into UDP-frame handling.

Malformed frame headers, unexpected frame types, impossible payload lengths, or invalid protocol order fail the line. When possible, the server sends an ERROR frame before closing.

Finish behavior

If upstream closes before a complete test, the server destroys only its own per-line state. It does not call lineDestroy(), because that line was created by the listener or another upstream adapter.

When the server completes or fails a test itself, it sends any required final protocol bytes, destroys its own line state, and forwards downstream Finish.

Node metadata

PropertyValue
Node flagkNodeFlagChainEnd
Previous nodeRequired
Next nodeNot allowed
Layer groupkNodeLayer4
required_padding_left0

Common mistakes

  • Do not place any node after SpeedTestServer; it is the chain end.
  • Do not use it without a matching SpeedTestClient.
  • Do not expect it to accept arbitrary application traffic.
  • Do not treat it as a packet-line node. It works with normal connection lines created by listeners or upstream adapters.