Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b508a94198 | ||
|
|
4ed09e2209 | ||
|
|
e2a9d68843 | ||
|
|
c04e8f7084 | ||
|
|
323e5f7f3d | ||
|
|
7ad83ca1f6 | ||
|
|
8ef53c999a | ||
|
|
21bc7805f7 | ||
|
|
8d1d0f50de | ||
|
|
d6c6e60742 | ||
|
|
1cdd100473 | ||
|
|
80fda92231 | ||
|
|
5f6bff5dcb | ||
|
|
1a04f44bb5 | ||
|
|
d8b580a19c | ||
|
|
023e85741a |
No files matched your search
@@ -13,24 +13,25 @@ name: OSV-Scanner
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "main" ]
|
branches: ["main"]
|
||||||
merge_group:
|
merge_group:
|
||||||
branches: [ "main" ]
|
branches: ["main"]
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '45 8 * * 1'
|
- cron: "45 8 * * 1"
|
||||||
push:
|
push:
|
||||||
branches: [ "main" ]
|
branches: ["main"]
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
# Require writing security events to upload SARIF file to security tab
|
# Require writing security events to upload SARIF file to security tab
|
||||||
security-events: write
|
security-events: write
|
||||||
# Read commit contents
|
# Read commit contents
|
||||||
contents: read
|
contents: read
|
||||||
|
actions: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
scan-scheduled:
|
scan-scheduled:
|
||||||
if: ${{ github.event_name == 'push' || github.event_name == 'schedule' }}
|
if: ${{ github.event_name == 'push' || github.event_name == 'schedule' }}
|
||||||
uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@c51854704019a247608d928f370c98740469d4b5" # v2.3.5
|
uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@9a498708959aeaef5ef730655706c5a1df1edbc2" # v2.3.8
|
||||||
with:
|
with:
|
||||||
# Example of specifying custom arguments
|
# Example of specifying custom arguments
|
||||||
scan-args: |-
|
scan-args: |-
|
||||||
@@ -39,7 +40,7 @@ jobs:
|
|||||||
./
|
./
|
||||||
scan-pr:
|
scan-pr:
|
||||||
if: ${{ github.event_name == 'pull_request' || github.event_name == 'merge_group' }}
|
if: ${{ github.event_name == 'pull_request' || github.event_name == 'merge_group' }}
|
||||||
uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable-pr.yml@c51854704019a247608d928f370c98740469d4b5" # v2.3.5
|
uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable-pr.yml@9a498708959aeaef5ef730655706c5a1df1edbc2" # v2.3.8
|
||||||
with:
|
with:
|
||||||
# Example of specifying custom arguments
|
# Example of specifying custom arguments
|
||||||
scan-args: |-
|
scan-args: |-
|
||||||
|
|||||||
@@ -0,0 +1,296 @@
|
|||||||
|
# This file was autogenerated by dist: https://axodotdev.github.io/cargo-dist
|
||||||
|
#
|
||||||
|
# Copyright 2022-2024, axodotdev
|
||||||
|
# SPDX-License-Identifier: MIT or Apache-2.0
|
||||||
|
#
|
||||||
|
# CI that:
|
||||||
|
#
|
||||||
|
# * checks for a Git Tag that looks like a release
|
||||||
|
# * builds artifacts with dist (archives, installers, hashes)
|
||||||
|
# * uploads those artifacts to temporary workflow zip
|
||||||
|
# * on success, uploads the artifacts to a GitHub Release
|
||||||
|
#
|
||||||
|
# Note that the GitHub Release will be created with a generated
|
||||||
|
# title/body based on your changelogs.
|
||||||
|
|
||||||
|
name: Release
|
||||||
|
permissions:
|
||||||
|
"contents": "write"
|
||||||
|
|
||||||
|
# This task will run whenever you push a git tag that looks like a version
|
||||||
|
# like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc.
|
||||||
|
# Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where
|
||||||
|
# PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION
|
||||||
|
# must be a Cargo-style SemVer Version (must have at least major.minor.patch).
|
||||||
|
#
|
||||||
|
# If PACKAGE_NAME is specified, then the announcement will be for that
|
||||||
|
# package (erroring out if it doesn't have the given version or isn't dist-able).
|
||||||
|
#
|
||||||
|
# If PACKAGE_NAME isn't specified, then the announcement will be for all
|
||||||
|
# (dist-able) packages in the workspace with that version (this mode is
|
||||||
|
# intended for workspaces with only one dist-able package, or with all dist-able
|
||||||
|
# packages versioned/released in lockstep).
|
||||||
|
#
|
||||||
|
# If you push multiple tags at once, separate instances of this workflow will
|
||||||
|
# spin up, creating an independent announcement for each one. However, GitHub
|
||||||
|
# will hard limit this to 3 tags per commit, as it will assume more tags is a
|
||||||
|
# mistake.
|
||||||
|
#
|
||||||
|
# If there's a prerelease-style suffix to the version, then the release(s)
|
||||||
|
# will be marked as a prerelease.
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '**[0-9]+.[0-9]+.[0-9]+*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Run 'dist plan' (or host) to determine what tasks we need to do
|
||||||
|
plan:
|
||||||
|
runs-on: "ubuntu-22.04"
|
||||||
|
outputs:
|
||||||
|
val: ${{ steps.plan.outputs.manifest }}
|
||||||
|
tag: ${{ !github.event.pull_request && github.ref_name || '' }}
|
||||||
|
tag-flag: ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }}
|
||||||
|
publishing: ${{ !github.event.pull_request }}
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
submodules: recursive
|
||||||
|
- name: Install dist
|
||||||
|
# we specify bash to get pipefail; it guards against the `curl` command
|
||||||
|
# failing. otherwise `sh` won't catch that `curl` returned non-0
|
||||||
|
shell: bash
|
||||||
|
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.31.0/cargo-dist-installer.sh | sh"
|
||||||
|
- name: Cache dist
|
||||||
|
uses: actions/upload-artifact@v6
|
||||||
|
with:
|
||||||
|
name: cargo-dist-cache
|
||||||
|
path: ~/.cargo/bin/dist
|
||||||
|
# sure would be cool if github gave us proper conditionals...
|
||||||
|
# so here's a doubly-nested ternary-via-truthiness to try to provide the best possible
|
||||||
|
# functionality based on whether this is a pull_request, and whether it's from a fork.
|
||||||
|
# (PRs run on the *source* but secrets are usually on the *target* -- that's *good*
|
||||||
|
# but also really annoying to build CI around when it needs secrets to work right.)
|
||||||
|
- id: plan
|
||||||
|
run: |
|
||||||
|
dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json
|
||||||
|
echo "dist ran successfully"
|
||||||
|
cat plan-dist-manifest.json
|
||||||
|
echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT"
|
||||||
|
- name: "Upload dist-manifest.json"
|
||||||
|
uses: actions/upload-artifact@v6
|
||||||
|
with:
|
||||||
|
name: artifacts-plan-dist-manifest
|
||||||
|
path: plan-dist-manifest.json
|
||||||
|
|
||||||
|
# Build and packages all the platform-specific things
|
||||||
|
build-local-artifacts:
|
||||||
|
name: build-local-artifacts (${{ join(matrix.targets, ', ') }})
|
||||||
|
# Let the initial task tell us to not run (currently very blunt)
|
||||||
|
needs:
|
||||||
|
- plan
|
||||||
|
if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
# Target platforms/runners are computed by dist in create-release.
|
||||||
|
# Each member of the matrix has the following arguments:
|
||||||
|
#
|
||||||
|
# - runner: the github runner
|
||||||
|
# - dist-args: cli flags to pass to dist
|
||||||
|
# - install-dist: expression to run to install dist on the runner
|
||||||
|
#
|
||||||
|
# Typically there will be:
|
||||||
|
# - 1 "global" task that builds universal installers
|
||||||
|
# - N "local" tasks that build each platform's binaries and platform-specific installers
|
||||||
|
matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }}
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
|
container: ${{ matrix.container && matrix.container.image || null }}
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json
|
||||||
|
steps:
|
||||||
|
- name: enable windows longpaths
|
||||||
|
run: |
|
||||||
|
git config --global core.longpaths true
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
submodules: recursive
|
||||||
|
- name: Install Rust non-interactively if not already installed
|
||||||
|
if: ${{ matrix.container }}
|
||||||
|
run: |
|
||||||
|
if ! command -v cargo > /dev/null 2>&1; then
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||||
|
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
|
||||||
|
fi
|
||||||
|
- name: Install dist
|
||||||
|
run: ${{ matrix.install_dist.run }}
|
||||||
|
# Get the dist-manifest
|
||||||
|
- name: Fetch local artifacts
|
||||||
|
uses: actions/download-artifact@v7
|
||||||
|
with:
|
||||||
|
pattern: artifacts-*
|
||||||
|
path: target/distrib/
|
||||||
|
merge-multiple: true
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
${{ matrix.packages_install }}
|
||||||
|
- name: Build artifacts
|
||||||
|
run: |
|
||||||
|
# Actually do builds and make zips and whatnot
|
||||||
|
dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json
|
||||||
|
echo "dist ran successfully"
|
||||||
|
- id: cargo-dist
|
||||||
|
name: Post-build
|
||||||
|
# We force bash here just because github makes it really hard to get values up
|
||||||
|
# to "real" actions without writing to env-vars, and writing to env-vars has
|
||||||
|
# inconsistent syntax between shell and powershell.
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
# Parse out what we just built and upload it to scratch storage
|
||||||
|
echo "paths<<EOF" >> "$GITHUB_OUTPUT"
|
||||||
|
dist print-upload-files-from-manifest --manifest dist-manifest.json >> "$GITHUB_OUTPUT"
|
||||||
|
echo "EOF" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
|
||||||
|
- name: "Upload artifacts"
|
||||||
|
uses: actions/upload-artifact@v6
|
||||||
|
with:
|
||||||
|
name: artifacts-build-local-${{ join(matrix.targets, '_') }}
|
||||||
|
path: |
|
||||||
|
${{ steps.cargo-dist.outputs.paths }}
|
||||||
|
${{ env.BUILD_MANIFEST_NAME }}
|
||||||
|
|
||||||
|
# Build and package all the platform-agnostic(ish) things
|
||||||
|
build-global-artifacts:
|
||||||
|
needs:
|
||||||
|
- plan
|
||||||
|
- build-local-artifacts
|
||||||
|
runs-on: "ubuntu-22.04"
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
submodules: recursive
|
||||||
|
- name: Install cached dist
|
||||||
|
uses: actions/download-artifact@v7
|
||||||
|
with:
|
||||||
|
name: cargo-dist-cache
|
||||||
|
path: ~/.cargo/bin/
|
||||||
|
- run: chmod +x ~/.cargo/bin/dist
|
||||||
|
# Get all the local artifacts for the global tasks to use (for e.g. checksums)
|
||||||
|
- name: Fetch local artifacts
|
||||||
|
uses: actions/download-artifact@v7
|
||||||
|
with:
|
||||||
|
pattern: artifacts-*
|
||||||
|
path: target/distrib/
|
||||||
|
merge-multiple: true
|
||||||
|
- id: cargo-dist
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json
|
||||||
|
echo "dist ran successfully"
|
||||||
|
|
||||||
|
# Parse out what we just built and upload it to scratch storage
|
||||||
|
echo "paths<<EOF" >> "$GITHUB_OUTPUT"
|
||||||
|
jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT"
|
||||||
|
echo "EOF" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
|
||||||
|
- name: "Upload artifacts"
|
||||||
|
uses: actions/upload-artifact@v6
|
||||||
|
with:
|
||||||
|
name: artifacts-build-global
|
||||||
|
path: |
|
||||||
|
${{ steps.cargo-dist.outputs.paths }}
|
||||||
|
${{ env.BUILD_MANIFEST_NAME }}
|
||||||
|
# Determines if we should publish/announce
|
||||||
|
host:
|
||||||
|
needs:
|
||||||
|
- plan
|
||||||
|
- build-local-artifacts
|
||||||
|
- build-global-artifacts
|
||||||
|
# Only run if we're "publishing", and only if plan, local and global didn't fail (skipped is fine)
|
||||||
|
if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }}
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
runs-on: "ubuntu-22.04"
|
||||||
|
outputs:
|
||||||
|
val: ${{ steps.host.outputs.manifest }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
submodules: recursive
|
||||||
|
- name: Install cached dist
|
||||||
|
uses: actions/download-artifact@v7
|
||||||
|
with:
|
||||||
|
name: cargo-dist-cache
|
||||||
|
path: ~/.cargo/bin/
|
||||||
|
- run: chmod +x ~/.cargo/bin/dist
|
||||||
|
# Fetch artifacts from scratch-storage
|
||||||
|
- name: Fetch artifacts
|
||||||
|
uses: actions/download-artifact@v7
|
||||||
|
with:
|
||||||
|
pattern: artifacts-*
|
||||||
|
path: target/distrib/
|
||||||
|
merge-multiple: true
|
||||||
|
- id: host
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json
|
||||||
|
echo "artifacts uploaded and released successfully"
|
||||||
|
cat dist-manifest.json
|
||||||
|
echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT"
|
||||||
|
- name: "Upload dist-manifest.json"
|
||||||
|
uses: actions/upload-artifact@v6
|
||||||
|
with:
|
||||||
|
# Overwrite the previous copy
|
||||||
|
name: artifacts-dist-manifest
|
||||||
|
path: dist-manifest.json
|
||||||
|
# Create a GitHub Release while uploading all files to it
|
||||||
|
- name: "Download GitHub Artifacts"
|
||||||
|
uses: actions/download-artifact@v7
|
||||||
|
with:
|
||||||
|
pattern: artifacts-*
|
||||||
|
path: artifacts
|
||||||
|
merge-multiple: true
|
||||||
|
- name: Cleanup
|
||||||
|
run: |
|
||||||
|
# Remove the granular manifests
|
||||||
|
rm -f artifacts/*-dist-manifest.json
|
||||||
|
- name: Create GitHub Release
|
||||||
|
env:
|
||||||
|
PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}"
|
||||||
|
ANNOUNCEMENT_TITLE: "${{ fromJson(steps.host.outputs.manifest).announcement_title }}"
|
||||||
|
ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}"
|
||||||
|
RELEASE_COMMIT: "${{ github.sha }}"
|
||||||
|
run: |
|
||||||
|
# Write and read notes from a file to avoid quoting breaking things
|
||||||
|
echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt
|
||||||
|
|
||||||
|
gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/*
|
||||||
|
|
||||||
|
announce:
|
||||||
|
needs:
|
||||||
|
- plan
|
||||||
|
- host
|
||||||
|
# use "always() && ..." to allow us to wait for all publish jobs while
|
||||||
|
# still allowing individual publish jobs to skip themselves (for prereleases).
|
||||||
|
# "host" however must run to completion, no skipping allowed!
|
||||||
|
if: ${{ always() && needs.host.result == 'success' }}
|
||||||
|
runs-on: "ubuntu-22.04"
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
submodules: recursive
|
||||||
@@ -43,7 +43,7 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Run cargo-deny
|
- name: Run cargo-deny
|
||||||
uses: EmbarkStudios/cargo-deny-action@91bf2b620e09e18d6eb78b92e7861937469acedb # v2
|
uses: EmbarkStudios/cargo-deny-action@6c8f9facfa5047ec02d8485b6bf52b587b7777d1 # v2
|
||||||
with:
|
with:
|
||||||
command: check
|
command: check
|
||||||
arguments: --all-features
|
arguments: --all-features
|
||||||
|
|||||||
Generated
+6
-6
@@ -225,7 +225,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "irtt-cli"
|
name = "irtt-cli"
|
||||||
version = "0.1.1"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"ctrlc",
|
"ctrlc",
|
||||||
@@ -236,7 +236,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "irtt-client"
|
name = "irtt-client"
|
||||||
version = "0.1.1"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"irtt-proto",
|
"irtt-proto",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -246,7 +246,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "irtt-proto"
|
name = "irtt-proto"
|
||||||
version = "0.1.1"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hmac",
|
"hmac",
|
||||||
"md-5",
|
"md-5",
|
||||||
@@ -256,7 +256,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "irtt-stats"
|
name = "irtt-stats"
|
||||||
version = "0.1.1"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"irtt-client",
|
"irtt-client",
|
||||||
]
|
]
|
||||||
@@ -285,9 +285,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nix"
|
name = "nix"
|
||||||
version = "0.31.2"
|
version = "0.31.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3"
|
checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
|||||||
+15
-3
@@ -16,6 +16,18 @@ homepage = "https://github.com/Lochnair/irtt-rs"
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
irtt-proto = { version = "0.1.1", path = "crates/irtt-proto" }
|
irtt-proto = { version = "0.2.0", path = "crates/irtt-proto" }
|
||||||
irtt-client = { version = "0.1.1", path = "crates/irtt-client" }
|
irtt-client = { version = "0.2.0", path = "crates/irtt-client" }
|
||||||
irtt-stats = { version = "0.1.1", path = "crates/irtt-stats" }
|
irtt-stats = { version = "0.2.0", path = "crates/irtt-stats" }
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
opt-level = "z"
|
||||||
|
lto = true
|
||||||
|
codegen-units = 1
|
||||||
|
panic = "abort"
|
||||||
|
strip = "symbols"
|
||||||
|
|
||||||
|
# The profile that 'dist' will build with
|
||||||
|
[profile.dist]
|
||||||
|
inherits = "release"
|
||||||
|
lto = "thin"
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "irtt-cli"
|
name = "irtt-cli"
|
||||||
version = "0.1.1"
|
version = "0.2.0"
|
||||||
description = "Command-line IRTT-compatible client"
|
description = "Command-line IRTT-compatible client"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|||||||
+37
-79
@@ -326,8 +326,8 @@ pub struct HumanEventStats {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct HumanIpdvPair {
|
pub struct HumanIpdvPair {
|
||||||
pub previous_logical_seq: u64,
|
pub previous_seq: u32,
|
||||||
pub current_logical_seq: u64,
|
pub current_seq: u32,
|
||||||
pub rtt_ipdv: Duration,
|
pub rtt_ipdv: Duration,
|
||||||
pub send_ipdv: Option<Duration>,
|
pub send_ipdv: Option<Duration>,
|
||||||
pub receive_ipdv: Option<Duration>,
|
pub receive_ipdv: Option<Duration>,
|
||||||
@@ -352,8 +352,8 @@ impl From<irtt_stats::EventStatsUpdate> for HumanEventStats {
|
|||||||
impl From<irtt_stats::IpdvPairUpdate> for HumanIpdvPair {
|
impl From<irtt_stats::IpdvPairUpdate> for HumanIpdvPair {
|
||||||
fn from(value: irtt_stats::IpdvPairUpdate) -> Self {
|
fn from(value: irtt_stats::IpdvPairUpdate) -> Self {
|
||||||
Self {
|
Self {
|
||||||
previous_logical_seq: value.previous_logical_seq,
|
previous_seq: value.previous_seq,
|
||||||
current_logical_seq: value.current_logical_seq,
|
current_seq: value.current_seq,
|
||||||
rtt_ipdv: value.rtt_ipdv,
|
rtt_ipdv: value.rtt_ipdv,
|
||||||
send_ipdv: value.send_ipdv,
|
send_ipdv: value.send_ipdv,
|
||||||
receive_ipdv: value.receive_ipdv,
|
receive_ipdv: value.receive_ipdv,
|
||||||
@@ -385,7 +385,6 @@ pub fn format_human_event_with_options(
|
|||||||
ClientEvent::EchoSent { .. } => String::new(),
|
ClientEvent::EchoSent { .. } => String::new(),
|
||||||
ClientEvent::EchoReply {
|
ClientEvent::EchoReply {
|
||||||
seq,
|
seq,
|
||||||
logical_seq,
|
|
||||||
rtt,
|
rtt,
|
||||||
server_timing,
|
server_timing,
|
||||||
one_way,
|
one_way,
|
||||||
@@ -393,17 +392,9 @@ pub fn format_human_event_with_options(
|
|||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let mut out = format!("seq={seq}");
|
let mut out = format!("seq={seq}");
|
||||||
if options.verbose {
|
|
||||||
write!(out, " logical_seq={logical_seq}").unwrap();
|
|
||||||
}
|
|
||||||
write!(out, " rtt={}", format_duration(rtt.effective)).unwrap();
|
write!(out, " rtt={}", format_duration(rtt.effective)).unwrap();
|
||||||
write_human_one_way(&mut out, *one_way);
|
write_human_one_way(&mut out, *one_way);
|
||||||
write!(
|
write!(out, " ipdv={}", format_human_ipdv(stats, *seq)).unwrap();
|
||||||
out,
|
|
||||||
" ipdv={}",
|
|
||||||
format_human_ipdv(stats, Some(*logical_seq))
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
if let Some(processing) = server_timing.and_then(|timing| timing.processing) {
|
if let Some(processing) = server_timing.and_then(|timing| timing.processing) {
|
||||||
write!(out, " proc={}", format_duration(processing)).unwrap();
|
write!(out, " proc={}", format_duration(processing)).unwrap();
|
||||||
}
|
}
|
||||||
@@ -412,17 +403,14 @@ pub fn format_human_event_with_options(
|
|||||||
}
|
}
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
ClientEvent::EchoLoss {
|
ClientEvent::EchoLoss { seq, .. } => {
|
||||||
seq, logical_seq, ..
|
format!("loss seq={seq}")
|
||||||
} => {
|
|
||||||
format!("loss seq={seq} logical_seq={logical_seq}")
|
|
||||||
}
|
}
|
||||||
ClientEvent::DuplicateReply { seq, remote, .. } => {
|
ClientEvent::DuplicateReply { seq, remote, .. } => {
|
||||||
format!("duplicate seq={seq} remote={remote}")
|
format!("duplicate seq={seq} remote={remote}")
|
||||||
}
|
}
|
||||||
ClientEvent::LateReply {
|
ClientEvent::LateReply {
|
||||||
seq,
|
seq,
|
||||||
logical_seq,
|
|
||||||
highest_seen,
|
highest_seen,
|
||||||
remote,
|
remote,
|
||||||
rtt,
|
rtt,
|
||||||
@@ -430,19 +418,16 @@ pub fn format_human_event_with_options(
|
|||||||
received_stats,
|
received_stats,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let mut out = format!(
|
let mut out = format!("late seq={seq} highest_seen={highest_seen} remote={remote}",);
|
||||||
"late seq={seq} logical_seq={} highest_seen={highest_seen} remote={remote}",
|
|
||||||
optional_u64(*logical_seq)
|
|
||||||
);
|
|
||||||
if let Some(rtt) = rtt {
|
if let Some(rtt) = rtt {
|
||||||
write!(out, " rtt={}", format_duration(rtt.effective)).unwrap();
|
write!(out, " rtt={}", format_duration(rtt.effective)).unwrap();
|
||||||
write_human_one_way(&mut out, *one_way);
|
write_human_one_way(&mut out, *one_way);
|
||||||
write!(out, " ipdv={}", format_human_ipdv(stats, *logical_seq)).unwrap();
|
write!(out, " ipdv={}", format_human_ipdv(stats, *seq)).unwrap();
|
||||||
}
|
}
|
||||||
write_human_received_stats(&mut out, *received_stats);
|
write_human_received_stats(&mut out, *received_stats);
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
ClientEvent::Warning { kind, message } => {
|
ClientEvent::Warning { kind, message, .. } => {
|
||||||
format!("warning kind={} message={message}", warning_kind(*kind))
|
format!("warning kind={} message={message}", warning_kind(*kind))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -489,7 +474,6 @@ fn format_machine(event: &ClientEvent) -> String {
|
|||||||
ClientEvent::EchoSent { .. } => {}
|
ClientEvent::EchoSent { .. } => {}
|
||||||
ClientEvent::EchoReply {
|
ClientEvent::EchoReply {
|
||||||
seq,
|
seq,
|
||||||
logical_seq,
|
|
||||||
remote,
|
remote,
|
||||||
sent_at,
|
sent_at,
|
||||||
received_at,
|
received_at,
|
||||||
@@ -501,7 +485,7 @@ fn format_machine(event: &ClientEvent) -> String {
|
|||||||
packet_meta,
|
packet_meta,
|
||||||
} => {
|
} => {
|
||||||
write_common(&mut out, "echo_reply");
|
write_common(&mut out, "echo_reply");
|
||||||
write_seq(&mut out, *seq, Some(*logical_seq));
|
write_seq(&mut out, *seq);
|
||||||
write_remote(&mut out, *remote);
|
write_remote(&mut out, *remote);
|
||||||
write_wall(&mut out, "client_send_wall_ns", sent_at.wall);
|
write_wall(&mut out, "client_send_wall_ns", sent_at.wall);
|
||||||
write_wall(&mut out, "client_receive_wall_ns", received_at.wall);
|
write_wall(&mut out, "client_receive_wall_ns", received_at.wall);
|
||||||
@@ -511,14 +495,9 @@ fn format_machine(event: &ClientEvent) -> String {
|
|||||||
write_received_stats(&mut out, *received_stats);
|
write_received_stats(&mut out, *received_stats);
|
||||||
write_packet_meta(&mut out, *packet_meta);
|
write_packet_meta(&mut out, *packet_meta);
|
||||||
}
|
}
|
||||||
ClientEvent::EchoLoss {
|
ClientEvent::EchoLoss { seq, sent_at, .. } => {
|
||||||
seq,
|
|
||||||
logical_seq,
|
|
||||||
sent_at,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
write_common(&mut out, "loss");
|
write_common(&mut out, "loss");
|
||||||
write_seq(&mut out, *seq, Some(*logical_seq));
|
write_seq(&mut out, *seq);
|
||||||
write_wall(&mut out, "client_send_wall_ns", sent_at.wall);
|
write_wall(&mut out, "client_send_wall_ns", sent_at.wall);
|
||||||
out.push_str(" warning=loss");
|
out.push_str(" warning=loss");
|
||||||
}
|
}
|
||||||
@@ -529,14 +508,13 @@ fn format_machine(event: &ClientEvent) -> String {
|
|||||||
bytes: _,
|
bytes: _,
|
||||||
} => {
|
} => {
|
||||||
write_common(&mut out, "duplicate");
|
write_common(&mut out, "duplicate");
|
||||||
write_seq(&mut out, *seq, None);
|
write_seq(&mut out, *seq);
|
||||||
write_remote(&mut out, *remote);
|
write_remote(&mut out, *remote);
|
||||||
write_wall(&mut out, "client_receive_wall_ns", received_at.wall);
|
write_wall(&mut out, "client_receive_wall_ns", received_at.wall);
|
||||||
out.push_str(" warning=duplicate");
|
out.push_str(" warning=duplicate");
|
||||||
}
|
}
|
||||||
ClientEvent::LateReply {
|
ClientEvent::LateReply {
|
||||||
seq,
|
seq,
|
||||||
logical_seq,
|
|
||||||
highest_seen,
|
highest_seen,
|
||||||
remote,
|
remote,
|
||||||
sent_at,
|
sent_at,
|
||||||
@@ -549,7 +527,7 @@ fn format_machine(event: &ClientEvent) -> String {
|
|||||||
packet_meta,
|
packet_meta,
|
||||||
} => {
|
} => {
|
||||||
write_common(&mut out, "late");
|
write_common(&mut out, "late");
|
||||||
write_seq(&mut out, *seq, *logical_seq);
|
write_seq(&mut out, *seq);
|
||||||
write_remote(&mut out, *remote);
|
write_remote(&mut out, *remote);
|
||||||
write!(out, " highest_seen={highest_seen}").unwrap();
|
write!(out, " highest_seen={highest_seen}").unwrap();
|
||||||
if let Some(sent_at) = sent_at {
|
if let Some(sent_at) = sent_at {
|
||||||
@@ -565,8 +543,9 @@ fn format_machine(event: &ClientEvent) -> String {
|
|||||||
write_packet_meta(&mut out, *packet_meta);
|
write_packet_meta(&mut out, *packet_meta);
|
||||||
out.push_str(" warning=late");
|
out.push_str(" warning=late");
|
||||||
}
|
}
|
||||||
ClientEvent::Warning { kind, message } => {
|
ClientEvent::Warning { kind, message, at } => {
|
||||||
write_common(&mut out, "warning");
|
write_common(&mut out, "warning");
|
||||||
|
write_wall(&mut out, "at", at.wall);
|
||||||
write!(
|
write!(
|
||||||
out,
|
out,
|
||||||
" warning_kind={} message={}",
|
" warning_kind={} message={}",
|
||||||
@@ -593,7 +572,6 @@ fn format_simple(event: &ClientEvent) -> String {
|
|||||||
ClientEvent::EchoSent { .. } => String::new(),
|
ClientEvent::EchoSent { .. } => String::new(),
|
||||||
ClientEvent::EchoReply {
|
ClientEvent::EchoReply {
|
||||||
seq,
|
seq,
|
||||||
logical_seq,
|
|
||||||
remote,
|
remote,
|
||||||
rtt,
|
rtt,
|
||||||
server_timing,
|
server_timing,
|
||||||
@@ -601,7 +579,7 @@ fn format_simple(event: &ClientEvent) -> String {
|
|||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let mut out = format!(
|
let mut out = format!(
|
||||||
"reply seq={seq} logical_seq={logical_seq} remote={remote} rtt_us={}",
|
"reply seq={seq} remote={remote} rtt_us={}",
|
||||||
duration_us(rtt.effective)
|
duration_us(rtt.effective)
|
||||||
);
|
);
|
||||||
if let Some(raw) = rtt.adjusted.map(|_| rtt.raw) {
|
if let Some(raw) = rtt.adjusted.map(|_| rtt.raw) {
|
||||||
@@ -612,32 +590,26 @@ fn format_simple(event: &ClientEvent) -> String {
|
|||||||
}
|
}
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
ClientEvent::EchoLoss {
|
ClientEvent::EchoLoss { seq, .. } => {
|
||||||
seq, logical_seq, ..
|
format!("loss seq={seq}")
|
||||||
} => {
|
|
||||||
format!("loss seq={seq} logical_seq={logical_seq}")
|
|
||||||
}
|
}
|
||||||
ClientEvent::DuplicateReply { seq, remote, .. } => {
|
ClientEvent::DuplicateReply { seq, remote, .. } => {
|
||||||
format!("duplicate seq={seq} remote={remote}")
|
format!("duplicate seq={seq} remote={remote}")
|
||||||
}
|
}
|
||||||
ClientEvent::LateReply {
|
ClientEvent::LateReply {
|
||||||
seq,
|
seq,
|
||||||
logical_seq,
|
|
||||||
highest_seen,
|
highest_seen,
|
||||||
remote,
|
remote,
|
||||||
rtt,
|
rtt,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let mut out = format!(
|
let mut out = format!("late seq={seq} highest_seen={highest_seen} remote={remote}",);
|
||||||
"late seq={seq} logical_seq={} highest_seen={highest_seen} remote={remote}",
|
|
||||||
optional_u64(*logical_seq)
|
|
||||||
);
|
|
||||||
if let Some(rtt) = rtt {
|
if let Some(rtt) = rtt {
|
||||||
write!(out, " rtt_us={}", duration_us(rtt.effective)).unwrap();
|
write!(out, " rtt_us={}", duration_us(rtt.effective)).unwrap();
|
||||||
}
|
}
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
ClientEvent::Warning { kind, message } => {
|
ClientEvent::Warning { kind, message, .. } => {
|
||||||
format!("warning kind={} message={message}", warning_kind(*kind))
|
format!("warning kind={} message={message}", warning_kind(*kind))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -647,11 +619,8 @@ fn write_common(out: &mut String, event: &str) {
|
|||||||
write!(out, "event={event}").unwrap();
|
write!(out, "event={event}").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_seq(out: &mut String, seq: u32, logical_seq: Option<u64>) {
|
fn write_seq(out: &mut String, seq: u32) {
|
||||||
write!(out, " seq={seq}").unwrap();
|
write!(out, " seq={seq}").unwrap();
|
||||||
if let Some(logical_seq) = logical_seq {
|
|
||||||
write!(out, " logical_seq={logical_seq}").unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_remote(out: &mut String, remote: SocketAddr) {
|
fn write_remote(out: &mut String, remote: SocketAddr) {
|
||||||
@@ -762,19 +731,21 @@ fn write_human_received_stats(out: &mut String, stats: Option<ReceivedStatsSampl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_human_ipdv(stats: Option<&HumanEventStats>, logical_seq: Option<u64>) -> String {
|
fn format_human_ipdv(stats: Option<&HumanEventStats>, seq: u32) -> String {
|
||||||
let Some(stats) = stats else {
|
let Some(stats) = stats else {
|
||||||
return "n/a".to_owned();
|
return "n/a".to_owned();
|
||||||
};
|
};
|
||||||
|
|
||||||
let pair = logical_seq
|
let pair = stats
|
||||||
.and_then(|seq| {
|
.ipdv_pairs
|
||||||
|
.iter()
|
||||||
|
.find(|pair| pair.current_seq == seq)
|
||||||
|
.or_else(|| {
|
||||||
stats
|
stats
|
||||||
.ipdv_pairs
|
.ipdv_pairs
|
||||||
.iter()
|
.iter()
|
||||||
.find(|pair| pair.current_logical_seq == seq)
|
.find(|pair| pair.previous_seq == seq)
|
||||||
})
|
});
|
||||||
.or_else(|| stats.ipdv_pairs.last());
|
|
||||||
|
|
||||||
pair.map(|pair| format_duration(pair.rtt_ipdv))
|
pair.map(|pair| format_duration(pair.rtt_ipdv))
|
||||||
.unwrap_or_else(|| "n/a".to_owned())
|
.unwrap_or_else(|| "n/a".to_owned())
|
||||||
@@ -819,17 +790,12 @@ fn format_ns(ns: f64) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn optional_u64(value: Option<u64>) -> String {
|
|
||||||
value
|
|
||||||
.map(|value| value.to_string())
|
|
||||||
.unwrap_or_else(|| "none".to_owned())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn warning_kind(kind: WarningKind) -> &'static str {
|
fn warning_kind(kind: WarningKind) -> &'static str {
|
||||||
match kind {
|
match kind {
|
||||||
WarningKind::MalformedOrUnrelatedPacket => "malformed_or_unrelated_packet",
|
WarningKind::MalformedOrUnrelatedPacket => "malformed_or_unrelated_packet",
|
||||||
WarningKind::WrongToken => "wrong_token",
|
WarningKind::WrongToken => "wrong_token",
|
||||||
WarningKind::UntrackedReply => "untracked_reply",
|
WarningKind::UntrackedReply => "untracked_reply",
|
||||||
|
_ => "unknown",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -877,7 +843,6 @@ mod tests {
|
|||||||
fn reply_event_with_meta(packet_meta: PacketMeta) -> ClientEvent {
|
fn reply_event_with_meta(packet_meta: PacketMeta) -> ClientEvent {
|
||||||
ClientEvent::EchoReply {
|
ClientEvent::EchoReply {
|
||||||
seq: 7,
|
seq: 7,
|
||||||
logical_seq: 8,
|
|
||||||
remote: test_remote(),
|
remote: test_remote(),
|
||||||
sent_at: test_timestamp(Duration::from_secs(1)),
|
sent_at: test_timestamp(Duration::from_secs(1)),
|
||||||
received_at: test_timestamp(Duration::from_secs(1) + Duration::from_micros(1500)),
|
received_at: test_timestamp(Duration::from_secs(1) + Duration::from_micros(1500)),
|
||||||
@@ -913,7 +878,6 @@ mod tests {
|
|||||||
fn late_event_with_meta(packet_meta: PacketMeta) -> ClientEvent {
|
fn late_event_with_meta(packet_meta: PacketMeta) -> ClientEvent {
|
||||||
ClientEvent::LateReply {
|
ClientEvent::LateReply {
|
||||||
seq: 4,
|
seq: 4,
|
||||||
logical_seq: None,
|
|
||||||
highest_seen: 9,
|
highest_seen: 9,
|
||||||
remote: test_remote(),
|
remote: test_remote(),
|
||||||
sent_at: None,
|
sent_at: None,
|
||||||
@@ -1205,6 +1169,7 @@ mod tests {
|
|||||||
&ClientEvent::Warning {
|
&ClientEvent::Warning {
|
||||||
kind: WarningKind::WrongToken,
|
kind: WarningKind::WrongToken,
|
||||||
message: "bad".to_owned(),
|
message: "bad".to_owned(),
|
||||||
|
at: ClientTimestamp::now()
|
||||||
},
|
},
|
||||||
OutputMode::RttUs
|
OutputMode::RttUs
|
||||||
),
|
),
|
||||||
@@ -1217,7 +1182,6 @@ mod tests {
|
|||||||
let line = format_event(&reply_event(), OutputMode::Machine).unwrap();
|
let line = format_event(&reply_event(), OutputMode::Machine).unwrap();
|
||||||
assert!(line.starts_with("event=echo_reply "));
|
assert!(line.starts_with("event=echo_reply "));
|
||||||
assert!(line.contains("seq=7"));
|
assert!(line.contains("seq=7"));
|
||||||
assert!(line.contains("logical_seq=8"));
|
|
||||||
assert!(line.contains("remote=127.0.0.1:2112"));
|
assert!(line.contains("remote=127.0.0.1:2112"));
|
||||||
assert!(line.contains("client_send_wall_ns=1000000000"));
|
assert!(line.contains("client_send_wall_ns=1000000000"));
|
||||||
assert!(line.contains("client_receive_wall_ns=1001500000"));
|
assert!(line.contains("client_receive_wall_ns=1001500000"));
|
||||||
@@ -1326,7 +1290,7 @@ mod tests {
|
|||||||
let line = format_event(&reply_event(), OutputMode::Simple).unwrap();
|
let line = format_event(&reply_event(), OutputMode::Simple).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
line,
|
line,
|
||||||
"reply seq=7 logical_seq=8 remote=127.0.0.1:2112 rtt_us=1200 raw_rtt_us=1500 server_processing_us=300"
|
"reply seq=7 remote=127.0.0.1:2112 rtt_us=1200 raw_rtt_us=1500 server_processing_us=300"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1338,7 +1302,6 @@ mod tests {
|
|||||||
assert!(line.contains("sd=400.0µs"));
|
assert!(line.contains("sd=400.0µs"));
|
||||||
assert!(line.contains("ipdv=n/a"));
|
assert!(line.contains("ipdv=n/a"));
|
||||||
assert!(line.contains("proc=300.0µs"));
|
assert!(line.contains("proc=300.0µs"));
|
||||||
assert!(!line.contains("logical_seq="));
|
|
||||||
assert!(!line.contains("server_received="));
|
assert!(!line.contains("server_received="));
|
||||||
assert!(!line.contains("server_window="));
|
assert!(!line.contains("server_window="));
|
||||||
assert!(!line.contains("rtt_us="));
|
assert!(!line.contains("rtt_us="));
|
||||||
@@ -1360,7 +1323,6 @@ mod tests {
|
|||||||
HumanOutputOptions { verbose: true },
|
HumanOutputOptions { verbose: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(line.contains("logical_seq=8"));
|
|
||||||
assert!(line.contains("server_received=9"));
|
assert!(line.contains("server_received=9"));
|
||||||
assert!(line.contains("server_window=0x7"));
|
assert!(line.contains("server_window=0x7"));
|
||||||
}
|
}
|
||||||
@@ -1372,8 +1334,8 @@ mod tests {
|
|||||||
Some(HumanEventStats {
|
Some(HumanEventStats {
|
||||||
contributed_sample: true,
|
contributed_sample: true,
|
||||||
ipdv_pairs: vec![HumanIpdvPair {
|
ipdv_pairs: vec![HumanIpdvPair {
|
||||||
previous_logical_seq: 7,
|
previous_seq: 7,
|
||||||
current_logical_seq: 8,
|
current_seq: 8,
|
||||||
rtt_ipdv: Duration::from_micros(47),
|
rtt_ipdv: Duration::from_micros(47),
|
||||||
send_ipdv: None,
|
send_ipdv: None,
|
||||||
receive_ipdv: None,
|
receive_ipdv: None,
|
||||||
@@ -1388,7 +1350,6 @@ mod tests {
|
|||||||
fn human_reply_marks_missing_one_way_delay_unavailable() {
|
fn human_reply_marks_missing_one_way_delay_unavailable() {
|
||||||
let ClientEvent::EchoReply {
|
let ClientEvent::EchoReply {
|
||||||
seq,
|
seq,
|
||||||
logical_seq,
|
|
||||||
remote,
|
remote,
|
||||||
sent_at,
|
sent_at,
|
||||||
received_at,
|
received_at,
|
||||||
@@ -1404,7 +1365,6 @@ mod tests {
|
|||||||
};
|
};
|
||||||
let event = ClientEvent::EchoReply {
|
let event = ClientEvent::EchoReply {
|
||||||
seq,
|
seq,
|
||||||
logical_seq,
|
|
||||||
remote,
|
remote,
|
||||||
sent_at,
|
sent_at,
|
||||||
received_at,
|
received_at,
|
||||||
@@ -1427,7 +1387,6 @@ mod tests {
|
|||||||
let ts = test_timestamp(Duration::from_secs(1));
|
let ts = test_timestamp(Duration::from_secs(1));
|
||||||
let event = ClientEvent::EchoSent {
|
let event = ClientEvent::EchoSent {
|
||||||
seq: 1,
|
seq: 1,
|
||||||
logical_seq: 2,
|
|
||||||
remote: test_remote(),
|
remote: test_remote(),
|
||||||
scheduled_at: ts.mono,
|
scheduled_at: ts.mono,
|
||||||
sent_at: ts,
|
sent_at: ts,
|
||||||
@@ -1448,7 +1407,6 @@ mod tests {
|
|||||||
let events = [
|
let events = [
|
||||||
ClientEvent::EchoLoss {
|
ClientEvent::EchoLoss {
|
||||||
seq: 1,
|
seq: 1,
|
||||||
logical_seq: 2,
|
|
||||||
sent_at: ts,
|
sent_at: ts,
|
||||||
timeout_at: ts.mono + Duration::from_secs(1),
|
timeout_at: ts.mono + Duration::from_secs(1),
|
||||||
},
|
},
|
||||||
@@ -1460,7 +1418,6 @@ mod tests {
|
|||||||
},
|
},
|
||||||
ClientEvent::LateReply {
|
ClientEvent::LateReply {
|
||||||
seq: 4,
|
seq: 4,
|
||||||
logical_seq: None,
|
|
||||||
highest_seen: 9,
|
highest_seen: 9,
|
||||||
remote: test_remote(),
|
remote: test_remote(),
|
||||||
sent_at: None,
|
sent_at: None,
|
||||||
@@ -1475,6 +1432,7 @@ mod tests {
|
|||||||
ClientEvent::Warning {
|
ClientEvent::Warning {
|
||||||
kind: WarningKind::UntrackedReply,
|
kind: WarningKind::UntrackedReply,
|
||||||
message: "untracked reply".to_owned(),
|
message: "untracked reply".to_owned(),
|
||||||
|
at: ClientTimestamp::now(),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
+17
-17
@@ -13,7 +13,11 @@ use clap::Parser;
|
|||||||
use irtt_cli::{
|
use irtt_cli::{
|
||||||
format_event, format_human_event_with_options, CliArgs, HumanOutputOptions, OutputMode,
|
format_event, format_human_event_with_options, CliArgs, HumanOutputOptions, OutputMode,
|
||||||
};
|
};
|
||||||
use irtt_client::{Client, ClientEvent, ClientTimestamp, OpenOutcome, RecvBudget};
|
use irtt_client::{Client, ClientEvent, OpenOutcome, RecvBudget};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use irtt_client::ClientTimestamp;
|
||||||
|
|
||||||
#[cfg(feature = "stats")]
|
#[cfg(feature = "stats")]
|
||||||
use irtt_stats::{StatsCollector, StatsConfig};
|
use irtt_stats::{StatsCollector, StatsConfig};
|
||||||
|
|
||||||
@@ -72,7 +76,7 @@ fn run(args: CliArgs, shutdown_requested: &AtomicBool) -> Result<(), Box<dyn std
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let open = client.open(ClientTimestamp::now())?;
|
let open = client.open()?;
|
||||||
output.print_event(open_event(&open))?;
|
output.print_event(open_event(&open))?;
|
||||||
|
|
||||||
let mut interrupted = false;
|
let mut interrupted = false;
|
||||||
@@ -85,7 +89,7 @@ fn run(args: CliArgs, shutdown_requested: &AtomicBool) -> Result<(), Box<dyn std
|
|||||||
let events = client.recv_available(RECV_BUDGET)?;
|
let events = client.recv_available(RECV_BUDGET)?;
|
||||||
output.print_events(&events)?;
|
output.print_events(&events)?;
|
||||||
|
|
||||||
let events = client.poll_timeouts(ClientTimestamp::now())?;
|
let events = client.poll_timeouts()?;
|
||||||
output.print_events(&events)?;
|
output.print_events(&events)?;
|
||||||
|
|
||||||
if is_shutdown_requested(shutdown_requested) {
|
if is_shutdown_requested(shutdown_requested) {
|
||||||
@@ -105,10 +109,10 @@ fn run(args: CliArgs, shutdown_requested: &AtomicBool) -> Result<(), Box<dyn std
|
|||||||
drain_final_replies(&mut client, &mut output)?;
|
drain_final_replies(&mut client, &mut output)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let events = client.poll_timeouts(ClientTimestamp::now())?;
|
let events = client.poll_timeouts()?;
|
||||||
output.print_events(&events)?;
|
output.print_events(&events)?;
|
||||||
|
|
||||||
let events = client.close(ClientTimestamp::now())?;
|
let events = client.close()?;
|
||||||
output.print_events(&events)?;
|
output.print_events(&events)?;
|
||||||
output.print_summary()?;
|
output.print_summary()?;
|
||||||
output.out.flush()?;
|
output.out.flush()?;
|
||||||
@@ -154,7 +158,7 @@ fn drain_final_replies<W: Write>(
|
|||||||
printed |= !events.is_empty();
|
printed |= !events.is_empty();
|
||||||
output.print_events(&events)?;
|
output.print_events(&events)?;
|
||||||
|
|
||||||
let events = client.poll_timeouts(ClientTimestamp::now())?;
|
let events = client.poll_timeouts()?;
|
||||||
printed |= !events.is_empty();
|
printed |= !events.is_empty();
|
||||||
output.print_events(&events)?;
|
output.print_events(&events)?;
|
||||||
|
|
||||||
@@ -309,15 +313,14 @@ mod tests {
|
|||||||
SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 2112)
|
SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 2112)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reply_event(logical_seq: u64, rtt_us: u64) -> ClientEvent {
|
fn reply_event(seq: u32, rtt_us: u64) -> ClientEvent {
|
||||||
let sent_at = test_timestamp(Duration::from_millis(logical_seq));
|
let sent_at = test_timestamp(Duration::from_millis(seq as u64));
|
||||||
let received_at = ClientTimestamp {
|
let received_at = ClientTimestamp {
|
||||||
wall: sent_at.wall + Duration::from_micros(rtt_us),
|
wall: sent_at.wall + Duration::from_micros(rtt_us),
|
||||||
mono: sent_at.mono + Duration::from_micros(rtt_us),
|
mono: sent_at.mono + Duration::from_micros(rtt_us),
|
||||||
};
|
};
|
||||||
ClientEvent::EchoReply {
|
ClientEvent::EchoReply {
|
||||||
seq: logical_seq as u32,
|
seq,
|
||||||
logical_seq,
|
|
||||||
remote: test_remote(),
|
remote: test_remote(),
|
||||||
sent_at,
|
sent_at,
|
||||||
received_at,
|
received_at,
|
||||||
@@ -345,7 +348,6 @@ mod tests {
|
|||||||
let events = [
|
let events = [
|
||||||
ClientEvent::EchoSent {
|
ClientEvent::EchoSent {
|
||||||
seq: 1,
|
seq: 1,
|
||||||
logical_seq: 1,
|
|
||||||
remote: test_remote(),
|
remote: test_remote(),
|
||||||
scheduled_at: sent_at.mono,
|
scheduled_at: sent_at.mono,
|
||||||
sent_at,
|
sent_at,
|
||||||
@@ -355,7 +357,6 @@ mod tests {
|
|||||||
},
|
},
|
||||||
ClientEvent::EchoReply {
|
ClientEvent::EchoReply {
|
||||||
seq: 1,
|
seq: 1,
|
||||||
logical_seq: 1,
|
|
||||||
remote: test_remote(),
|
remote: test_remote(),
|
||||||
sent_at,
|
sent_at,
|
||||||
received_at,
|
received_at,
|
||||||
@@ -438,12 +439,11 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn continuous_mode_uses_continuous_stats_config() {
|
fn continuous_mode_uses_continuous_stats_config() {
|
||||||
let mut collector = StatsCollector::new(stats_config(true));
|
let mut collector = StatsCollector::new(stats_config(true));
|
||||||
for seq in 0..5000 {
|
for seq in 0..5000_u32 {
|
||||||
let sent_at = test_timestamp(Duration::from_micros(seq));
|
let sent_at = test_timestamp(Duration::from_micros(seq as u64));
|
||||||
let received_at = test_timestamp(Duration::from_micros(seq + 1));
|
let received_at = test_timestamp(Duration::from_micros(seq as u64 + 1));
|
||||||
collector.process(&ClientEvent::EchoReply {
|
collector.process(&ClientEvent::EchoReply {
|
||||||
seq: seq as u32,
|
seq,
|
||||||
logical_seq: seq,
|
|
||||||
remote: test_remote(),
|
remote: test_remote(),
|
||||||
sent_at,
|
sent_at,
|
||||||
received_at,
|
received_at,
|
||||||
|
|||||||
@@ -229,7 +229,6 @@ mod tests {
|
|||||||
|
|
||||||
collector.process(&ClientEvent::EchoSent {
|
collector.process(&ClientEvent::EchoSent {
|
||||||
seq: 1,
|
seq: 1,
|
||||||
logical_seq: 1,
|
|
||||||
remote: test_remote(),
|
remote: test_remote(),
|
||||||
scheduled_at: sent_at.mono,
|
scheduled_at: sent_at.mono,
|
||||||
sent_at,
|
sent_at,
|
||||||
@@ -239,7 +238,6 @@ mod tests {
|
|||||||
});
|
});
|
||||||
collector.process(&ClientEvent::EchoReply {
|
collector.process(&ClientEvent::EchoReply {
|
||||||
seq: 1,
|
seq: 1,
|
||||||
logical_seq: 1,
|
|
||||||
remote: test_remote(),
|
remote: test_remote(),
|
||||||
sent_at,
|
sent_at,
|
||||||
received_at,
|
received_at,
|
||||||
@@ -287,7 +285,6 @@ mod tests {
|
|||||||
let received_at = test_timestamp(Duration::from_secs(1) + Duration::from_micros(1500));
|
let received_at = test_timestamp(Duration::from_secs(1) + Duration::from_micros(1500));
|
||||||
collector.process(&ClientEvent::EchoReply {
|
collector.process(&ClientEvent::EchoReply {
|
||||||
seq: 1,
|
seq: 1,
|
||||||
logical_seq: 1,
|
|
||||||
remote: test_remote(),
|
remote: test_remote(),
|
||||||
sent_at: test_timestamp(Duration::from_secs(1)),
|
sent_at: test_timestamp(Duration::from_secs(1)),
|
||||||
received_at,
|
received_at,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "irtt-client"
|
name = "irtt-client"
|
||||||
version = "0.1.1"
|
version = "0.2.0"
|
||||||
description = "IRTT-compatible Rust client library"
|
description = "IRTT-compatible Rust client library"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ impl Client {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open(&mut self, now: ClientTimestamp) -> Result<OpenOutcome, ClientError> {
|
pub fn open(&mut self) -> Result<OpenOutcome, ClientError> {
|
||||||
match self.phase {
|
match self.phase {
|
||||||
ClientPhase::Connected => {}
|
ClientPhase::Connected => {}
|
||||||
ClientPhase::Open { .. } => return Err(ClientError::AlreadyOpen),
|
ClientPhase::Open { .. } => return Err(ClientError::AlreadyOpen),
|
||||||
@@ -81,10 +81,44 @@ impl Client {
|
|||||||
ClientPhase::NoTestCompleted => return Err(ClientError::AlreadyCompleted),
|
ClientPhase::NoTestCompleted => return Err(ClientError::AlreadyCompleted),
|
||||||
}
|
}
|
||||||
|
|
||||||
let outcome = self.open_inner(now);
|
let outcome = (|| -> Result<OpenOutcome, ClientError> {
|
||||||
|
let request = OpenRequest {
|
||||||
|
params: self.requested.clone(),
|
||||||
|
close: self.config.run_mode == RunMode::NoTest,
|
||||||
|
};
|
||||||
|
|
||||||
|
let packet = encode_open_request(&request, self.config.hmac_key.as_deref())?;
|
||||||
|
let mut buf = [0_u8; MAX_OPEN_PACKET_SIZE];
|
||||||
|
|
||||||
|
for timeout in &self.config.open_timeouts {
|
||||||
|
self.socket.set_read_timeout(Some(*timeout))?;
|
||||||
|
self.socket.send(&packet)?;
|
||||||
|
|
||||||
|
match self.socket.recv(&mut buf) {
|
||||||
|
Ok(size) => {
|
||||||
|
let reply = irtt_proto::decode_open_reply(
|
||||||
|
&buf[..size],
|
||||||
|
self.config.hmac_key.as_deref(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
return self.accept_open_reply(reply, ClientTimestamp::now());
|
||||||
|
}
|
||||||
|
Err(err)
|
||||||
|
if matches!(
|
||||||
|
err.kind(),
|
||||||
|
io::ErrorKind::WouldBlock | io::ErrorKind::TimedOut
|
||||||
|
) => {}
|
||||||
|
Err(err) => return Err(ClientError::Socket(err)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(ClientError::OpenTimeout)
|
||||||
|
})();
|
||||||
|
|
||||||
let restore = self
|
let restore = self
|
||||||
.socket
|
.socket
|
||||||
.set_read_timeout(self.config.socket_config.recv_timeout);
|
.set_read_timeout(self.config.socket_config.recv_timeout);
|
||||||
|
|
||||||
match (outcome, restore) {
|
match (outcome, restore) {
|
||||||
(Ok(outcome), Ok(())) => Ok(outcome),
|
(Ok(outcome), Ok(())) => Ok(outcome),
|
||||||
(Ok(_), Err(source)) => Err(ClientError::ReadTimeoutRestore { source }),
|
(Ok(_), Err(source)) => Err(ClientError::ReadTimeoutRestore { source }),
|
||||||
@@ -93,39 +127,7 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_inner(&mut self, _now: ClientTimestamp) -> Result<OpenOutcome, ClientError> {
|
pub fn close(&mut self) -> Result<Vec<ClientEvent>, ClientError> {
|
||||||
let request = OpenRequest {
|
|
||||||
params: self.requested.clone(),
|
|
||||||
close: self.config.run_mode == RunMode::NoTest,
|
|
||||||
};
|
|
||||||
let packet = encode_open_request(&request, self.config.hmac_key.as_deref())?;
|
|
||||||
let mut buf = [0_u8; MAX_OPEN_PACKET_SIZE];
|
|
||||||
|
|
||||||
for timeout in &self.config.open_timeouts {
|
|
||||||
self.socket.set_read_timeout(Some(*timeout))?;
|
|
||||||
self.socket.send(&packet)?;
|
|
||||||
|
|
||||||
match self.socket.recv(&mut buf) {
|
|
||||||
Ok(size) => {
|
|
||||||
let reply = irtt_proto::decode_open_reply(
|
|
||||||
&buf[..size],
|
|
||||||
self.config.hmac_key.as_deref(),
|
|
||||||
)?;
|
|
||||||
return self.accept_open_reply(reply, ClientTimestamp::now());
|
|
||||||
}
|
|
||||||
Err(err)
|
|
||||||
if matches!(
|
|
||||||
err.kind(),
|
|
||||||
io::ErrorKind::WouldBlock | io::ErrorKind::TimedOut
|
|
||||||
) => {}
|
|
||||||
Err(err) => return Err(ClientError::Socket(err)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(ClientError::OpenTimeout)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn close(&mut self, now: ClientTimestamp) -> Result<Vec<ClientEvent>, ClientError> {
|
|
||||||
let token = match self.phase {
|
let token = match self.phase {
|
||||||
ClientPhase::Open { token } => token,
|
ClientPhase::Open { token } => token,
|
||||||
ClientPhase::Closed => return Err(ClientError::AlreadyClosed),
|
ClientPhase::Closed => return Err(ClientError::AlreadyClosed),
|
||||||
@@ -147,7 +149,7 @@ impl Client {
|
|||||||
Ok(vec![ClientEvent::SessionClosed {
|
Ok(vec![ClientEvent::SessionClosed {
|
||||||
remote: self.remote,
|
remote: self.remote,
|
||||||
token,
|
token,
|
||||||
at: now,
|
at: ClientTimestamp::now(),
|
||||||
}])
|
}])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,7 +210,6 @@ impl Client {
|
|||||||
.expect("negotiated must exist when Open");
|
.expect("negotiated must exist when Open");
|
||||||
|
|
||||||
let wire_seq = session.next_wire_seq;
|
let wire_seq = session.next_wire_seq;
|
||||||
let logical_seq = session.next_logical_seq;
|
|
||||||
let scheduled_at = session.next_send_at;
|
let scheduled_at = session.next_send_at;
|
||||||
|
|
||||||
let request = EchoRequest {
|
let request = EchoRequest {
|
||||||
@@ -230,7 +231,6 @@ impl Client {
|
|||||||
.expect("session must exist when phase is Open");
|
.expect("session must exist when phase is Open");
|
||||||
|
|
||||||
let pending = PendingProbe {
|
let pending = PendingProbe {
|
||||||
logical_seq,
|
|
||||||
wire_seq,
|
wire_seq,
|
||||||
sent_at,
|
sent_at,
|
||||||
timeout_at: sent_at
|
timeout_at: sent_at
|
||||||
@@ -241,13 +241,6 @@ impl Client {
|
|||||||
session.pending.insert(pending)?;
|
session.pending.insert(pending)?;
|
||||||
|
|
||||||
session.next_wire_seq = session.next_wire_seq.wrapping_add(1);
|
session.next_wire_seq = session.next_wire_seq.wrapping_add(1);
|
||||||
session.next_logical_seq =
|
|
||||||
session
|
|
||||||
.next_logical_seq
|
|
||||||
.checked_add(1)
|
|
||||||
.ok_or(ClientError::CounterOverflow {
|
|
||||||
counter: "next_logical_seq",
|
|
||||||
})?;
|
|
||||||
session.packets_sent =
|
session.packets_sent =
|
||||||
session
|
session
|
||||||
.packets_sent
|
.packets_sent
|
||||||
@@ -277,7 +270,6 @@ impl Client {
|
|||||||
|
|
||||||
Ok(vec![ClientEvent::EchoSent {
|
Ok(vec![ClientEvent::EchoSent {
|
||||||
seq: wire_seq,
|
seq: wire_seq,
|
||||||
logical_seq,
|
|
||||||
remote: self.remote,
|
remote: self.remote,
|
||||||
scheduled_at,
|
scheduled_at,
|
||||||
sent_at,
|
sent_at,
|
||||||
@@ -317,6 +309,7 @@ impl Client {
|
|||||||
return Ok(vec![ClientEvent::Warning {
|
return Ok(vec![ClientEvent::Warning {
|
||||||
kind: WarningKind::MalformedOrUnrelatedPacket,
|
kind: WarningKind::MalformedOrUnrelatedPacket,
|
||||||
message: "dropped malformed or unrelated packet".to_owned(),
|
message: "dropped malformed or unrelated packet".to_owned(),
|
||||||
|
at: now,
|
||||||
}]);
|
}]);
|
||||||
};
|
};
|
||||||
self.process_echo_reply(reply, packet_len, now, datagram.meta)
|
self.process_echo_reply(reply, packet_len, now, datagram.meta)
|
||||||
@@ -339,7 +332,19 @@ impl Client {
|
|||||||
recv_buffer_size(self.config.hmac_key.is_some(), self.negotiated.as_ref())
|
recv_buffer_size(self.config.hmac_key.is_some(), self.negotiated.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn poll_timeouts(&mut self, now: ClientTimestamp) -> Result<Vec<ClientEvent>, ClientError> {
|
/// Polls for probes that have timed out as of the current monotonic time.
|
||||||
|
pub fn poll_timeouts(&mut self) -> Result<Vec<ClientEvent>, ClientError> {
|
||||||
|
self.poll_timeouts_at(Instant::now())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Polls for probes that have timed out as of `now`.
|
||||||
|
///
|
||||||
|
/// This is useful for callers that drive `Client` from their own event loop and
|
||||||
|
/// want timeout decisions to use the same sampled `Instant` as their scheduling
|
||||||
|
/// logic.
|
||||||
|
///
|
||||||
|
/// `now` is monotonic time only; wall-clock time is not used for timeout expiry.
|
||||||
|
pub fn poll_timeouts_at(&mut self, now: Instant) -> Result<Vec<ClientEvent>, ClientError> {
|
||||||
match self.phase {
|
match self.phase {
|
||||||
ClientPhase::Open { .. } => {}
|
ClientPhase::Open { .. } => {}
|
||||||
ClientPhase::Closed => return Err(ClientError::AlreadyClosed),
|
ClientPhase::Closed => return Err(ClientError::AlreadyClosed),
|
||||||
@@ -352,12 +357,11 @@ impl Client {
|
|||||||
.as_mut()
|
.as_mut()
|
||||||
.expect("session must exist when phase is Open");
|
.expect("session must exist when phase is Open");
|
||||||
|
|
||||||
let expired = session.pending.drain_expired(now.mono);
|
let expired = session.pending.drain_expired(now);
|
||||||
let mut events = Vec::with_capacity(expired.len());
|
let mut events = Vec::with_capacity(expired.len());
|
||||||
for probe in expired {
|
for probe in expired {
|
||||||
events.push(ClientEvent::EchoLoss {
|
events.push(ClientEvent::EchoLoss {
|
||||||
seq: probe.wire_seq,
|
seq: probe.wire_seq,
|
||||||
logical_seq: probe.logical_seq,
|
|
||||||
sent_at: probe.sent_at,
|
sent_at: probe.sent_at,
|
||||||
timeout_at: probe.timeout_at,
|
timeout_at: probe.timeout_at,
|
||||||
});
|
});
|
||||||
@@ -401,6 +405,7 @@ impl Client {
|
|||||||
return Ok(vec![ClientEvent::Warning {
|
return Ok(vec![ClientEvent::Warning {
|
||||||
kind: WarningKind::MalformedOrUnrelatedPacket,
|
kind: WarningKind::MalformedOrUnrelatedPacket,
|
||||||
message: "dropped malformed or unrelated packet".to_owned(),
|
message: "dropped malformed or unrelated packet".to_owned(),
|
||||||
|
at: now,
|
||||||
}]);
|
}]);
|
||||||
};
|
};
|
||||||
self.process_echo_reply(reply, packet_len, now, meta)
|
self.process_echo_reply(reply, packet_len, now, meta)
|
||||||
@@ -433,6 +438,7 @@ impl Client {
|
|||||||
"dropped reply with wrong token: expected {token:#x}, got {:#x}",
|
"dropped reply with wrong token: expected {token:#x}, got {:#x}",
|
||||||
reply.token
|
reply.token
|
||||||
),
|
),
|
||||||
|
at: now,
|
||||||
}]);
|
}]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -451,13 +457,12 @@ impl Client {
|
|||||||
let highest_seen = session.highest_received_seq.unwrap_or(wire_seq);
|
let highest_seen = session.highest_received_seq.unwrap_or(wire_seq);
|
||||||
|
|
||||||
update_highest_received(session, wire_seq);
|
update_highest_received(session, wire_seq);
|
||||||
session.completed.insert(wire_seq, pending.logical_seq);
|
session.completed.insert(wire_seq);
|
||||||
|
|
||||||
let mut events = Vec::new();
|
let mut events = Vec::new();
|
||||||
if is_late {
|
if is_late {
|
||||||
events.push(ClientEvent::LateReply {
|
events.push(ClientEvent::LateReply {
|
||||||
seq: wire_seq,
|
seq: wire_seq,
|
||||||
logical_seq: Some(pending.logical_seq),
|
|
||||||
highest_seen,
|
highest_seen,
|
||||||
remote: self.remote,
|
remote: self.remote,
|
||||||
sent_at: Some(pending.sent_at),
|
sent_at: Some(pending.sent_at),
|
||||||
@@ -472,7 +477,6 @@ impl Client {
|
|||||||
} else {
|
} else {
|
||||||
events.push(ClientEvent::EchoReply {
|
events.push(ClientEvent::EchoReply {
|
||||||
seq: wire_seq,
|
seq: wire_seq,
|
||||||
logical_seq: pending.logical_seq,
|
|
||||||
remote: self.remote,
|
remote: self.remote,
|
||||||
sent_at: pending.sent_at,
|
sent_at: pending.sent_at,
|
||||||
received_at: now,
|
received_at: now,
|
||||||
@@ -503,11 +507,10 @@ impl Client {
|
|||||||
let received_stats = build_received_stats(&reply);
|
let received_stats = build_received_stats(&reply);
|
||||||
let highest_seen = session.highest_received_seq.unwrap_or(wire_seq);
|
let highest_seen = session.highest_received_seq.unwrap_or(wire_seq);
|
||||||
update_highest_received(session, wire_seq);
|
update_highest_received(session, wire_seq);
|
||||||
session.completed.insert(wire_seq, timed_out.logical_seq);
|
session.completed.insert(wire_seq);
|
||||||
|
|
||||||
let mut events = vec![ClientEvent::LateReply {
|
let mut events = vec![ClientEvent::LateReply {
|
||||||
seq: wire_seq,
|
seq: wire_seq,
|
||||||
logical_seq: Some(timed_out.logical_seq),
|
|
||||||
highest_seen,
|
highest_seen,
|
||||||
remote: self.remote,
|
remote: self.remote,
|
||||||
sent_at: Some(timed_out.sent_at),
|
sent_at: Some(timed_out.sent_at),
|
||||||
@@ -529,7 +532,6 @@ impl Client {
|
|||||||
{
|
{
|
||||||
Ok(vec![ClientEvent::LateReply {
|
Ok(vec![ClientEvent::LateReply {
|
||||||
seq: wire_seq,
|
seq: wire_seq,
|
||||||
logical_seq: None,
|
|
||||||
highest_seen: session.highest_received_seq.unwrap(),
|
highest_seen: session.highest_received_seq.unwrap(),
|
||||||
remote: self.remote,
|
remote: self.remote,
|
||||||
sent_at: None,
|
sent_at: None,
|
||||||
@@ -547,6 +549,7 @@ impl Client {
|
|||||||
message: format!(
|
message: format!(
|
||||||
"dropped reply with untracked seq {wire_seq} (no pending or completed entry)"
|
"dropped reply with untracked seq {wire_seq} (no pending or completed entry)"
|
||||||
),
|
),
|
||||||
|
at: now,
|
||||||
}])
|
}])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -630,7 +633,6 @@ impl Client {
|
|||||||
|
|
||||||
self.session = Some(ActiveSession {
|
self.session = Some(ActiveSession {
|
||||||
next_wire_seq: 0,
|
next_wire_seq: 0,
|
||||||
next_logical_seq: 0,
|
|
||||||
highest_received_seq: None,
|
highest_received_seq: None,
|
||||||
packets_sent: 0,
|
packets_sent: 0,
|
||||||
start_mono: now.mono,
|
start_mono: now.mono,
|
||||||
|
|||||||
@@ -11,12 +11,9 @@ fn open_fails_after_close() {
|
|||||||
let _ = recv_request(&socket, &tx);
|
let _ = recv_request(&socket, &tx);
|
||||||
});
|
});
|
||||||
let mut client = Client::connect(default_test_config(server.addr)).unwrap();
|
let mut client = Client::connect(default_test_config(server.addr)).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(client.open(), Err(ClientError::AlreadyClosed)));
|
||||||
client.open(ClientTimestamp::now()),
|
|
||||||
Err(ClientError::AlreadyClosed)
|
|
||||||
));
|
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,8 +28,8 @@ fn close_sends_one_close_packet_with_negotiated_token() {
|
|||||||
let _ = recv_request(&socket, &tx);
|
let _ = recv_request(&socket, &tx);
|
||||||
});
|
});
|
||||||
let mut client = Client::connect(default_test_config(server.addr)).unwrap();
|
let mut client = Client::connect(default_test_config(server.addr)).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
let events = client.close(ClientTimestamp::now()).unwrap();
|
let events = client.close().unwrap();
|
||||||
assert_eq!(events.len(), 1);
|
assert_eq!(events.len(), 1);
|
||||||
let packets: Vec<_> = server.rx.iter().take(2).collect();
|
let packets: Vec<_> = server.rx.iter().take(2).collect();
|
||||||
let close = &packets[1];
|
let close = &packets[1];
|
||||||
@@ -62,8 +59,8 @@ fn send_probe_fails_after_close() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
let mut client = Client::connect(default_test_config(server.addr)).unwrap();
|
let mut client = Client::connect(default_test_config(server.addr)).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
client.send_probe(),
|
client.send_probe(),
|
||||||
Err(ClientError::AlreadyClosed)
|
Err(ClientError::AlreadyClosed)
|
||||||
@@ -84,14 +81,14 @@ fn close_clears_timed_out_metadata() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
|
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
thread::sleep(Duration::from_millis(60));
|
thread::sleep(Duration::from_millis(60));
|
||||||
client.poll_timeouts(ClientTimestamp::now()).unwrap();
|
client.poll_timeouts().unwrap();
|
||||||
assert_eq!(client.session.as_ref().unwrap().timed_out.len(), 1);
|
assert_eq!(client.session.as_ref().unwrap().timed_out.len(), 1);
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
assert!(client.session.is_none());
|
assert!(client.session.is_none());
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
@@ -132,7 +129,7 @@ fn close_flagged_echo_reply_emits_reply_then_closes_without_sending_close() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
|
|
||||||
let events = client.recv_once().unwrap();
|
let events = client.recv_once().unwrap();
|
||||||
@@ -170,6 +167,6 @@ fn normal_echo_reply_does_not_close_session() {
|
|||||||
assert!(client.next_send_deadline().is_some());
|
assert!(client.next_send_deadline().is_some());
|
||||||
assert!(client.send_probe().is_ok());
|
assert!(client.send_probe().is_ok());
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
@@ -26,13 +26,13 @@ fn normal_open_applies_negotiated_dscp_after_open_and_close_clears_it() {
|
|||||||
socket_traffic_class(&client.socket, client.remote).unwrap() & 0xfc,
|
socket_traffic_class(&client.socket, client.remote).unwrap() & 0xfc,
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
socket_traffic_class(&client.socket, client.remote).unwrap() & 0xfc,
|
socket_traffic_class(&client.socket, client.remote).unwrap() & 0xfc,
|
||||||
184
|
184
|
||||||
);
|
);
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
socket_traffic_class(&client.socket, client.remote).unwrap(),
|
socket_traffic_class(&client.socket, client.remote).unwrap(),
|
||||||
0
|
0
|
||||||
@@ -58,7 +58,7 @@ fn normal_open_uses_negotiated_dscp_not_requested_dscp() {
|
|||||||
config.negotiation_policy = NegotiationPolicy::Loose;
|
config.negotiation_policy = NegotiationPolicy::Loose;
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
|
|
||||||
let negotiated = assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
let negotiated = assert_open_started(client.open().unwrap());
|
||||||
assert_eq!(negotiated.params.dscp, 0);
|
assert_eq!(negotiated.params.dscp, 0);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
socket_traffic_class(&client.socket, client.remote).unwrap(),
|
socket_traffic_class(&client.socket, client.remote).unwrap(),
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ fn hmac_open_success() {
|
|||||||
});
|
});
|
||||||
config.server_addr = server.addr.to_string();
|
config.server_addr = server.addr.to_string();
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ fn hmac_open_rejects_missing_hmac() {
|
|||||||
config.server_addr = server.addr.to_string();
|
config.server_addr = server.addr.to_string();
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
client.open(ClientTimestamp::now()),
|
client.open(),
|
||||||
Err(ClientError::Protocol(
|
Err(ClientError::Protocol(
|
||||||
irtt_proto::ProtoError::HmacPresenceMismatch
|
irtt_proto::ProtoError::HmacPresenceMismatch
|
||||||
))
|
))
|
||||||
@@ -55,7 +55,7 @@ fn hmac_open_rejects_bad_hmac() {
|
|||||||
config.server_addr = server.addr.to_string();
|
config.server_addr = server.addr.to_string();
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
client.open(ClientTimestamp::now()),
|
client.open(),
|
||||||
Err(ClientError::Protocol(irtt_proto::ProtoError::BadHmac))
|
Err(ClientError::Protocol(irtt_proto::ProtoError::BadHmac))
|
||||||
));
|
));
|
||||||
server.join();
|
server.join();
|
||||||
@@ -76,8 +76,8 @@ fn hmac_close_packet_includes_valid_hmac() {
|
|||||||
});
|
});
|
||||||
config.server_addr = server.addr.to_string();
|
config.server_addr = server.addr.to_string();
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
let packets: Vec<_> = server.rx.iter().take(2).collect();
|
let packets: Vec<_> = server.rx.iter().take(2).collect();
|
||||||
let close = &packets[1];
|
let close = &packets[1];
|
||||||
assert_eq!(close[3], flags::FLAG_CLOSE | FLAG_HMAC);
|
assert_eq!(close[3], flags::FLAG_CLOSE | FLAG_HMAC);
|
||||||
@@ -125,7 +125,7 @@ fn bad_hmac_reply_is_dropped() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
thread::sleep(Duration::from_millis(30));
|
thread::sleep(Duration::from_millis(30));
|
||||||
let events = client.recv_once().unwrap();
|
let events = client.recv_once().unwrap();
|
||||||
@@ -174,7 +174,7 @@ fn hmac_echo_request_reply_works() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
thread::sleep(Duration::from_millis(50));
|
thread::sleep(Duration::from_millis(50));
|
||||||
let events = client.recv_once().unwrap();
|
let events = client.recv_once().unwrap();
|
||||||
|
|||||||
@@ -8,13 +8,10 @@ fn no_test_open_close_succeeds_on_open_reply_close() {
|
|||||||
let server = no_test_server(params.clone(), 0);
|
let server = no_test_server(params.clone(), 0);
|
||||||
config.server_addr = server.addr.to_string();
|
config.server_addr = server.addr.to_string();
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
let negotiated = assert_no_test_completed(client.open(ClientTimestamp::now()).unwrap());
|
let negotiated = assert_no_test_completed(client.open().unwrap());
|
||||||
assert_eq!(negotiated.params, params);
|
assert_eq!(negotiated.params, params);
|
||||||
assert_eq!(client.negotiated.as_ref(), Some(&negotiated));
|
assert_eq!(client.negotiated.as_ref(), Some(&negotiated));
|
||||||
assert!(matches!(
|
assert!(matches!(client.close(), Err(ClientError::NotOpen)));
|
||||||
client.close(ClientTimestamp::now()),
|
|
||||||
Err(ClientError::NotOpen)
|
|
||||||
));
|
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +23,7 @@ fn no_test_success_validates_params() {
|
|||||||
let server = no_test_server(params.clone(), 0);
|
let server = no_test_server(params.clone(), 0);
|
||||||
config.server_addr = server.addr.to_string();
|
config.server_addr = server.addr.to_string();
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
let negotiated = assert_no_test_completed(client.open(ClientTimestamp::now()).unwrap());
|
let negotiated = assert_no_test_completed(client.open().unwrap());
|
||||||
assert_eq!(negotiated.params, params);
|
assert_eq!(negotiated.params, params);
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
@@ -40,7 +37,7 @@ fn no_test_rejects_non_close_open_reply() {
|
|||||||
config.server_addr = server.addr.to_string();
|
config.server_addr = server.addr.to_string();
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
client.open(ClientTimestamp::now()),
|
client.open(),
|
||||||
Err(ClientError::UnexpectedNoTestReply)
|
Err(ClientError::UnexpectedNoTestReply)
|
||||||
));
|
));
|
||||||
server.join();
|
server.join();
|
||||||
@@ -55,7 +52,7 @@ fn no_test_rejects_non_zero_token_with_close_reply() {
|
|||||||
config.server_addr = server.addr.to_string();
|
config.server_addr = server.addr.to_string();
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
client.open(ClientTimestamp::now()),
|
client.open(),
|
||||||
Err(ClientError::NonZeroNoTestToken { token: TOKEN })
|
Err(ClientError::NonZeroNoTestToken { token: TOKEN })
|
||||||
));
|
));
|
||||||
server.join();
|
server.join();
|
||||||
@@ -71,7 +68,7 @@ fn no_test_strict_negotiation_rejects_changed_params() {
|
|||||||
config.server_addr = server.addr.to_string();
|
config.server_addr = server.addr.to_string();
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
client.open(ClientTimestamp::now()),
|
client.open(),
|
||||||
Err(ClientError::NegotiationRejected { .. })
|
Err(ClientError::NegotiationRejected { .. })
|
||||||
));
|
));
|
||||||
server.join();
|
server.join();
|
||||||
@@ -87,7 +84,7 @@ fn no_test_loose_negotiation_accepts_restricted_params() {
|
|||||||
let server = no_test_server(params.clone(), 0);
|
let server = no_test_server(params.clone(), 0);
|
||||||
config.server_addr = server.addr.to_string();
|
config.server_addr = server.addr.to_string();
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
let negotiated = assert_no_test_completed(client.open(ClientTimestamp::now()).unwrap());
|
let negotiated = assert_no_test_completed(client.open().unwrap());
|
||||||
assert_eq!(negotiated.params, params);
|
assert_eq!(negotiated.params, params);
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
@@ -100,7 +97,7 @@ fn send_probe_fails_after_no_test_completed() {
|
|||||||
let server = no_test_server(params, 0);
|
let server = no_test_server(params, 0);
|
||||||
config.server_addr = server.addr.to_string();
|
config.server_addr = server.addr.to_string();
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_no_test_completed(client.open(ClientTimestamp::now()).unwrap());
|
assert_no_test_completed(client.open().unwrap());
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
client.send_probe(),
|
client.send_probe(),
|
||||||
Err(ClientError::AlreadyCompleted)
|
Err(ClientError::AlreadyCompleted)
|
||||||
@@ -116,10 +113,7 @@ fn open_fails_after_no_test_completed() {
|
|||||||
let server = no_test_server(params, 0);
|
let server = no_test_server(params, 0);
|
||||||
config.server_addr = server.addr.to_string();
|
config.server_addr = server.addr.to_string();
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_no_test_completed(client.open(ClientTimestamp::now()).unwrap());
|
assert_no_test_completed(client.open().unwrap());
|
||||||
assert!(matches!(
|
assert!(matches!(client.open(), Err(ClientError::AlreadyCompleted)));
|
||||||
client.open(ClientTimestamp::now()),
|
|
||||||
Err(ClientError::AlreadyCompleted)
|
|
||||||
));
|
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,7 @@ fn successful_open_handshake() {
|
|||||||
let server = open_success_server(params.clone());
|
let server = open_success_server(params.clone());
|
||||||
let mut client = Client::connect(default_test_config(server.addr)).unwrap();
|
let mut client = Client::connect(default_test_config(server.addr)).unwrap();
|
||||||
|
|
||||||
let negotiated = assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
let negotiated = assert_open_started(client.open().unwrap());
|
||||||
assert_eq!(negotiated.params, params);
|
assert_eq!(negotiated.params, params);
|
||||||
assert!(matches!(client.phase, ClientPhase::Open { token: TOKEN }));
|
assert!(matches!(client.phase, ClientPhase::Open { token: TOKEN }));
|
||||||
server.join();
|
server.join();
|
||||||
@@ -30,11 +30,8 @@ fn open_fails_when_already_open() {
|
|||||||
let params = params_from_config(&config).unwrap();
|
let params = params_from_config(&config).unwrap();
|
||||||
let server = open_success_server(params);
|
let server = open_success_server(params);
|
||||||
let mut client = Client::connect(default_test_config(server.addr)).unwrap();
|
let mut client = Client::connect(default_test_config(server.addr)).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
assert!(matches!(
|
assert!(matches!(client.open(), Err(ClientError::AlreadyOpen)));
|
||||||
client.open(ClientTimestamp::now()),
|
|
||||||
Err(ClientError::AlreadyOpen)
|
|
||||||
));
|
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +50,7 @@ fn open_retries_after_first_timeout() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
let outcome = client.open(ClientTimestamp::now()).unwrap();
|
let outcome = client.open().unwrap();
|
||||||
assert_open_started(outcome);
|
assert_open_started(outcome);
|
||||||
assert_eq!(server.rx.iter().take(2).count(), 2);
|
assert_eq!(server.rx.iter().take(2).count(), 2);
|
||||||
server.join();
|
server.join();
|
||||||
@@ -67,10 +64,7 @@ fn open_timeout_after_all_timeouts() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(client.open(), Err(ClientError::OpenTimeout)));
|
||||||
client.open(ClientTimestamp::now()),
|
|
||||||
Err(ClientError::OpenTimeout)
|
|
||||||
));
|
|
||||||
assert_eq!(server.rx.iter().take(2).count(), 2);
|
assert_eq!(server.rx.iter().take(2).count(), 2);
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
@@ -87,10 +81,7 @@ fn open_restores_configured_read_timeout_after_timeout() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(client.open(), Err(ClientError::OpenTimeout)));
|
||||||
client.open(ClientTimestamp::now()),
|
|
||||||
Err(ClientError::OpenTimeout)
|
|
||||||
));
|
|
||||||
|
|
||||||
let start = std::time::Instant::now();
|
let start = std::time::Instant::now();
|
||||||
let mut buf = [0_u8; 1];
|
let mut buf = [0_u8; 1];
|
||||||
@@ -108,7 +99,7 @@ fn protocol_version_mismatch_fails() {
|
|||||||
let server = open_success_server(params);
|
let server = open_success_server(params);
|
||||||
let mut client = Client::connect(default_test_config(server.addr)).unwrap();
|
let mut client = Client::connect(default_test_config(server.addr)).unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
client.open(ClientTimestamp::now()),
|
client.open(),
|
||||||
Err(ClientError::ProtocolVersionMismatch { received: 2, .. })
|
Err(ClientError::ProtocolVersionMismatch { received: 2, .. })
|
||||||
));
|
));
|
||||||
server.join();
|
server.join();
|
||||||
@@ -124,9 +115,6 @@ fn server_rejection_fails_in_normal_mode() {
|
|||||||
socket.send_to(&reply, peer).unwrap();
|
socket.send_to(&reply, peer).unwrap();
|
||||||
});
|
});
|
||||||
let mut client = Client::connect(default_test_config(server.addr)).unwrap();
|
let mut client = Client::connect(default_test_config(server.addr)).unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(client.open(), Err(ClientError::ServerRejected)));
|
||||||
client.open(ClientTimestamp::now()),
|
|
||||||
Err(ClientError::ServerRejected)
|
|
||||||
));
|
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
@@ -38,19 +38,14 @@ fn send_probe_sends_valid_echo_request() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
let events = client.send_probe().unwrap();
|
let events = client.send_probe().unwrap();
|
||||||
assert_eq!(events.len(), 1);
|
assert_eq!(events.len(), 1);
|
||||||
match &events[0] {
|
match &events[0] {
|
||||||
ClientEvent::EchoSent {
|
ClientEvent::EchoSent {
|
||||||
seq,
|
seq, remote, bytes, ..
|
||||||
logical_seq,
|
|
||||||
remote,
|
|
||||||
bytes,
|
|
||||||
..
|
|
||||||
} => {
|
} => {
|
||||||
assert_eq!(*seq, 0);
|
assert_eq!(*seq, 0);
|
||||||
assert_eq!(*logical_seq, 0);
|
|
||||||
assert_eq!(*remote, server.addr);
|
assert_eq!(*remote, server.addr);
|
||||||
assert_eq!(*bytes, echo_packet_len(false, ¶ms));
|
assert_eq!(*bytes, echo_packet_len(false, ¶ms));
|
||||||
}
|
}
|
||||||
@@ -69,7 +64,7 @@ fn send_probe_sends_valid_echo_request() {
|
|||||||
assert_eq!(req_token, TOKEN);
|
assert_eq!(req_token, TOKEN);
|
||||||
let seq = u32::from_le_bytes(echo_req[12..16].try_into().unwrap());
|
let seq = u32::from_le_bytes(echo_req[12..16].try_into().unwrap());
|
||||||
assert_eq!(seq, 0);
|
assert_eq!(seq, 0);
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +84,7 @@ fn echo_sent_reports_schedule_and_timer_error() {
|
|||||||
mono: Instant::now(),
|
mono: Instant::now(),
|
||||||
wall: SystemTime::now(),
|
wall: SystemTime::now(),
|
||||||
};
|
};
|
||||||
assert_open_started(client.open(start).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
let session_start = client.session.as_ref().unwrap().start_mono;
|
let session_start = client.session.as_ref().unwrap().start_mono;
|
||||||
assert!(
|
assert!(
|
||||||
session_start >= start.mono,
|
session_start >= start.mono,
|
||||||
@@ -115,7 +110,7 @@ fn echo_sent_reports_schedule_and_timer_error() {
|
|||||||
other => panic!("expected EchoSent, got {other:?}"),
|
other => panic!("expected EchoSent, got {other:?}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +126,7 @@ fn send_probe_starts_seq_at_zero_and_increments() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
@@ -146,7 +141,7 @@ fn send_probe_starts_seq_at_zero_and_increments() {
|
|||||||
let seq = u32::from_le_bytes(pkt[12..16].try_into().unwrap());
|
let seq = u32::from_le_bytes(pkt[12..16].try_into().unwrap());
|
||||||
assert_eq!(seq, i as u32);
|
assert_eq!(seq, i as u32);
|
||||||
}
|
}
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +167,7 @@ fn send_probe_respects_finite_duration_exclusive_end() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
|
|
||||||
let session = client.session.as_ref().unwrap();
|
let session = client.session.as_ref().unwrap();
|
||||||
let start = session.start_mono;
|
let start = session.start_mono;
|
||||||
@@ -199,7 +194,7 @@ fn send_probe_respects_finite_duration_exclusive_end() {
|
|||||||
assert!(client.session.as_ref().unwrap().sending_done);
|
assert!(client.session.as_ref().unwrap().sending_done);
|
||||||
assert!(client.next_send_deadline().is_none());
|
assert!(client.next_send_deadline().is_none());
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,7 +220,7 @@ fn continuous_duration_keeps_generating_send_deadlines() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
|
|
||||||
let start = client.session.as_ref().unwrap().start_mono;
|
let start = client.session.as_ref().unwrap().start_mono;
|
||||||
let interval = Duration::from_millis(500);
|
let interval = Duration::from_millis(500);
|
||||||
@@ -240,7 +235,7 @@ fn continuous_duration_keeps_generating_send_deadlines() {
|
|||||||
assert!(!client.session.as_ref().unwrap().sending_done);
|
assert!(!client.session.as_ref().unwrap().sending_done);
|
||||||
}
|
}
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,7 +257,7 @@ fn next_send_deadline_advances_by_interval() {
|
|||||||
let deadline2 = client.next_send_deadline().unwrap();
|
let deadline2 = client.next_send_deadline().unwrap();
|
||||||
assert_eq!(deadline2, start + Duration::from_secs(2));
|
assert_eq!(deadline2, start + Duration::from_secs(2));
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,7 +296,7 @@ fn send_probe_reports_schedule_overflow() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
client.session.as_mut().unwrap().packets_sent = u64::MAX - 1;
|
client.session.as_mut().unwrap().packets_sent = u64::MAX - 1;
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
@@ -311,30 +306,6 @@ fn send_probe_reports_schedule_overflow() {
|
|||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn send_probe_reports_logical_sequence_counter_overflow() {
|
|
||||||
let params = default_params();
|
|
||||||
let server = silent_open_server(params);
|
|
||||||
let config = ClientConfig {
|
|
||||||
socket_config: crate::SocketConfig {
|
|
||||||
recv_timeout: Some(Duration::from_millis(200)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..default_test_config(server.addr)
|
|
||||||
};
|
|
||||||
let mut client = Client::connect(config).unwrap();
|
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
|
||||||
client.session.as_mut().unwrap().next_logical_seq = u64::MAX;
|
|
||||||
|
|
||||||
assert!(matches!(
|
|
||||||
client.send_probe(),
|
|
||||||
Err(ClientError::CounterOverflow {
|
|
||||||
counter: "next_logical_seq"
|
|
||||||
})
|
|
||||||
));
|
|
||||||
server.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn send_probe_reports_packets_sent_counter_overflow() {
|
fn send_probe_reports_packets_sent_counter_overflow() {
|
||||||
let params = default_params();
|
let params = default_params();
|
||||||
@@ -347,7 +318,7 @@ fn send_probe_reports_packets_sent_counter_overflow() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
client.session.as_mut().unwrap().packets_sent = u64::MAX;
|
client.session.as_mut().unwrap().packets_sent = u64::MAX;
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
@@ -371,7 +342,7 @@ fn recv_once_returns_empty_on_timeout() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
let events = client.recv_once().unwrap();
|
let events = client.recv_once().unwrap();
|
||||||
assert!(events.is_empty());
|
assert!(events.is_empty());
|
||||||
server.join();
|
server.join();
|
||||||
@@ -390,7 +361,6 @@ fn recv_once_decodes_echo_reply_and_emits_event() {
|
|||||||
match &events[0] {
|
match &events[0] {
|
||||||
ClientEvent::EchoReply {
|
ClientEvent::EchoReply {
|
||||||
seq,
|
seq,
|
||||||
logical_seq,
|
|
||||||
rtt,
|
rtt,
|
||||||
received_stats,
|
received_stats,
|
||||||
server_timing,
|
server_timing,
|
||||||
@@ -399,7 +369,6 @@ fn recv_once_decodes_echo_reply_and_emits_event() {
|
|||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
assert_eq!(*seq, 0);
|
assert_eq!(*seq, 0);
|
||||||
assert_eq!(*logical_seq, 0);
|
|
||||||
assert_eq!(*bytes, echo_packet_len(false, ¶ms));
|
assert_eq!(*bytes, echo_packet_len(false, ¶ms));
|
||||||
assert!(rtt.raw > Duration::ZERO);
|
assert!(rtt.raw > Duration::ZERO);
|
||||||
assert_eq!(rtt.effective, rtt.adjusted.unwrap_or(rtt.raw));
|
assert_eq!(rtt.effective, rtt.adjusted.unwrap_or(rtt.raw));
|
||||||
@@ -431,7 +400,7 @@ fn recv_once_decodes_echo_reply_and_emits_event() {
|
|||||||
}
|
}
|
||||||
other => panic!("expected EchoReply, got {other:?}"),
|
other => panic!("expected EchoReply, got {other:?}"),
|
||||||
}
|
}
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,7 +446,7 @@ fn recv_once_decodes_only_received_bytes_after_longer_datagram() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
|
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
@@ -487,14 +456,8 @@ fn recv_once_decodes_only_received_bytes_after_longer_datagram() {
|
|||||||
let events = client.recv_once().unwrap();
|
let events = client.recv_once().unwrap();
|
||||||
assert_eq!(events.len(), 1);
|
assert_eq!(events.len(), 1);
|
||||||
match &events[0] {
|
match &events[0] {
|
||||||
ClientEvent::EchoReply {
|
ClientEvent::EchoReply { seq, bytes, .. } => {
|
||||||
seq,
|
|
||||||
logical_seq,
|
|
||||||
bytes,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
assert_eq!(*seq, 1);
|
assert_eq!(*seq, 1);
|
||||||
assert_eq!(*logical_seq, 1);
|
|
||||||
assert_eq!(*bytes, echo_packet_len(false, ¶ms));
|
assert_eq!(*bytes, echo_packet_len(false, ¶ms));
|
||||||
}
|
}
|
||||||
other => panic!("expected EchoReply after shorter datagram, got {other:?}"),
|
other => panic!("expected EchoReply after shorter datagram, got {other:?}"),
|
||||||
@@ -520,7 +483,7 @@ fn echo_reply_metadata_is_unavailable_without_ancillary() {
|
|||||||
other => panic!("expected EchoReply, got {other:?}"),
|
other => panic!("expected EchoReply, got {other:?}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -550,7 +513,7 @@ fn echo_reply_metadata_propagates_observed_dscp_with_ancillary() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
|
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
thread::sleep(Duration::from_millis(50));
|
thread::sleep(Duration::from_millis(50));
|
||||||
@@ -563,7 +526,7 @@ fn echo_reply_metadata_propagates_observed_dscp_with_ancillary() {
|
|||||||
metadata_unavailable_skip(
|
metadata_unavailable_skip(
|
||||||
"echo_reply_metadata_propagates_observed_dscp_with_ancillary",
|
"echo_reply_metadata_propagates_observed_dscp_with_ancillary",
|
||||||
);
|
);
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -574,7 +537,7 @@ fn echo_reply_metadata_propagates_observed_dscp_with_ancillary() {
|
|||||||
other => panic!("expected EchoReply, got {other:?}"),
|
other => panic!("expected EchoReply, got {other:?}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -595,7 +558,7 @@ fn echo_reply_metadata_preserves_observed_zero_with_ancillary() {
|
|||||||
metadata_unavailable_skip(
|
metadata_unavailable_skip(
|
||||||
"echo_reply_metadata_preserves_observed_zero_with_ancillary",
|
"echo_reply_metadata_preserves_observed_zero_with_ancillary",
|
||||||
);
|
);
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -606,7 +569,7 @@ fn echo_reply_metadata_preserves_observed_zero_with_ancillary() {
|
|||||||
other => panic!("expected EchoReply, got {other:?}"),
|
other => panic!("expected EchoReply, got {other:?}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -627,7 +590,7 @@ fn echo_reply_metadata_propagates_kernel_rx_timestamp_with_ancillary() {
|
|||||||
kernel_rx_timestamp_unavailable_skip(
|
kernel_rx_timestamp_unavailable_skip(
|
||||||
"echo_reply_metadata_propagates_kernel_rx_timestamp_with_ancillary",
|
"echo_reply_metadata_propagates_kernel_rx_timestamp_with_ancillary",
|
||||||
);
|
);
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -636,7 +599,7 @@ fn echo_reply_metadata_propagates_kernel_rx_timestamp_with_ancillary() {
|
|||||||
other => panic!("expected EchoReply, got {other:?}"),
|
other => panic!("expected EchoReply, got {other:?}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -654,7 +617,7 @@ fn echo_reply_rtt_uses_client_monotonic() {
|
|||||||
} else {
|
} else {
|
||||||
panic!("expected EchoReply");
|
panic!("expected EchoReply");
|
||||||
}
|
}
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -679,7 +642,7 @@ fn server_processing_subtracted_when_valid() {
|
|||||||
} else {
|
} else {
|
||||||
panic!("expected EchoReply");
|
panic!("expected EchoReply");
|
||||||
}
|
}
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -724,7 +687,7 @@ fn received_stats_parsed_into_sample() {
|
|||||||
} else {
|
} else {
|
||||||
panic!("expected EchoReply");
|
panic!("expected EchoReply");
|
||||||
}
|
}
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -757,7 +720,7 @@ fn wrong_token_reply_is_dropped() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
thread::sleep(Duration::from_millis(30));
|
thread::sleep(Duration::from_millis(30));
|
||||||
let events = client.recv_once().unwrap();
|
let events = client.recv_once().unwrap();
|
||||||
@@ -796,7 +759,7 @@ fn duplicate_reply_emits_duplicate_event() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
thread::sleep(Duration::from_millis(50));
|
thread::sleep(Duration::from_millis(50));
|
||||||
|
|
||||||
@@ -850,7 +813,7 @@ fn out_of_order_reply_emits_late_event() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
thread::sleep(Duration::from_millis(50));
|
thread::sleep(Duration::from_millis(50));
|
||||||
@@ -891,16 +854,16 @@ fn poll_timeouts_emits_echo_loss() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
|
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
|
|
||||||
let no_loss = client.poll_timeouts(ClientTimestamp::now()).unwrap();
|
let no_loss = client.poll_timeouts().unwrap();
|
||||||
assert!(no_loss.is_empty());
|
assert!(no_loss.is_empty());
|
||||||
|
|
||||||
thread::sleep(Duration::from_millis(150));
|
thread::sleep(Duration::from_millis(150));
|
||||||
let events = client.poll_timeouts(ClientTimestamp::now()).unwrap();
|
let events = client.poll_timeouts().unwrap();
|
||||||
assert_eq!(events.len(), 2);
|
assert_eq!(events.len(), 2);
|
||||||
for event in &events {
|
for event in &events {
|
||||||
assert!(matches!(event, ClientEvent::EchoLoss { .. }));
|
assert!(matches!(event, ClientEvent::EchoLoss { .. }));
|
||||||
@@ -921,11 +884,11 @@ fn poll_timeouts_removes_expired_pending() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
|
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
thread::sleep(Duration::from_millis(150));
|
thread::sleep(Duration::from_millis(150));
|
||||||
client.poll_timeouts(ClientTimestamp::now()).unwrap();
|
client.poll_timeouts().unwrap();
|
||||||
|
|
||||||
let session = client.session.as_ref().unwrap();
|
let session = client.session.as_ref().unwrap();
|
||||||
assert_eq!(session.pending.len(), 0);
|
assert_eq!(session.pending.len(), 0);
|
||||||
@@ -967,11 +930,11 @@ fn late_reply_after_timeout_preserves_measurement_metadata() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
|
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
thread::sleep(Duration::from_millis(60));
|
thread::sleep(Duration::from_millis(60));
|
||||||
let losses = client.poll_timeouts(ClientTimestamp::now()).unwrap();
|
let losses = client.poll_timeouts().unwrap();
|
||||||
assert!(matches!(&losses[0], ClientEvent::EchoLoss { seq: 0, .. }));
|
assert!(matches!(&losses[0], ClientEvent::EchoLoss { seq: 0, .. }));
|
||||||
assert_eq!(client.session.as_ref().unwrap().pending.len(), 0);
|
assert_eq!(client.session.as_ref().unwrap().pending.len(), 0);
|
||||||
assert_eq!(client.session.as_ref().unwrap().timed_out.len(), 1);
|
assert_eq!(client.session.as_ref().unwrap().timed_out.len(), 1);
|
||||||
@@ -980,7 +943,6 @@ fn late_reply_after_timeout_preserves_measurement_metadata() {
|
|||||||
match &late[0] {
|
match &late[0] {
|
||||||
ClientEvent::LateReply {
|
ClientEvent::LateReply {
|
||||||
seq,
|
seq,
|
||||||
logical_seq,
|
|
||||||
sent_at,
|
sent_at,
|
||||||
rtt,
|
rtt,
|
||||||
server_timing,
|
server_timing,
|
||||||
@@ -990,7 +952,6 @@ fn late_reply_after_timeout_preserves_measurement_metadata() {
|
|||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
assert_eq!(*seq, 0);
|
assert_eq!(*seq, 0);
|
||||||
assert_eq!(*logical_seq, Some(0));
|
|
||||||
assert!(sent_at.is_some());
|
assert!(sent_at.is_some());
|
||||||
assert!(rtt.is_some());
|
assert!(rtt.is_some());
|
||||||
assert!(server_timing.is_some());
|
assert!(server_timing.is_some());
|
||||||
@@ -1014,7 +975,7 @@ fn late_reply_after_timeout_preserves_measurement_metadata() {
|
|||||||
} if *bytes == echo_packet_len(false, &default_params())
|
} if *bytes == echo_packet_len(false, &default_params())
|
||||||
));
|
));
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1046,11 +1007,11 @@ fn late_reply_metadata_is_unavailable_without_ancillary() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
|
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
thread::sleep(Duration::from_millis(60));
|
thread::sleep(Duration::from_millis(60));
|
||||||
let losses = client.poll_timeouts(ClientTimestamp::now()).unwrap();
|
let losses = client.poll_timeouts().unwrap();
|
||||||
assert!(matches!(&losses[0], ClientEvent::EchoLoss { seq: 0, .. }));
|
assert!(matches!(&losses[0], ClientEvent::EchoLoss { seq: 0, .. }));
|
||||||
|
|
||||||
let late = client.recv_once().unwrap();
|
let late = client.recv_once().unwrap();
|
||||||
@@ -1062,7 +1023,7 @@ fn late_reply_metadata_is_unavailable_without_ancillary() {
|
|||||||
other => panic!("expected LateReply, got {other:?}"),
|
other => panic!("expected LateReply, got {other:?}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1095,11 +1056,11 @@ fn late_reply_metadata_propagates_observed_dscp_with_ancillary() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
|
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
thread::sleep(Duration::from_millis(60));
|
thread::sleep(Duration::from_millis(60));
|
||||||
let losses = client.poll_timeouts(ClientTimestamp::now()).unwrap();
|
let losses = client.poll_timeouts().unwrap();
|
||||||
assert!(matches!(&losses[0], ClientEvent::EchoLoss { seq: 0, .. }));
|
assert!(matches!(&losses[0], ClientEvent::EchoLoss { seq: 0, .. }));
|
||||||
|
|
||||||
let events = client.recv_once().unwrap();
|
let events = client.recv_once().unwrap();
|
||||||
@@ -1110,7 +1071,7 @@ fn late_reply_metadata_propagates_observed_dscp_with_ancillary() {
|
|||||||
metadata_unavailable_skip(
|
metadata_unavailable_skip(
|
||||||
"late_reply_metadata_propagates_observed_dscp_with_ancillary",
|
"late_reply_metadata_propagates_observed_dscp_with_ancillary",
|
||||||
);
|
);
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -1128,7 +1089,7 @@ fn late_reply_metadata_propagates_observed_dscp_with_ancillary() {
|
|||||||
other => panic!("expected LateReply, got {other:?}"),
|
other => panic!("expected LateReply, got {other:?}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1149,7 +1110,7 @@ fn pending_map_bounded() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
@@ -1157,7 +1118,7 @@ fn pending_map_bounded() {
|
|||||||
client.send_probe(),
|
client.send_probe(),
|
||||||
Err(ClientError::PendingLimitExceeded { limit: 3 })
|
Err(ClientError::PendingLimitExceeded { limit: 3 })
|
||||||
));
|
));
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1199,7 +1160,7 @@ fn minimal_negotiated_layout_works() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
thread::sleep(Duration::from_millis(30));
|
thread::sleep(Duration::from_millis(30));
|
||||||
let events = client.recv_once().unwrap();
|
let events = client.recv_once().unwrap();
|
||||||
@@ -1253,7 +1214,7 @@ fn late_reply_with_pending_preserves_rtt() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
@@ -1296,7 +1257,7 @@ fn pending_full_does_not_send_packet() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
|
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
@@ -1326,7 +1287,7 @@ fn pending_full_does_not_send_packet() {
|
|||||||
"no packet should be sent when pending is full"
|
"no packet should be sent when pending is full"
|
||||||
);
|
);
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1361,7 +1322,7 @@ fn unmatched_future_reply_emits_warning_not_late() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
thread::sleep(Duration::from_millis(50));
|
thread::sleep(Duration::from_millis(50));
|
||||||
|
|
||||||
@@ -1419,7 +1380,7 @@ fn unmatched_future_reply_does_not_update_highest_received_seq() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
thread::sleep(Duration::from_millis(50));
|
thread::sleep(Duration::from_millis(50));
|
||||||
@@ -1523,7 +1484,7 @@ fn matched_reply_with_reversed_monotonic_time_still_emits_event() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
|
|
||||||
let base = Instant::now() + Duration::from_secs(1);
|
let base = Instant::now() + Duration::from_secs(1);
|
||||||
let send_ts = ClientTimestamp {
|
let send_ts = ClientTimestamp {
|
||||||
@@ -1550,7 +1511,7 @@ fn matched_reply_with_reversed_monotonic_time_still_emits_event() {
|
|||||||
other => panic!("expected EchoReply, got {other:?}"),
|
other => panic!("expected EchoReply, got {other:?}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1566,7 +1527,7 @@ fn process_received_packet_uses_supplied_receive_metadata() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
|
|
||||||
client.send_probe_at(ClientTimestamp::now()).unwrap();
|
client.send_probe_at(ClientTimestamp::now()).unwrap();
|
||||||
let recv_ts = ClientTimestamp::now();
|
let recv_ts = ClientTimestamp::now();
|
||||||
@@ -1596,7 +1557,7 @@ fn process_received_packet_uses_supplied_receive_metadata() {
|
|||||||
other => panic!("expected EchoReply, got {other:?}"),
|
other => panic!("expected EchoReply, got {other:?}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1613,11 +1574,11 @@ fn process_received_packet_uses_supplied_receive_metadata_for_late_reply() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
|
|
||||||
client.send_probe_at(ClientTimestamp::now()).unwrap();
|
client.send_probe_at(ClientTimestamp::now()).unwrap();
|
||||||
thread::sleep(Duration::from_millis(60));
|
thread::sleep(Duration::from_millis(60));
|
||||||
let losses = client.poll_timeouts(ClientTimestamp::now()).unwrap();
|
let losses = client.poll_timeouts().unwrap();
|
||||||
assert!(matches!(&losses[0], ClientEvent::EchoLoss { seq: 0, .. }));
|
assert!(matches!(&losses[0], ClientEvent::EchoLoss { seq: 0, .. }));
|
||||||
|
|
||||||
let recv_ts = ClientTimestamp::now();
|
let recv_ts = ClientTimestamp::now();
|
||||||
@@ -1647,7 +1608,7 @@ fn process_received_packet_uses_supplied_receive_metadata_for_late_reply() {
|
|||||||
other => panic!("expected LateReply, got {other:?}"),
|
other => panic!("expected LateReply, got {other:?}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1663,7 +1624,7 @@ fn receive_metadata_does_not_broaden_malformed_warning() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
|
|
||||||
let events = client
|
let events = client
|
||||||
.process_received_packet(
|
.process_received_packet(
|
||||||
@@ -1678,14 +1639,14 @@ fn receive_metadata_does_not_broaden_malformed_warning() {
|
|||||||
|
|
||||||
assert_eq!(events.len(), 1);
|
assert_eq!(events.len(), 1);
|
||||||
match &events[0] {
|
match &events[0] {
|
||||||
ClientEvent::Warning { kind, message } => {
|
ClientEvent::Warning { kind, message, .. } => {
|
||||||
assert_eq!(*kind, WarningKind::MalformedOrUnrelatedPacket);
|
assert_eq!(*kind, WarningKind::MalformedOrUnrelatedPacket);
|
||||||
assert_eq!(message, "dropped malformed or unrelated packet");
|
assert_eq!(message, "dropped malformed or unrelated packet");
|
||||||
}
|
}
|
||||||
other => panic!("expected Warning, got {other:?}"),
|
other => panic!("expected Warning, got {other:?}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1713,7 +1674,7 @@ fn recv_available_drains_burst_replies() {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1738,7 +1699,7 @@ fn recv_available_respects_packet_budget() {
|
|||||||
assert_eq!(second.len(), 1);
|
assert_eq!(second.len(), 1);
|
||||||
assert!(matches!(&second[0], ClientEvent::EchoReply { seq: 1, .. }));
|
assert!(matches!(&second[0], ClientEvent::EchoReply { seq: 1, .. }));
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1754,11 +1715,10 @@ fn send_probe_wraps_wire_sequence_at_u32_max() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
|
|
||||||
let session = client.session.as_mut().unwrap();
|
let session = client.session.as_mut().unwrap();
|
||||||
session.next_wire_seq = u32::MAX;
|
session.next_wire_seq = u32::MAX;
|
||||||
session.next_logical_seq = 41;
|
|
||||||
|
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
@@ -1772,7 +1732,7 @@ fn send_probe_wraps_wire_sequence_at_u32_max() {
|
|||||||
.collect();
|
.collect();
|
||||||
assert_eq!(seqs, vec![u32::MAX, 0]);
|
assert_eq!(seqs, vec![u32::MAX, 0]);
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1811,10 +1771,9 @@ fn wrapped_reply_after_u32_max_is_not_late() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
let session = client.session.as_mut().unwrap();
|
let session = client.session.as_mut().unwrap();
|
||||||
session.next_wire_seq = u32::MAX;
|
session.next_wire_seq = u32::MAX;
|
||||||
session.next_logical_seq = 41;
|
|
||||||
|
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
@@ -1848,7 +1807,7 @@ fn send_probe_after_sending_done_is_noop() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
|
|
||||||
client.session.as_mut().unwrap().sending_done = true;
|
client.session.as_mut().unwrap().sending_done = true;
|
||||||
let events = client.send_probe().unwrap();
|
let events = client.send_probe().unwrap();
|
||||||
@@ -1863,7 +1822,7 @@ fn send_probe_after_sending_done_is_noop() {
|
|||||||
.count();
|
.count();
|
||||||
assert_eq!(echo_count, 0);
|
assert_eq!(echo_count, 0);
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1926,7 +1885,7 @@ fn recv_buffer_uses_negotiated_packet_length() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
let buf_size = client.recv_buffer_size();
|
let buf_size = client.recv_buffer_size();
|
||||||
assert!(
|
assert!(
|
||||||
buf_size > 4096,
|
buf_size > 4096,
|
||||||
@@ -1934,7 +1893,7 @@ fn recv_buffer_uses_negotiated_packet_length() {
|
|||||||
);
|
);
|
||||||
assert_eq!(buf_size, 4097);
|
assert_eq!(buf_size, 4097);
|
||||||
assert_eq!(client.recv_buffer.len(), buf_size);
|
assert_eq!(client.recv_buffer.len(), buf_size);
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1954,7 +1913,7 @@ fn short_echo_reply_does_not_emit_echo_reply() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
|
|
||||||
let mut packet = echo_reply_packet(TOKEN, 0, ¶ms, &TimestampFields::default(), None);
|
let mut packet = echo_reply_packet(TOKEN, 0, ¶ms, &TimestampFields::default(), None);
|
||||||
@@ -1970,7 +1929,7 @@ fn short_echo_reply_does_not_emit_echo_reply() {
|
|||||||
..
|
..
|
||||||
}]
|
}]
|
||||||
));
|
));
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1986,7 +1945,7 @@ fn overlong_echo_reply_does_not_emit_echo_reply() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
|
|
||||||
let mut packet = echo_reply_packet(TOKEN, 0, ¶ms, &TimestampFields::default(), None);
|
let mut packet = echo_reply_packet(TOKEN, 0, ¶ms, &TimestampFields::default(), None);
|
||||||
@@ -2002,7 +1961,7 @@ fn overlong_echo_reply_does_not_emit_echo_reply() {
|
|||||||
..
|
..
|
||||||
}]
|
}]
|
||||||
));
|
));
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2036,7 +1995,7 @@ fn overlong_datagram_detection_uses_extra_receive_byte() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client.recv_buffer.len(),
|
client.recv_buffer.len(),
|
||||||
echo_packet_len(false, ¶ms) + 1
|
echo_packet_len(false, ¶ms) + 1
|
||||||
@@ -2051,7 +2010,7 @@ fn overlong_datagram_detection_uses_extra_receive_byte() {
|
|||||||
..
|
..
|
||||||
}]
|
}]
|
||||||
));
|
));
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2083,7 +2042,7 @@ fn exact_length_echo_reply_still_emits_echo_reply() {
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
|
|
||||||
let events = client.recv_once().unwrap();
|
let events = client.recv_once().unwrap();
|
||||||
@@ -2091,7 +2050,7 @@ fn exact_length_echo_reply_still_emits_echo_reply() {
|
|||||||
events.as_slice(),
|
events.as_slice(),
|
||||||
[ClientEvent::EchoReply { bytes, .. }] if *bytes == echo_packet_len(false, ¶ms)
|
[ClientEvent::EchoReply { bytes, .. }] if *bytes == echo_packet_len(false, ¶ms)
|
||||||
));
|
));
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2113,6 +2072,6 @@ fn recv_once_at_test_helper_provides_deterministic_timestamp() {
|
|||||||
} else {
|
} else {
|
||||||
panic!("expected EchoReply");
|
panic!("expected EchoReply");
|
||||||
}
|
}
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
@@ -26,7 +26,6 @@ fn sequence_ordering_handles_normal_and_wrapped_values() {
|
|||||||
fn highest_received_seq_updates_across_wrap() {
|
fn highest_received_seq_updates_across_wrap() {
|
||||||
let mut session = ActiveSession {
|
let mut session = ActiveSession {
|
||||||
next_wire_seq: 0,
|
next_wire_seq: 0,
|
||||||
next_logical_seq: 0,
|
|
||||||
highest_received_seq: Some(u32::MAX),
|
highest_received_seq: Some(u32::MAX),
|
||||||
packets_sent: 0,
|
packets_sent: 0,
|
||||||
start_mono: Instant::now(),
|
start_mono: Instant::now(),
|
||||||
|
|||||||
@@ -283,7 +283,7 @@ pub(super) fn open_client_with_echo_server(params: &Params) -> (Client, FakeServ
|
|||||||
..default_test_config(server.addr)
|
..default_test_config(server.addr)
|
||||||
};
|
};
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert_open_started(client.open(ClientTimestamp::now()).unwrap());
|
assert_open_started(client.open().unwrap());
|
||||||
(client, server)
|
(client, server)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,10 +29,10 @@ fn connect_applies_configured_ipv4_ttl_before_open_and_close_preserves_it() {
|
|||||||
|
|
||||||
assert_eq!(socket_ttl(&client.socket, client.remote).unwrap(), 64);
|
assert_eq!(socket_ttl(&client.socket, client.remote).unwrap(), 64);
|
||||||
|
|
||||||
client.open(ClientTimestamp::now()).unwrap();
|
client.open().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
assert_eq!(socket_ttl(&client.socket, client.remote).unwrap(), 64);
|
assert_eq!(socket_ttl(&client.socket, client.remote).unwrap(), 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use std::{io, time::Duration};
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum ClientError {
|
pub enum ClientError {
|
||||||
#[error("failed to resolve server address {addr:?}")]
|
#[error("failed to resolve server address {addr:?}")]
|
||||||
Resolve { addr: String },
|
Resolve { addr: String },
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ pub enum ClientEvent {
|
|||||||
|
|
||||||
EchoSent {
|
EchoSent {
|
||||||
seq: u32,
|
seq: u32,
|
||||||
logical_seq: u64,
|
|
||||||
remote: SocketAddr,
|
remote: SocketAddr,
|
||||||
scheduled_at: Instant,
|
scheduled_at: Instant,
|
||||||
sent_at: ClientTimestamp,
|
sent_at: ClientTimestamp,
|
||||||
@@ -52,7 +51,6 @@ pub enum ClientEvent {
|
|||||||
|
|
||||||
EchoReply {
|
EchoReply {
|
||||||
seq: u32,
|
seq: u32,
|
||||||
logical_seq: u64,
|
|
||||||
remote: SocketAddr,
|
remote: SocketAddr,
|
||||||
sent_at: ClientTimestamp,
|
sent_at: ClientTimestamp,
|
||||||
received_at: ClientTimestamp,
|
received_at: ClientTimestamp,
|
||||||
@@ -66,7 +64,6 @@ pub enum ClientEvent {
|
|||||||
|
|
||||||
EchoLoss {
|
EchoLoss {
|
||||||
seq: u32,
|
seq: u32,
|
||||||
logical_seq: u64,
|
|
||||||
sent_at: ClientTimestamp,
|
sent_at: ClientTimestamp,
|
||||||
timeout_at: std::time::Instant,
|
timeout_at: std::time::Instant,
|
||||||
},
|
},
|
||||||
@@ -80,7 +77,6 @@ pub enum ClientEvent {
|
|||||||
|
|
||||||
LateReply {
|
LateReply {
|
||||||
seq: u32,
|
seq: u32,
|
||||||
logical_seq: Option<u64>,
|
|
||||||
highest_seen: u32,
|
highest_seen: u32,
|
||||||
remote: SocketAddr,
|
remote: SocketAddr,
|
||||||
sent_at: Option<ClientTimestamp>,
|
sent_at: Option<ClientTimestamp>,
|
||||||
@@ -96,10 +92,12 @@ pub enum ClientEvent {
|
|||||||
Warning {
|
Warning {
|
||||||
kind: WarningKind,
|
kind: WarningKind,
|
||||||
message: String,
|
message: String,
|
||||||
|
at: ClientTimestamp,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum WarningKind {
|
pub enum WarningKind {
|
||||||
MalformedOrUnrelatedPacket,
|
MalformedOrUnrelatedPacket,
|
||||||
WrongToken,
|
WrongToken,
|
||||||
|
|||||||
@@ -279,6 +279,7 @@ mod tests {
|
|||||||
ClientEvent::Warning {
|
ClientEvent::Warning {
|
||||||
kind: WarningKind::UntrackedReply,
|
kind: WarningKind::UntrackedReply,
|
||||||
message: format!("event-{n}"),
|
message: format!("event-{n}"),
|
||||||
|
at: ClientTimestamp::now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ use crate::{
|
|||||||
config::{ClientConfig, RecvBudget},
|
config::{ClientConfig, RecvBudget},
|
||||||
error::ClientError,
|
error::ClientError,
|
||||||
event::{ClientEvent, OpenOutcome},
|
event::{ClientEvent, OpenOutcome},
|
||||||
timing::ClientTimestamp,
|
|
||||||
Client,
|
Client,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -94,7 +93,7 @@ impl ManagedClient {
|
|||||||
.transpose()?;
|
.transpose()?;
|
||||||
|
|
||||||
let mut client = Client::connect(config)?;
|
let mut client = Client::connect(config)?;
|
||||||
let outcome = client.open(ClientTimestamp::now())?;
|
let outcome = client.open()?;
|
||||||
publish_open_outcome(&hub, &outcome);
|
publish_open_outcome(&hub, &outcome);
|
||||||
|
|
||||||
let cancellation = CancellationToken::new();
|
let cancellation = CancellationToken::new();
|
||||||
@@ -182,11 +181,7 @@ fn run_client(
|
|||||||
&mut counters,
|
&mut counters,
|
||||||
client.recv_available(MANAGED_RECV_BUDGET)?,
|
client.recv_available(MANAGED_RECV_BUDGET)?,
|
||||||
);
|
);
|
||||||
publish_events(
|
publish_events(&hub, &mut counters, client.poll_timeouts()?);
|
||||||
&hub,
|
|
||||||
&mut counters,
|
|
||||||
client.poll_timeouts(ClientTimestamp::now())?,
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,11 +200,7 @@ fn run_client(
|
|||||||
&mut counters,
|
&mut counters,
|
||||||
client.recv_available(MANAGED_RECV_BUDGET)?,
|
client.recv_available(MANAGED_RECV_BUDGET)?,
|
||||||
);
|
);
|
||||||
publish_events(
|
publish_events(&hub, &mut counters, client.poll_timeouts()?);
|
||||||
&hub,
|
|
||||||
&mut counters,
|
|
||||||
client.poll_timeouts(ClientTimestamp::now())?,
|
|
||||||
);
|
|
||||||
|
|
||||||
if client.is_run_complete() {
|
if client.is_run_complete() {
|
||||||
break;
|
break;
|
||||||
@@ -223,7 +214,7 @@ fn run_client(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let packets_sent = client.packets_sent();
|
let packets_sent = client.packets_sent();
|
||||||
let close_events = client.close(ClientTimestamp::now())?;
|
let close_events = client.close()?;
|
||||||
publish_events(&hub, &mut counters, close_events);
|
publish_events(&hub, &mut counters, close_events);
|
||||||
|
|
||||||
Ok(SessionOutcome {
|
Ok(SessionOutcome {
|
||||||
@@ -267,7 +258,7 @@ fn drain_final_late_replies(
|
|||||||
published |= !events.is_empty();
|
published |= !events.is_empty();
|
||||||
publish_events(hub, counters, events);
|
publish_events(hub, counters, events);
|
||||||
|
|
||||||
let events = client.poll_timeouts(ClientTimestamp::now())?;
|
let events = client.poll_timeouts()?;
|
||||||
published |= !events.is_empty();
|
published |= !events.is_empty();
|
||||||
publish_events(hub, counters, events);
|
publish_events(hub, counters, events);
|
||||||
|
|
||||||
@@ -603,19 +594,13 @@ mod tests {
|
|||||||
server.join();
|
server.join();
|
||||||
|
|
||||||
assert_eq!(outcome.end_reason, SessionEndReason::TestComplete);
|
assert_eq!(outcome.end_reason, SessionEndReason::TestComplete);
|
||||||
assert!(events.iter().any(|event| matches!(
|
assert!(events
|
||||||
event,
|
.iter()
|
||||||
ClientEvent::EchoLoss {
|
.any(|event| matches!(event, ClientEvent::EchoLoss { seq: 0, .. })));
|
||||||
seq: 0,
|
|
||||||
logical_seq: 0,
|
|
||||||
..
|
|
||||||
}
|
|
||||||
)));
|
|
||||||
assert!(events.iter().any(|event| matches!(
|
assert!(events.iter().any(|event| matches!(
|
||||||
event,
|
event,
|
||||||
ClientEvent::LateReply {
|
ClientEvent::LateReply {
|
||||||
seq: 0,
|
seq: 0,
|
||||||
logical_seq: Some(0),
|
|
||||||
sent_at: Some(_),
|
sent_at: Some(_),
|
||||||
rtt: Some(_),
|
rtt: Some(_),
|
||||||
..
|
..
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, VecDeque},
|
collections::{HashMap, HashSet, VecDeque},
|
||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -7,7 +7,6 @@ use crate::{error::ClientError, timing::ClientTimestamp};
|
|||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct PendingProbe {
|
pub(crate) struct PendingProbe {
|
||||||
pub logical_seq: u64,
|
|
||||||
pub wire_seq: u32,
|
pub wire_seq: u32,
|
||||||
pub sent_at: ClientTimestamp,
|
pub sent_at: ClientTimestamp,
|
||||||
pub timeout_at: Instant,
|
pub timeout_at: Instant,
|
||||||
@@ -59,11 +58,10 @@ impl PendingMap {
|
|||||||
expired.push(probe);
|
expired.push(probe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
expired.sort_by_key(|p| p.logical_seq);
|
expired.sort_by_key(|p| p.sent_at.mono);
|
||||||
expired
|
expired
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.map.len()
|
self.map.len()
|
||||||
}
|
}
|
||||||
@@ -115,7 +113,6 @@ impl TimedOutMap {
|
|||||||
self.insertion_order.clear();
|
self.insertion_order.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.map.len()
|
self.map.len()
|
||||||
}
|
}
|
||||||
@@ -136,7 +133,7 @@ impl TimedOutMap {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct CompletedSet {
|
pub(crate) struct CompletedSet {
|
||||||
set: HashMap<u32, u64>,
|
set: HashSet<u32>,
|
||||||
insertion_order: VecDeque<u32>,
|
insertion_order: VecDeque<u32>,
|
||||||
max_capacity: usize,
|
max_capacity: usize,
|
||||||
}
|
}
|
||||||
@@ -144,31 +141,32 @@ pub(crate) struct CompletedSet {
|
|||||||
impl CompletedSet {
|
impl CompletedSet {
|
||||||
pub fn new(max_capacity: usize) -> Self {
|
pub fn new(max_capacity: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
set: HashMap::new(),
|
set: HashSet::new(),
|
||||||
insertion_order: VecDeque::new(),
|
insertion_order: VecDeque::new(),
|
||||||
max_capacity,
|
max_capacity,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(&mut self, wire_seq: u32, logical_seq: u64) {
|
pub fn insert(&mut self, seq: u32) {
|
||||||
if let Some(existing) = self.set.get_mut(&wire_seq) {
|
if self.set.contains(&seq) {
|
||||||
*existing = logical_seq;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.set.len() >= self.max_capacity {
|
if self.set.len() >= self.max_capacity {
|
||||||
self.evict_oldest();
|
self.evict_oldest();
|
||||||
}
|
}
|
||||||
self.insertion_order.push_back(wire_seq);
|
|
||||||
self.set.insert(wire_seq, logical_seq);
|
self.insertion_order.push_back(seq);
|
||||||
|
self.set.insert(seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contains(&self, wire_seq: u32) -> bool {
|
pub fn contains(&self, seq: u32) -> bool {
|
||||||
self.set.contains_key(&wire_seq)
|
self.set.contains(&seq)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn evict_oldest(&mut self) {
|
fn evict_oldest(&mut self) {
|
||||||
while let Some(oldest_key) = self.insertion_order.pop_front() {
|
while let Some(oldest_seq) = self.insertion_order.pop_front() {
|
||||||
if self.set.remove(&oldest_key).is_some() {
|
if self.set.remove(&oldest_seq) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,9 +185,8 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pending(seq: u32, logical: u64, timeout_at: Instant) -> PendingProbe {
|
fn pending(seq: u32, timeout_at: Instant) -> PendingProbe {
|
||||||
PendingProbe {
|
PendingProbe {
|
||||||
logical_seq: logical,
|
|
||||||
wire_seq: seq,
|
wire_seq: seq,
|
||||||
sent_at: ts(timeout_at - Duration::from_secs(1)),
|
sent_at: ts(timeout_at - Duration::from_secs(1)),
|
||||||
timeout_at,
|
timeout_at,
|
||||||
@@ -200,7 +197,7 @@ mod tests {
|
|||||||
fn pending_map_insert_and_remove() {
|
fn pending_map_insert_and_remove() {
|
||||||
let mut map = PendingMap::new(10);
|
let mut map = PendingMap::new(10);
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let probe = pending(0, 0, now + Duration::from_secs(4));
|
let probe = pending(0, now + Duration::from_secs(4));
|
||||||
map.insert(probe).unwrap();
|
map.insert(probe).unwrap();
|
||||||
assert_eq!(map.len(), 1);
|
assert_eq!(map.len(), 1);
|
||||||
assert!(map.remove(0).is_some());
|
assert!(map.remove(0).is_some());
|
||||||
@@ -212,42 +209,26 @@ mod tests {
|
|||||||
fn pending_map_rejects_over_capacity() {
|
fn pending_map_rejects_over_capacity() {
|
||||||
let mut map = PendingMap::new(2);
|
let mut map = PendingMap::new(2);
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
map.insert(pending(0, 0, now + Duration::from_secs(4)))
|
map.insert(pending(0, now + Duration::from_secs(4)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
map.insert(pending(1, 1, now + Duration::from_secs(4)))
|
map.insert(pending(1, now + Duration::from_secs(4)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
map.insert(pending(2, 2, now + Duration::from_secs(4))),
|
map.insert(pending(2, now + Duration::from_secs(4))),
|
||||||
Err(ClientError::PendingLimitExceeded { limit: 2 })
|
Err(ClientError::PendingLimitExceeded { limit: 2 })
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn drain_expired_returns_in_logical_order() {
|
|
||||||
let mut map = PendingMap::new(10);
|
|
||||||
let now = Instant::now();
|
|
||||||
let past = now - Duration::from_secs(1);
|
|
||||||
map.insert(pending(5, 5, past)).unwrap();
|
|
||||||
map.insert(pending(2, 2, past)).unwrap();
|
|
||||||
map.insert(pending(8, 8, now + Duration::from_secs(10)))
|
|
||||||
.unwrap();
|
|
||||||
let expired = map.drain_expired(now);
|
|
||||||
assert_eq!(expired.len(), 2);
|
|
||||||
assert_eq!(expired[0].wire_seq, 2);
|
|
||||||
assert_eq!(expired[1].wire_seq, 5);
|
|
||||||
assert_eq!(map.len(), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completed_set_tracks_and_evicts() {
|
fn completed_set_tracks_and_evicts() {
|
||||||
let mut set = CompletedSet::new(3);
|
let mut set = CompletedSet::new(3);
|
||||||
set.insert(0, 0);
|
set.insert(0);
|
||||||
set.insert(1, 1);
|
set.insert(1);
|
||||||
set.insert(2, 2);
|
set.insert(2);
|
||||||
assert!(set.contains(0));
|
assert!(set.contains(0));
|
||||||
assert!(set.contains(1));
|
assert!(set.contains(1));
|
||||||
assert!(set.contains(2));
|
assert!(set.contains(2));
|
||||||
set.insert(3, 3);
|
set.insert(3);
|
||||||
assert_eq!(set.set.len(), 3);
|
assert_eq!(set.set.len(), 3);
|
||||||
assert!(!set.contains(0));
|
assert!(!set.contains(0));
|
||||||
assert!(set.contains(3));
|
assert!(set.contains(3));
|
||||||
@@ -257,9 +238,9 @@ mod tests {
|
|||||||
fn timed_out_map_tracks_and_evicts() {
|
fn timed_out_map_tracks_and_evicts() {
|
||||||
let mut map = TimedOutMap::new(2);
|
let mut map = TimedOutMap::new(2);
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
map.insert(pending(0, 0, now));
|
map.insert(pending(0, now));
|
||||||
map.insert(pending(1, 1, now));
|
map.insert(pending(1, now));
|
||||||
map.insert(pending(2, 2, now));
|
map.insert(pending(2, now));
|
||||||
|
|
||||||
assert_eq!(map.len(), 2);
|
assert_eq!(map.len(), 2);
|
||||||
assert!(map.remove(0).is_none());
|
assert!(map.remove(0).is_none());
|
||||||
@@ -273,7 +254,7 @@ mod tests {
|
|||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
|
|
||||||
for i in 0..20 {
|
for i in 0..20 {
|
||||||
map.insert(pending(i, u64::from(i), now));
|
map.insert(pending(i, now));
|
||||||
assert!(map.remove(i).is_some());
|
assert!(map.remove(i).is_some());
|
||||||
assert_eq!(map.len(), 0);
|
assert_eq!(map.len(), 0);
|
||||||
assert_eq!(map.insertion_order_len(), 0);
|
assert_eq!(map.insertion_order_len(), 0);
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ pub(crate) enum ClientPhase {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct ActiveSession {
|
pub(crate) struct ActiveSession {
|
||||||
pub next_wire_seq: u32,
|
pub next_wire_seq: u32,
|
||||||
pub next_logical_seq: u64,
|
|
||||||
pub highest_received_seq: Option<u32>,
|
pub highest_received_seq: Option<u32>,
|
||||||
pub packets_sent: u64,
|
pub packets_sent: u64,
|
||||||
pub start_mono: Instant,
|
pub start_mono: Instant,
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ mod support;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use irtt_client::{
|
use irtt_client::{
|
||||||
Client, ClientConfig, ClientError, ClientEvent, ClientTimestamp, OpenOutcome, SocketConfig,
|
Client, ClientConfig, ClientError, ClientEvent, OpenOutcome, SocketConfig, WarningKind,
|
||||||
WarningKind,
|
|
||||||
};
|
};
|
||||||
use irtt_proto::{echo_packet_len, ProtoError, TimestampFields};
|
use irtt_proto::{echo_packet_len, ProtoError, TimestampFields};
|
||||||
|
|
||||||
@@ -74,13 +73,11 @@ fn hmac_echo_success_verifies_request_and_accepts_reply() {
|
|||||||
match &run.reply {
|
match &run.reply {
|
||||||
ClientEvent::EchoReply {
|
ClientEvent::EchoReply {
|
||||||
seq,
|
seq,
|
||||||
logical_seq,
|
|
||||||
bytes,
|
bytes,
|
||||||
server_timing,
|
server_timing,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
assert_eq!(*seq, 0);
|
assert_eq!(*seq, 0);
|
||||||
assert_eq!(*logical_seq, 0);
|
|
||||||
assert_eq!(*bytes, echo_packet_len(true, ¶ms));
|
assert_eq!(*bytes, echo_packet_len(true, ¶ms));
|
||||||
assert!(server_timing.is_some());
|
assert!(server_timing.is_some());
|
||||||
}
|
}
|
||||||
@@ -98,10 +95,10 @@ fn hmac_close_success_sends_authenticated_close_and_closes_session() {
|
|||||||
config.hmac_key = Some(key);
|
config.hmac_key = Some(key);
|
||||||
|
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
let outcome = client.open(ClientTimestamp::now()).unwrap();
|
let outcome = client.open().unwrap();
|
||||||
assert_started(outcome, ¶ms);
|
assert_started(outcome, ¶ms);
|
||||||
|
|
||||||
let events = client.close(ClientTimestamp::now()).unwrap();
|
let events = client.close().unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
events.as_slice(),
|
events.as_slice(),
|
||||||
[ClientEvent::SessionClosed { token: TOKEN, .. }]
|
[ClientEvent::SessionClosed { token: TOKEN, .. }]
|
||||||
@@ -131,10 +128,7 @@ fn missing_client_key_against_hmac_required_server_times_out() {
|
|||||||
config.hmac_key = None;
|
config.hmac_key = None;
|
||||||
|
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(client.open(), Err(ClientError::OpenTimeout)));
|
||||||
client.open(ClientTimestamp::now()),
|
|
||||||
Err(ClientError::OpenTimeout)
|
|
||||||
));
|
|
||||||
|
|
||||||
let observations = server.observations(1);
|
let observations = server.observations(1);
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
@@ -156,10 +150,7 @@ fn wrong_client_key_against_hmac_required_server_times_out() {
|
|||||||
config.hmac_key = Some(b"wrong-secret".to_vec());
|
config.hmac_key = Some(b"wrong-secret".to_vec());
|
||||||
|
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(client.open(), Err(ClientError::OpenTimeout)));
|
||||||
client.open(ClientTimestamp::now()),
|
|
||||||
Err(ClientError::OpenTimeout)
|
|
||||||
));
|
|
||||||
|
|
||||||
let observations = server.observations(1);
|
let observations = server.observations(1);
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
@@ -185,7 +176,7 @@ fn bad_hmac_echo_reply_is_rejected_without_echo_reply_event() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
let outcome = client.open(ClientTimestamp::now()).unwrap();
|
let outcome = client.open().unwrap();
|
||||||
assert_started(outcome, ¶ms);
|
assert_started(outcome, ¶ms);
|
||||||
|
|
||||||
let sent = client.send_probe().unwrap();
|
let sent = client.send_probe().unwrap();
|
||||||
@@ -229,7 +220,7 @@ fn hmac_open_reply_with_bad_hmac_fails_with_protocol_error() {
|
|||||||
|
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
client.open(ClientTimestamp::now()),
|
client.open(),
|
||||||
Err(ClientError::Protocol(ProtoError::BadHmac))
|
Err(ClientError::Protocol(ProtoError::BadHmac))
|
||||||
));
|
));
|
||||||
server.join();
|
server.join();
|
||||||
@@ -247,7 +238,7 @@ fn non_hmac_client_open_does_not_set_hmac_flag() {
|
|||||||
config.hmac_key = None;
|
config.hmac_key = None;
|
||||||
|
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
let outcome = client.open(ClientTimestamp::now()).unwrap();
|
let outcome = client.open().unwrap();
|
||||||
assert_started(outcome, ¶ms);
|
assert_started(outcome, ¶ms);
|
||||||
|
|
||||||
let observations = server.observations(1);
|
let observations = server.observations(1);
|
||||||
@@ -304,7 +295,7 @@ fn backend_hmac_correct_key_succeeds() {
|
|||||||
config.hmac_key = Some(key);
|
config.hmac_key = Some(key);
|
||||||
|
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
let outcome = client.open(ClientTimestamp::now()).unwrap();
|
let outcome = client.open().unwrap();
|
||||||
assert!(matches!(outcome, OpenOutcome::Started { .. }));
|
assert!(matches!(outcome, OpenOutcome::Started { .. }));
|
||||||
|
|
||||||
let sent = client.send_probe().unwrap();
|
let sent = client.send_probe().unwrap();
|
||||||
@@ -314,7 +305,7 @@ fn backend_hmac_correct_key_succeeds() {
|
|||||||
assert_eq!(events.len(), 1);
|
assert_eq!(events.len(), 1);
|
||||||
assert!(matches!(events[0], ClientEvent::EchoReply { .. }));
|
assert!(matches!(events[0], ClientEvent::EchoReply { .. }));
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -326,10 +317,7 @@ fn backend_hmac_wrong_key_fails() {
|
|||||||
config.hmac_key = Some(b"wrong-secret".to_vec());
|
config.hmac_key = Some(b"wrong-secret".to_vec());
|
||||||
|
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(client.open(), Err(ClientError::OpenTimeout)));
|
||||||
client.open(ClientTimestamp::now()),
|
|
||||||
Err(ClientError::OpenTimeout)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -341,8 +329,5 @@ fn backend_hmac_missing_key_fails() {
|
|||||||
config.hmac_key = None;
|
config.hmac_key = None;
|
||||||
|
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(client.open(), Err(ClientError::OpenTimeout)));
|
||||||
client.open(ClientTimestamp::now()),
|
|
||||||
Err(ClientError::OpenTimeout)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
mod support;
|
mod support;
|
||||||
|
|
||||||
use irtt_client::{Client, ClientConfig, ClientEvent, ClientTimestamp, NegotiationPolicy};
|
use irtt_client::{Client, ClientConfig, ClientEvent, NegotiationPolicy};
|
||||||
use irtt_proto::{echo_header_len, echo_packet_len, Params, TimestampFields};
|
use irtt_proto::{echo_header_len, echo_packet_len, Params, TimestampFields};
|
||||||
|
|
||||||
use support::{config_for_params, default_params, run_one_probe, BackendPeer, ServerObservation};
|
use support::{config_for_params, default_params, run_one_probe, BackendPeer, ServerObservation};
|
||||||
@@ -128,7 +128,7 @@ fn backend_large_packet_smoke() {
|
|||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let outcome = client.open(ClientTimestamp::now()).unwrap();
|
let outcome = client.open().unwrap();
|
||||||
assert!(matches!(outcome, irtt_client::OpenOutcome::Started { .. }));
|
assert!(matches!(outcome, irtt_client::OpenOutcome::Started { .. }));
|
||||||
|
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
@@ -136,5 +136,5 @@ fn backend_large_packet_smoke() {
|
|||||||
assert_eq!(events.len(), 1);
|
assert_eq!(events.len(), 1);
|
||||||
assert!(matches!(events[0], ClientEvent::EchoReply { .. }));
|
assert!(matches!(events[0], ClientEvent::EchoReply { .. }));
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
mod support;
|
mod support;
|
||||||
|
|
||||||
use irtt_client::ClientTimestamp;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use irtt_client::{
|
use irtt_client::{
|
||||||
@@ -17,7 +16,6 @@ use support::{
|
|||||||
|
|
||||||
struct ReplyView<'a> {
|
struct ReplyView<'a> {
|
||||||
seq: u32,
|
seq: u32,
|
||||||
logical_seq: u64,
|
|
||||||
rtt: &'a RttSample,
|
rtt: &'a RttSample,
|
||||||
server_timing: Option<&'a ServerTiming>,
|
server_timing: Option<&'a ServerTiming>,
|
||||||
one_way: Option<&'a OneWayDelaySample>,
|
one_way: Option<&'a OneWayDelaySample>,
|
||||||
@@ -39,7 +37,6 @@ fn received_stats_modes_drive_negotiated_echo_and_reply_events() {
|
|||||||
|
|
||||||
let reply = expect_echo_reply(&run.reply);
|
let reply = expect_echo_reply(&run.reply);
|
||||||
assert_eq!(reply.seq, 0);
|
assert_eq!(reply.seq, 0);
|
||||||
assert_eq!(reply.logical_seq, 0);
|
|
||||||
assert_eq!(reply.bytes, echo_packet_len(false, ¶ms));
|
assert_eq!(reply.bytes, echo_packet_len(false, ¶ms));
|
||||||
assert!(reply.server_timing.is_none());
|
assert!(reply.server_timing.is_none());
|
||||||
assert!(reply.one_way.is_none());
|
assert!(reply.one_way.is_none());
|
||||||
@@ -167,7 +164,7 @@ fn strict_open_rejects_changed_compatibility_params_from_server() {
|
|||||||
config.server_addr = server.addr.to_string();
|
config.server_addr = server.addr.to_string();
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
client.open(irtt_client::ClientTimestamp::now()),
|
client.open(),
|
||||||
Err(ClientError::NegotiationRejected { .. })
|
Err(ClientError::NegotiationRejected { .. })
|
||||||
));
|
));
|
||||||
server.join();
|
server.join();
|
||||||
@@ -252,14 +249,8 @@ fn assert_echo_uses_params(run: &OneProbeRun, params: &Params, hmac: bool) {
|
|||||||
assert_eq!(sequence, 0);
|
assert_eq!(sequence, 0);
|
||||||
|
|
||||||
match &run.sent {
|
match &run.sent {
|
||||||
ClientEvent::EchoSent {
|
ClientEvent::EchoSent { seq, bytes, .. } => {
|
||||||
seq,
|
|
||||||
logical_seq,
|
|
||||||
bytes,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
assert_eq!(*seq, 0);
|
assert_eq!(*seq, 0);
|
||||||
assert_eq!(*logical_seq, 0);
|
|
||||||
assert_eq!(*bytes, echo_packet_len(hmac, params));
|
assert_eq!(*bytes, echo_packet_len(hmac, params));
|
||||||
}
|
}
|
||||||
other => panic!("expected EchoSent, got {other:?}"),
|
other => panic!("expected EchoSent, got {other:?}"),
|
||||||
@@ -289,7 +280,6 @@ fn expect_echo_reply(event: &ClientEvent) -> ReplyView<'_> {
|
|||||||
match event {
|
match event {
|
||||||
ClientEvent::EchoReply {
|
ClientEvent::EchoReply {
|
||||||
seq,
|
seq,
|
||||||
logical_seq,
|
|
||||||
rtt,
|
rtt,
|
||||||
server_timing,
|
server_timing,
|
||||||
one_way,
|
one_way,
|
||||||
@@ -298,7 +288,6 @@ fn expect_echo_reply(event: &ClientEvent) -> ReplyView<'_> {
|
|||||||
..
|
..
|
||||||
} => ReplyView {
|
} => ReplyView {
|
||||||
seq: *seq,
|
seq: *seq,
|
||||||
logical_seq: *logical_seq,
|
|
||||||
rtt,
|
rtt,
|
||||||
server_timing: server_timing.as_ref(),
|
server_timing: server_timing.as_ref(),
|
||||||
one_way: one_way.as_ref(),
|
one_way: one_way.as_ref(),
|
||||||
@@ -379,7 +368,7 @@ fn backend_basic_open_echo_close() {
|
|||||||
let peer = BackendPeer::start_open_echo(params.clone(), None);
|
let peer = BackendPeer::start_open_echo(params.clone(), None);
|
||||||
let mut client = Client::connect(config_for_params(peer.addr(), ¶ms)).unwrap();
|
let mut client = Client::connect(config_for_params(peer.addr(), ¶ms)).unwrap();
|
||||||
|
|
||||||
let outcome = client.open(ClientTimestamp::now()).unwrap();
|
let outcome = client.open().unwrap();
|
||||||
assert!(matches!(outcome, irtt_client::OpenOutcome::Started { .. }));
|
assert!(matches!(outcome, irtt_client::OpenOutcome::Started { .. }));
|
||||||
|
|
||||||
let sent = client.send_probe().unwrap();
|
let sent = client.send_probe().unwrap();
|
||||||
@@ -389,7 +378,7 @@ fn backend_basic_open_echo_close() {
|
|||||||
assert_eq!(events.len(), 1);
|
assert_eq!(events.len(), 1);
|
||||||
assert!(matches!(events[0], ClientEvent::EchoReply { .. }));
|
assert!(matches!(events[0], ClientEvent::EchoReply { .. }));
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -400,7 +389,7 @@ fn backend_ttl_smoke() {
|
|||||||
config.socket_config.ttl = Some(64);
|
config.socket_config.ttl = Some(64);
|
||||||
let mut client = Client::connect(config).unwrap();
|
let mut client = Client::connect(config).unwrap();
|
||||||
|
|
||||||
let outcome = client.open(ClientTimestamp::now()).unwrap();
|
let outcome = client.open().unwrap();
|
||||||
assert!(matches!(outcome, irtt_client::OpenOutcome::Started { .. }));
|
assert!(matches!(outcome, irtt_client::OpenOutcome::Started { .. }));
|
||||||
|
|
||||||
let sent = client.send_probe().unwrap();
|
let sent = client.send_probe().unwrap();
|
||||||
@@ -410,7 +399,7 @@ fn backend_ttl_smoke() {
|
|||||||
assert_eq!(events.len(), 1);
|
assert_eq!(events.len(), 1);
|
||||||
assert!(matches!(events[0], ClientEvent::EchoReply { .. }));
|
assert!(matches!(events[0], ClientEvent::EchoReply { .. }));
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -420,9 +409,9 @@ fn dscp_configured_open_close_smoke() {
|
|||||||
let server = start_open_server(params.clone(), None);
|
let server = start_open_server(params.clone(), None);
|
||||||
let mut client = Client::connect(config_for_params(server.addr, ¶ms)).unwrap();
|
let mut client = Client::connect(config_for_params(server.addr, ¶ms)).unwrap();
|
||||||
|
|
||||||
let outcome = client.open(ClientTimestamp::now()).unwrap();
|
let outcome = client.open().unwrap();
|
||||||
assert!(matches!(outcome, irtt_client::OpenOutcome::Started { .. }));
|
assert!(matches!(outcome, irtt_client::OpenOutcome::Started { .. }));
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,13 +427,13 @@ fn backend_received_stats_smoke() {
|
|||||||
let peer = BackendPeer::start_open_echo(params.clone(), None);
|
let peer = BackendPeer::start_open_echo(params.clone(), None);
|
||||||
let mut client = Client::connect(config_for_params(peer.addr(), ¶ms)).unwrap();
|
let mut client = Client::connect(config_for_params(peer.addr(), ¶ms)).unwrap();
|
||||||
|
|
||||||
client.open(ClientTimestamp::now()).unwrap();
|
client.open().unwrap();
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
let events = client.recv_once().unwrap();
|
let events = client.recv_once().unwrap();
|
||||||
assert_eq!(events.len(), 1);
|
assert_eq!(events.len(), 1);
|
||||||
assert!(matches!(events[0], ClientEvent::EchoReply { .. }));
|
assert!(matches!(events[0], ClientEvent::EchoReply { .. }));
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -461,7 +450,7 @@ fn backend_timestamp_smoke() {
|
|||||||
let peer = BackendPeer::start_open_echo(params.clone(), None);
|
let peer = BackendPeer::start_open_echo(params.clone(), None);
|
||||||
let mut client = Client::connect(config_for_params(peer.addr(), ¶ms)).unwrap();
|
let mut client = Client::connect(config_for_params(peer.addr(), ¶ms)).unwrap();
|
||||||
|
|
||||||
client.open(ClientTimestamp::now()).unwrap();
|
client.open().unwrap();
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
let events = client.recv_once().unwrap();
|
let events = client.recv_once().unwrap();
|
||||||
assert_eq!(events.len(), 1);
|
assert_eq!(events.len(), 1);
|
||||||
@@ -497,7 +486,7 @@ fn backend_timestamp_smoke() {
|
|||||||
other => panic!("expected EchoReply, got {other:?}"),
|
other => panic!("expected EchoReply, got {other:?}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -508,7 +497,7 @@ fn backend_clock_smoke() {
|
|||||||
let peer = BackendPeer::start_open_echo(params.clone(), None);
|
let peer = BackendPeer::start_open_echo(params.clone(), None);
|
||||||
let mut client = Client::connect(config_for_params(peer.addr(), ¶ms)).unwrap();
|
let mut client = Client::connect(config_for_params(peer.addr(), ¶ms)).unwrap();
|
||||||
|
|
||||||
client.open(ClientTimestamp::now()).unwrap();
|
client.open().unwrap();
|
||||||
client.send_probe().unwrap();
|
client.send_probe().unwrap();
|
||||||
let events = client.recv_once().unwrap();
|
let events = client.recv_once().unwrap();
|
||||||
assert_eq!(events.len(), 1);
|
assert_eq!(events.len(), 1);
|
||||||
@@ -536,6 +525,6 @@ fn backend_clock_smoke() {
|
|||||||
other => panic!("expected EchoReply, got {other:?}"),
|
other => panic!("expected EchoReply, got {other:?}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
client.close(ClientTimestamp::now()).unwrap();
|
client.close().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
mod support;
|
mod support;
|
||||||
|
|
||||||
use irtt_client::{
|
use irtt_client::{Client, ClientConfig, ClientError, ClientEvent, NegotiationPolicy};
|
||||||
Client, ClientConfig, ClientError, ClientEvent, ClientTimestamp, NegotiationPolicy,
|
|
||||||
};
|
|
||||||
use irtt_proto::{echo_packet_len, Params, TimestampFields};
|
use irtt_proto::{echo_packet_len, Params, TimestampFields};
|
||||||
|
|
||||||
use support::{
|
use support::{
|
||||||
@@ -112,7 +110,7 @@ fn server_fill_strict_rejects_removed_fill() {
|
|||||||
let server = start_open_server(returned, None);
|
let server = start_open_server(returned, None);
|
||||||
let mut client = Client::connect(config_for_params(server.addr, &requested)).unwrap();
|
let mut client = Client::connect(config_for_params(server.addr, &requested)).unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
client.open(ClientTimestamp::now()),
|
client.open(),
|
||||||
Err(ClientError::NegotiationRejected { .. })
|
Err(ClientError::NegotiationRejected { .. })
|
||||||
));
|
));
|
||||||
server.join();
|
server.join();
|
||||||
@@ -129,7 +127,7 @@ fn server_fill_strict_rejects_different_fill_value() {
|
|||||||
let server = start_open_server(returned, None);
|
let server = start_open_server(returned, None);
|
||||||
let mut client = Client::connect(config_for_params(server.addr, &requested)).unwrap();
|
let mut client = Client::connect(config_for_params(server.addr, &requested)).unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
client.open(ClientTimestamp::now()),
|
client.open(),
|
||||||
Err(ClientError::NegotiationRejected { .. })
|
Err(ClientError::NegotiationRejected { .. })
|
||||||
));
|
));
|
||||||
server.join();
|
server.join();
|
||||||
@@ -145,7 +143,7 @@ fn server_fill_strict_rejects_unexpected_fill_from_server() {
|
|||||||
let server = start_open_server(returned, None);
|
let server = start_open_server(returned, None);
|
||||||
let mut client = Client::connect(config_for_params(server.addr, &requested)).unwrap();
|
let mut client = Client::connect(config_for_params(server.addr, &requested)).unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
client.open(ClientTimestamp::now()),
|
client.open(),
|
||||||
Err(ClientError::NegotiationRejected { .. })
|
Err(ClientError::NegotiationRejected { .. })
|
||||||
));
|
));
|
||||||
server.join();
|
server.join();
|
||||||
|
|||||||
@@ -10,9 +10,7 @@ use std::{
|
|||||||
time::{Duration, SystemTime},
|
time::{Duration, SystemTime},
|
||||||
};
|
};
|
||||||
|
|
||||||
use irtt_client::{
|
use irtt_client::{Client, ClientConfig, ClientEvent, NegotiatedParams, OpenOutcome, SocketConfig};
|
||||||
Client, ClientConfig, ClientEvent, ClientTimestamp, NegotiatedParams, OpenOutcome, SocketConfig,
|
|
||||||
};
|
|
||||||
use irtt_proto::{
|
use irtt_proto::{
|
||||||
compute_hmac_in_place, echo_packet_len, flags, verify_hmac, Clock, Params, ProtoError,
|
compute_hmac_in_place, echo_packet_len, flags, verify_hmac, Clock, Params, ProtoError,
|
||||||
ReceivedStats, ServerFill, StampAt, TimestampFields, HMAC_SIZE, MAGIC, PROTOCOL_VERSION,
|
ReceivedStats, ServerFill, StampAt, TimestampFields, HMAC_SIZE, MAGIC, PROTOCOL_VERSION,
|
||||||
@@ -216,7 +214,7 @@ where
|
|||||||
{
|
{
|
||||||
let server = start_one_probe_server(server_params, timestamps, hmac_key);
|
let server = start_one_probe_server(server_params, timestamps, hmac_key);
|
||||||
let mut client = Client::connect(build_config(server.addr)).unwrap();
|
let mut client = Client::connect(build_config(server.addr)).unwrap();
|
||||||
let negotiated = assert_started(client.open(ClientTimestamp::now()).unwrap());
|
let negotiated = assert_started(client.open().unwrap());
|
||||||
|
|
||||||
let sent_events = client.send_probe().unwrap();
|
let sent_events = client.send_probe().unwrap();
|
||||||
assert_eq!(sent_events.len(), 1);
|
assert_eq!(sent_events.len(), 1);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "irtt-proto"
|
name = "irtt-proto"
|
||||||
version = "0.1.1"
|
version = "0.2.0"
|
||||||
description = "IRTT protocol encoding and decoding primitives"
|
description = "IRTT protocol encoding and decoding primitives"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "irtt-stats"
|
name = "irtt-stats"
|
||||||
version = "0.1.1"
|
version = "0.2.0"
|
||||||
description = "Statistics aggregation for irtt-client events"
|
description = "Statistics aggregation for irtt-client events"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|||||||
@@ -74,10 +74,6 @@ impl StatsCollector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn process(&mut self, event: &ClientEvent) -> EventStatsUpdate {
|
pub fn process(&mut self, event: &ClientEvent) -> EventStatsUpdate {
|
||||||
self.process_with_update(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn process_with_update(&mut self, event: &ClientEvent) -> EventStatsUpdate {
|
|
||||||
let Some(window_event) = WindowEvent::from_client_event(event) else {
|
let Some(window_event) = WindowEvent::from_client_event(event) else {
|
||||||
return EventStatsUpdate::default();
|
return EventStatsUpdate::default();
|
||||||
};
|
};
|
||||||
@@ -146,8 +142,8 @@ pub struct EventStatsUpdate {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct IpdvPairUpdate {
|
pub struct IpdvPairUpdate {
|
||||||
pub previous_logical_seq: u64,
|
pub previous_seq: u32,
|
||||||
pub current_logical_seq: u64,
|
pub current_seq: u32,
|
||||||
pub rtt_ipdv: Duration,
|
pub rtt_ipdv: Duration,
|
||||||
pub send_ipdv: Option<Duration>,
|
pub send_ipdv: Option<Duration>,
|
||||||
pub receive_ipdv: Option<Duration>,
|
pub receive_ipdv: Option<Duration>,
|
||||||
@@ -301,9 +297,9 @@ struct CoreStats {
|
|||||||
send_delay: MetricU64,
|
send_delay: MetricU64,
|
||||||
receive_delay: MetricU64,
|
receive_delay: MetricU64,
|
||||||
server_processing: MetricU64,
|
server_processing: MetricU64,
|
||||||
samples: HashMap<u64, UniqueSample>,
|
samples: HashMap<u32, UniqueSample>,
|
||||||
sample_order: VecDeque<u64>,
|
sample_order: VecDeque<u32>,
|
||||||
ipdv_pairs: HashSet<u64>,
|
ipdv_pairs: HashSet<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CoreStats {
|
impl CoreStats {
|
||||||
@@ -385,7 +381,7 @@ impl CoreStats {
|
|||||||
self.receive_delay.push(delay);
|
self.receive_delay.push(delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
let seq = sample.logical_seq;
|
let seq = sample.seq;
|
||||||
if self.samples.insert(seq, sample).is_none() {
|
if self.samples.insert(seq, sample).is_none() {
|
||||||
self.sample_order.push_back(seq);
|
self.sample_order.push_back(seq);
|
||||||
self.enforce_sequence_limit();
|
self.enforce_sequence_limit();
|
||||||
@@ -434,7 +430,7 @@ impl CoreStats {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_ipdv_pair(&mut self, current_seq: u64) -> Option<IpdvPairUpdate> {
|
fn try_ipdv_pair(&mut self, current_seq: u32) -> Option<IpdvPairUpdate> {
|
||||||
let previous_seq = current_seq.checked_sub(1)?;
|
let previous_seq = current_seq.checked_sub(1)?;
|
||||||
|
|
||||||
if !self.ipdv_pairs.insert(current_seq) {
|
if !self.ipdv_pairs.insert(current_seq) {
|
||||||
@@ -468,8 +464,8 @@ impl CoreStats {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Some(IpdvPairUpdate {
|
Some(IpdvPairUpdate {
|
||||||
previous_logical_seq: previous_seq,
|
previous_seq,
|
||||||
current_logical_seq: current_seq,
|
current_seq,
|
||||||
rtt_ipdv: Duration::from_nanos(rtt_ipdv),
|
rtt_ipdv: Duration::from_nanos(rtt_ipdv),
|
||||||
send_ipdv: send_ipdv.map(Duration::from_nanos),
|
send_ipdv: send_ipdv.map(Duration::from_nanos),
|
||||||
receive_ipdv: receive_ipdv.map(Duration::from_nanos),
|
receive_ipdv: receive_ipdv.map(Duration::from_nanos),
|
||||||
@@ -565,7 +561,7 @@ impl WindowEvent {
|
|||||||
timer_error_ns: duration_ns_u64(*timer_error),
|
timer_error_ns: duration_ns_u64(*timer_error),
|
||||||
}),
|
}),
|
||||||
ClientEvent::EchoReply {
|
ClientEvent::EchoReply {
|
||||||
logical_seq,
|
seq,
|
||||||
sent_at,
|
sent_at,
|
||||||
received_at,
|
received_at,
|
||||||
rtt,
|
rtt,
|
||||||
@@ -578,7 +574,7 @@ impl WindowEvent {
|
|||||||
at: received_at.mono,
|
at: received_at.mono,
|
||||||
is_late: false,
|
is_late: false,
|
||||||
sample: Box::new(UniqueSample::new(
|
sample: Box::new(UniqueSample::new(
|
||||||
*logical_seq,
|
*seq,
|
||||||
*sent_at,
|
*sent_at,
|
||||||
*received_at,
|
*received_at,
|
||||||
*rtt,
|
*rtt,
|
||||||
@@ -589,7 +585,7 @@ impl WindowEvent {
|
|||||||
)),
|
)),
|
||||||
}),
|
}),
|
||||||
ClientEvent::LateReply {
|
ClientEvent::LateReply {
|
||||||
logical_seq: Some(logical_seq),
|
seq,
|
||||||
sent_at: Some(sent_at),
|
sent_at: Some(sent_at),
|
||||||
received_at,
|
received_at,
|
||||||
rtt: Some(rtt),
|
rtt: Some(rtt),
|
||||||
@@ -602,7 +598,7 @@ impl WindowEvent {
|
|||||||
at: received_at.mono,
|
at: received_at.mono,
|
||||||
is_late: true,
|
is_late: true,
|
||||||
sample: Box::new(UniqueSample::new(
|
sample: Box::new(UniqueSample::new(
|
||||||
*logical_seq,
|
*seq,
|
||||||
*sent_at,
|
*sent_at,
|
||||||
*received_at,
|
*received_at,
|
||||||
*rtt,
|
*rtt,
|
||||||
@@ -619,7 +615,7 @@ impl WindowEvent {
|
|||||||
at: received_at.mono,
|
at: received_at.mono,
|
||||||
}),
|
}),
|
||||||
ClientEvent::EchoLoss { timeout_at, .. } => Some(Self::Loss { at: *timeout_at }),
|
ClientEvent::EchoLoss { timeout_at, .. } => Some(Self::Loss { at: *timeout_at }),
|
||||||
ClientEvent::Warning { .. } => Some(Self::Warning { at: Instant::now() }),
|
ClientEvent::Warning { at, .. } => Some(Self::Warning { at: at.mono }),
|
||||||
ClientEvent::SessionStarted { .. }
|
ClientEvent::SessionStarted { .. }
|
||||||
| ClientEvent::NoTestCompleted { .. }
|
| ClientEvent::NoTestCompleted { .. }
|
||||||
| ClientEvent::SessionClosed { .. } => None,
|
| ClientEvent::SessionClosed { .. } => None,
|
||||||
@@ -640,7 +636,7 @@ impl WindowEvent {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
struct UniqueSample {
|
struct UniqueSample {
|
||||||
logical_seq: u64,
|
seq: u32,
|
||||||
bytes: usize,
|
bytes: usize,
|
||||||
rtt_primary_ns: i128,
|
rtt_primary_ns: i128,
|
||||||
rtt_raw_ns: u64,
|
rtt_raw_ns: u64,
|
||||||
@@ -663,7 +659,7 @@ struct UniqueSample {
|
|||||||
impl UniqueSample {
|
impl UniqueSample {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn new(
|
fn new(
|
||||||
logical_seq: u64,
|
seq: u32,
|
||||||
sent_at: ClientTimestamp,
|
sent_at: ClientTimestamp,
|
||||||
received_at: ClientTimestamp,
|
received_at: ClientTimestamp,
|
||||||
rtt: RttSample,
|
rtt: RttSample,
|
||||||
@@ -673,7 +669,7 @@ impl UniqueSample {
|
|||||||
bytes: usize,
|
bytes: usize,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
logical_seq,
|
seq,
|
||||||
bytes,
|
bytes,
|
||||||
rtt_primary_ns: signed_duration_ns(rtt.effective_signed),
|
rtt_primary_ns: signed_duration_ns(rtt.effective_signed),
|
||||||
rtt_raw_ns: duration_ns_u64(rtt.raw),
|
rtt_raw_ns: duration_ns_u64(rtt.raw),
|
||||||
@@ -1074,10 +1070,9 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sent(seq: u32, logical_seq: u64, sent_at: ClientTimestamp) -> ClientEvent {
|
fn sent(seq: u32, sent_at: ClientTimestamp) -> ClientEvent {
|
||||||
ClientEvent::EchoSent {
|
ClientEvent::EchoSent {
|
||||||
seq,
|
seq,
|
||||||
logical_seq,
|
|
||||||
remote: "127.0.0.1:2112".parse().unwrap(),
|
remote: "127.0.0.1:2112".parse().unwrap(),
|
||||||
scheduled_at: sent_at.mono,
|
scheduled_at: sent_at.mono,
|
||||||
sent_at,
|
sent_at,
|
||||||
@@ -1087,24 +1082,23 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reply(logical_seq: u64, raw_ms: u64, effective_ms: i128) -> ClientEvent {
|
fn reply(seq: u32, raw_ms: u64, effective_ms: i128) -> ClientEvent {
|
||||||
let sent_at = ts(logical_seq * 10);
|
let sent_at = ts(seq as u64 * 10);
|
||||||
let received_at = ClientTimestamp {
|
let received_at = ClientTimestamp {
|
||||||
mono: sent_at.mono + Duration::from_millis(raw_ms),
|
mono: sent_at.mono + Duration::from_millis(raw_ms),
|
||||||
wall: sent_at.wall + Duration::from_millis(raw_ms),
|
wall: sent_at.wall + Duration::from_millis(raw_ms),
|
||||||
};
|
};
|
||||||
ClientEvent::EchoReply {
|
ClientEvent::EchoReply {
|
||||||
seq: logical_seq as u32,
|
seq,
|
||||||
logical_seq,
|
|
||||||
remote: "127.0.0.1:2112".parse().unwrap(),
|
remote: "127.0.0.1:2112".parse().unwrap(),
|
||||||
sent_at,
|
sent_at,
|
||||||
received_at,
|
received_at,
|
||||||
rtt: rtt(raw_ms, effective_ms),
|
rtt: rtt(raw_ms, effective_ms),
|
||||||
server_timing: Some(ServerTiming {
|
server_timing: Some(ServerTiming {
|
||||||
receive_wall_ns: Some(system_time_ns(sent_at.wall).unwrap() as i64 + 1_000_000),
|
receive_wall_ns: Some(system_time_ns(sent_at.wall).unwrap() as i64 + 1_000_000),
|
||||||
receive_mono_ns: Some(logical_seq as i64 * 10_000_000 + 1_000_000),
|
receive_mono_ns: Some(seq as i64 * 10_000_000 + 1_000_000),
|
||||||
send_wall_ns: Some(system_time_ns(sent_at.wall).unwrap() as i64 + 2_000_000),
|
send_wall_ns: Some(system_time_ns(sent_at.wall).unwrap() as i64 + 2_000_000),
|
||||||
send_mono_ns: Some(logical_seq as i64 * 10_000_000 + 2_000_000),
|
send_mono_ns: Some(seq as i64 * 10_000_000 + 2_000_000),
|
||||||
midpoint_wall_ns: None,
|
midpoint_wall_ns: None,
|
||||||
midpoint_mono_ns: None,
|
midpoint_mono_ns: None,
|
||||||
processing: Some(Duration::from_millis(1)),
|
processing: Some(Duration::from_millis(1)),
|
||||||
@@ -1114,7 +1108,7 @@ mod tests {
|
|||||||
server_to_client: Some(Duration::from_millis(2)),
|
server_to_client: Some(Duration::from_millis(2)),
|
||||||
}),
|
}),
|
||||||
received_stats: Some(ReceivedStatsSample {
|
received_stats: Some(ReceivedStatsSample {
|
||||||
count: Some((logical_seq + 1) as u32),
|
count: Some(seq + 1),
|
||||||
window: Some(0xff),
|
window: Some(0xff),
|
||||||
}),
|
}),
|
||||||
bytes: 64,
|
bytes: 64,
|
||||||
@@ -1122,7 +1116,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn late_reply(logical_seq: u64, raw_ms: u64, effective_ms: i128) -> ClientEvent {
|
fn late_reply(seq: u32, raw_ms: u64, effective_ms: i128) -> ClientEvent {
|
||||||
let ClientEvent::EchoReply {
|
let ClientEvent::EchoReply {
|
||||||
seq,
|
seq,
|
||||||
remote,
|
remote,
|
||||||
@@ -1135,13 +1129,12 @@ mod tests {
|
|||||||
bytes,
|
bytes,
|
||||||
packet_meta,
|
packet_meta,
|
||||||
..
|
..
|
||||||
} = reply(logical_seq, raw_ms, effective_ms)
|
} = reply(seq, raw_ms, effective_ms)
|
||||||
else {
|
else {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
};
|
};
|
||||||
ClientEvent::LateReply {
|
ClientEvent::LateReply {
|
||||||
seq,
|
seq,
|
||||||
logical_seq: Some(logical_seq),
|
|
||||||
highest_seen: seq + 1,
|
highest_seen: seq + 1,
|
||||||
remote,
|
remote,
|
||||||
sent_at: Some(sent_at),
|
sent_at: Some(sent_at),
|
||||||
@@ -1191,7 +1184,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn continuous_mode_bounds_sequence_tracking() {
|
fn continuous_mode_bounds_sequence_tracking() {
|
||||||
let mut collector = StatsCollector::new(StatsConfig::continuous());
|
let mut collector = StatsCollector::new(StatsConfig::continuous());
|
||||||
for seq in 0..(CONTINUOUS_SEQUENCE_LIMIT as u64 + 8) {
|
for seq in 0..(CONTINUOUS_SEQUENCE_LIMIT as u32 + 8) {
|
||||||
collector.process(&reply(seq, 10, 10));
|
collector.process(&reply(seq, 10, 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1219,8 +1212,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn late_unique_counts_and_duplicates_do_not_update_measurements() {
|
fn late_unique_counts_and_duplicates_do_not_update_measurements() {
|
||||||
let mut collector = StatsCollector::new(StatsConfig::finite());
|
let mut collector = StatsCollector::new(StatsConfig::finite());
|
||||||
collector.process(&sent(0, 0, ts(0)));
|
collector.process(&sent(0, ts(0)));
|
||||||
collector.process(&sent(1, 1, ts(10)));
|
collector.process(&sent(1, ts(10)));
|
||||||
collector.process(&reply(1, 10, 9));
|
collector.process(&reply(1, 10, 9));
|
||||||
collector.process(&late_reply(0, 20, 19));
|
collector.process(&late_reply(0, 20, 19));
|
||||||
collector.process(&ClientEvent::DuplicateReply {
|
collector.process(&ClientEvent::DuplicateReply {
|
||||||
@@ -1244,11 +1237,10 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn final_loss_uses_sent_minus_unique_replies_not_echo_loss_events() {
|
fn final_loss_uses_sent_minus_unique_replies_not_echo_loss_events() {
|
||||||
let mut collector = StatsCollector::new(StatsConfig::finite());
|
let mut collector = StatsCollector::new(StatsConfig::finite());
|
||||||
collector.process(&sent(0, 0, ts(0)));
|
collector.process(&sent(0, ts(0)));
|
||||||
collector.process(&sent(1, 1, ts(10)));
|
collector.process(&sent(1, ts(10)));
|
||||||
collector.process(&ClientEvent::EchoLoss {
|
collector.process(&ClientEvent::EchoLoss {
|
||||||
seq: 0,
|
seq: 0,
|
||||||
logical_seq: 0,
|
|
||||||
sent_at: ts(0),
|
sent_at: ts(0),
|
||||||
timeout_at: Instant::now(),
|
timeout_at: Instant::now(),
|
||||||
});
|
});
|
||||||
@@ -1267,14 +1259,14 @@ mod tests {
|
|||||||
|
|
||||||
fn assert_one_ipdv_pair(
|
fn assert_one_ipdv_pair(
|
||||||
update: &EventStatsUpdate,
|
update: &EventStatsUpdate,
|
||||||
previous_logical_seq: u64,
|
previous_seq: u32,
|
||||||
current_logical_seq: u64,
|
current_seq: u32,
|
||||||
rtt_ipdv: Duration,
|
rtt_ipdv: Duration,
|
||||||
) -> &IpdvPairUpdate {
|
) -> &IpdvPairUpdate {
|
||||||
assert_eq!(update.ipdv_pairs.len(), 1, "{update:?}");
|
assert_eq!(update.ipdv_pairs.len(), 1, "{update:?}");
|
||||||
let pair = &update.ipdv_pairs[0];
|
let pair = &update.ipdv_pairs[0];
|
||||||
assert_eq!(pair.previous_logical_seq, previous_logical_seq);
|
assert_eq!(pair.previous_seq, previous_seq);
|
||||||
assert_eq!(pair.current_logical_seq, current_logical_seq);
|
assert_eq!(pair.current_seq, current_seq);
|
||||||
assert_eq!(pair.rtt_ipdv, rtt_ipdv);
|
assert_eq!(pair.rtt_ipdv, rtt_ipdv);
|
||||||
pair
|
pair
|
||||||
}
|
}
|
||||||
@@ -1344,12 +1336,12 @@ mod tests {
|
|||||||
assert!(fill.contributed_sample);
|
assert!(fill.contributed_sample);
|
||||||
assert_eq!(fill.ipdv_pairs.len(), 2);
|
assert_eq!(fill.ipdv_pairs.len(), 2);
|
||||||
|
|
||||||
assert_eq!(fill.ipdv_pairs[0].previous_logical_seq, 0);
|
assert_eq!(fill.ipdv_pairs[0].previous_seq, 0);
|
||||||
assert_eq!(fill.ipdv_pairs[0].current_logical_seq, 1);
|
assert_eq!(fill.ipdv_pairs[0].current_seq, 1);
|
||||||
assert_eq!(fill.ipdv_pairs[0].rtt_ipdv, Duration::from_millis(3));
|
assert_eq!(fill.ipdv_pairs[0].rtt_ipdv, Duration::from_millis(3));
|
||||||
|
|
||||||
assert_eq!(fill.ipdv_pairs[1].previous_logical_seq, 1);
|
assert_eq!(fill.ipdv_pairs[1].previous_seq, 1);
|
||||||
assert_eq!(fill.ipdv_pairs[1].current_logical_seq, 2);
|
assert_eq!(fill.ipdv_pairs[1].current_seq, 2);
|
||||||
assert_eq!(fill.ipdv_pairs[1].rtt_ipdv, Duration::from_millis(7));
|
assert_eq!(fill.ipdv_pairs[1].rtt_ipdv, Duration::from_millis(7));
|
||||||
|
|
||||||
let snapshot = collector.cumulative();
|
let snapshot = collector.cumulative();
|
||||||
@@ -1363,7 +1355,6 @@ mod tests {
|
|||||||
collector.process(&reply(0, 10, 9));
|
collector.process(&reply(0, 10, 9));
|
||||||
collector.process(&ClientEvent::LateReply {
|
collector.process(&ClientEvent::LateReply {
|
||||||
seq: 9,
|
seq: 9,
|
||||||
logical_seq: None,
|
|
||||||
highest_seen: 10,
|
highest_seen: 10,
|
||||||
remote: "127.0.0.1:2112".parse().unwrap(),
|
remote: "127.0.0.1:2112".parse().unwrap(),
|
||||||
sent_at: None,
|
sent_at: None,
|
||||||
@@ -1389,7 +1380,7 @@ mod tests {
|
|||||||
rolling_count: Some(2),
|
rolling_count: Some(2),
|
||||||
rolling_time: None,
|
rolling_time: None,
|
||||||
});
|
});
|
||||||
collector.process(&sent(0, 0, ts(0)));
|
collector.process(&sent(0, ts(0)));
|
||||||
collector.process(&reply(0, 10, 10));
|
collector.process(&reply(0, 10, 10));
|
||||||
collector.process(&reply(1, 20, 20));
|
collector.process(&reply(1, 20, 20));
|
||||||
|
|
||||||
@@ -1406,9 +1397,9 @@ mod tests {
|
|||||||
rolling_count: None,
|
rolling_count: None,
|
||||||
rolling_time: Some(Duration::from_millis(15)),
|
rolling_time: Some(Duration::from_millis(15)),
|
||||||
});
|
});
|
||||||
collector.process(&sent(0, 0, ts(0)));
|
collector.process(&sent(0, ts(0)));
|
||||||
collector.process(&sent(1, 1, ts(10)));
|
collector.process(&sent(1, ts(10)));
|
||||||
collector.process(&sent(2, 2, ts(30)));
|
collector.process(&sent(2, ts(30)));
|
||||||
|
|
||||||
let rolling = collector.rolling_time().unwrap();
|
let rolling = collector.rolling_time().unwrap();
|
||||||
assert_eq!(rolling.packets.packets_sent, 1);
|
assert_eq!(rolling.packets.packets_sent, 1);
|
||||||
@@ -1420,7 +1411,7 @@ mod tests {
|
|||||||
assert_eq!(empty.loss.packet_loss_percent, 0.0);
|
assert_eq!(empty.loss.packet_loss_percent, 0.0);
|
||||||
|
|
||||||
let mut collector = StatsCollector::new(StatsConfig::finite());
|
let mut collector = StatsCollector::new(StatsConfig::finite());
|
||||||
collector.process(&sent(0, 0, ts(0)));
|
collector.process(&sent(0, ts(0)));
|
||||||
let all_lost = collector.summary();
|
let all_lost = collector.summary();
|
||||||
assert_eq!(all_lost.loss.lost_packets, 1);
|
assert_eq!(all_lost.loss.lost_packets, 1);
|
||||||
assert_eq!(all_lost.loss.packet_loss_percent, 100.0);
|
assert_eq!(all_lost.loss.packet_loss_percent, 100.0);
|
||||||
@@ -1429,8 +1420,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn directional_loss_uses_server_received_count_when_available() {
|
fn directional_loss_uses_server_received_count_when_available() {
|
||||||
let mut collector = StatsCollector::new(StatsConfig::finite());
|
let mut collector = StatsCollector::new(StatsConfig::finite());
|
||||||
collector.process(&sent(0, 0, ts(0)));
|
collector.process(&sent(0, ts(0)));
|
||||||
collector.process(&sent(1, 1, ts(10)));
|
collector.process(&sent(1, ts(10)));
|
||||||
collector.process(&reply(0, 10, 10));
|
collector.process(&reply(0, 10, 10));
|
||||||
|
|
||||||
let loss = collector.summary().loss;
|
let loss = collector.summary().loss;
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
[workspace]
|
||||||
|
members = ["cargo:."]
|
||||||
|
|
||||||
|
# Config for 'dist'
|
||||||
|
[dist]
|
||||||
|
# The preferred dist version to use in CI (Cargo.toml SemVer syntax)
|
||||||
|
cargo-dist-version = "0.31.0"
|
||||||
|
# CI backends to support
|
||||||
|
ci = "github"
|
||||||
|
# The installers to generate for each app
|
||||||
|
installers = []
|
||||||
|
# Target platforms to build apps for (Rust target-triple syntax)
|
||||||
|
targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "x86_64-pc-windows-msvc"]
|
||||||
Reference in new issue
Block a user