1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
name: CI
on:
push:
branches:
pull_request:
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
jobs:
test:
name: Test & lint (${{ matrix.os }}, ${{ matrix.backend.name }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
# Two TLS backends ship in the crate (purecrypto-tls is the default,
# rustls-tls is opt-in). Build & test both on every PR so neither
# rots. The flags are stored on the matrix entry to keep the steps
# below shell-portable across Linux/macOS/Windows runners.
backend:
- name: default
flags: --all-features
- name: rustls
flags: --no-default-features --features rustls-tls
# HTTP-only: no SSH, no BitTorrent (so no `puressh` dependency) — the
# minimal surface an HTTP source driver builds against. Guards the
# feature gating so an ungated `ssh`/`bittorrent` reference can't
# sneak back in unnoticed.
- name: http-only
flags: --no-default-features --features purecrypto-tls,idn
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- name: Cache
uses: Swatinem/rust-cache@v2
with:
key: ${{ matrix.os }}-${{ matrix.backend.name }}
- name: Format
# Formatting is platform- and backend-independent; only run it once.
if: runner.os == 'Linux' && matrix.backend.name == 'default'
run: cargo fmt --all --check
- name: Clippy (warnings denied)
run: cargo clippy --all-targets ${{ matrix.backend.flags }} -- -D warnings
- name: Test (release)
# `--release` so the integration tests in tests/ (which spin up a
# local HTTP/1.1 server and round-trip real bytes through rsurl)
# exercise the same code paths CI ships. The unit tests are cheap
# enough that running them in release adds no meaningful time.
run: cargo test --release ${{ matrix.backend.flags }}
c_abi:
name: C ABI smoke test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache
uses: Swatinem/rust-cache@v2
- name: Build the C library (static + shared)
# Cargo.toml declares crate-type = ["rlib", "cdylib", "staticlib"], so
# the build produces librsurl.a and librsurl.so. The `rsurl_*` C ABI
# symbols are gated behind the (off-by-default) `ffi` feature, so it must
# be enabled here for the smoke tests below to link.
run: cargo build --release --features ffi
- name: Compile and run the C smoke test (static link)
run: |
cc tests/ffi_smoke.c -I include target/release/librsurl.a \
-lpthread -ldl -lm -o ffi_smoke_static
./ffi_smoke_static
- name: Compile and run the C smoke test (shared link)
# Same source, linked against the cdylib instead of the staticlib.
# LD_LIBRARY_PATH points the loader at target/release so the
# binary can find librsurl.so at run time without installing it.
run: |
cc tests/ffi_smoke.c -I include \
-L target/release -lrsurl \
-o ffi_smoke_shared
LD_LIBRARY_PATH=target/release ./ffi_smoke_shared
- name: Install clippy
run: rustup component add clippy
- name: Build, lint & test the curl-compat shim
# curl-compat is a standalone sub-crate (this repo is not a workspace),
# so the steps above never touch it. Gate it here too — a non-exhaustive
# match or other breakage in the libcurl-ABI shim must turn CI red.
run: |
cargo clippy --manifest-path curl-compat/Cargo.toml --all-targets -- -D warnings
cargo build --manifest-path curl-compat/Cargo.toml --release
cargo test --manifest-path curl-compat/Cargo.toml
linux32:
name: 32-bit (i686, pure-Rust backends)
runs-on: ubuntu-latest
# rsurl's pure-Rust path (the purecrypto default and the http-only build)
# targets 32-bit/ILP32 embedded devices. x86-64 runners execute i686
# binaries natively with multilib, so the tests actually run. The rustls
# backend is intentionally excluded: its `ring` provider compiles C/asm and
# needs an i686 cross-toolchain, which is a `ring` concern, not rsurl's.
strategy:
fail-fast: false
matrix:
backend:
- name: default
flags: ""
- name: http-only
flags: "--no-default-features --features purecrypto-tls,idn"
steps:
- uses: actions/checkout@v6
- name: Install Rust (with i686 target)
uses: dtolnay/rust-toolchain@stable
with:
targets: i686-unknown-linux-gnu
components: clippy
- name: Install 32-bit link support
# rustc shells out to gcc to link the i686 binaries; gcc-multilib
# provides the 32-bit crt/libc and `-m32` support.
run: sudo apt-get update && sudo apt-get install -y gcc-multilib
- name: Cache
uses: Swatinem/rust-cache@v2
with:
key: i686-${{ matrix.backend.name }}
- name: Clippy (warnings denied)
run: cargo clippy --target i686-unknown-linux-gnu --all-targets ${{ matrix.backend.flags }} -- -D warnings
- name: Test (release)
run: cargo test --release --target i686-unknown-linux-gnu ${{ matrix.backend.flags }}
- name: Build, lint & test the curl-compat shim (i686)
# Exercises the pointer-width gate: CURLOPT_POSTFIELDSIZE_LARGE rejects
# with CURLE_NOT_BUILT_IN on ILP32 instead of truncating the curl_off_t.
if: matrix.backend.name == 'default'
run: |
cargo clippy --manifest-path curl-compat/Cargo.toml --target i686-unknown-linux-gnu --all-targets -- -D warnings
cargo build --manifest-path curl-compat/Cargo.toml --release --target i686-unknown-linux-gnu
cargo test --manifest-path curl-compat/Cargo.toml --target i686-unknown-linux-gnu
docs:
name: Docs build (warnings denied)
runs-on: ubuntu-latest
env:
RUSTDOCFLAGS: -D warnings -D rustdoc::broken-intra-doc-links
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache
uses: Swatinem/rust-cache@v2
- name: cargo doc --no-deps --all-features
run: cargo doc --no-deps --all-features
wasm:
name: wasm32 (browser) build & lint
runs-on: ubuntu-latest
# The browser backend (`crate::aio` over Fetch + the native WebSocket) is a
# whole separate compile target the matrix above never exercises: on
# `wasm32-unknown-unknown` the entire net/proto/tls socket stack is
# cfg'd out and the wasm bindings take over. Build & lint it here so that
# gating can't silently rot (e.g. an ungated `std::net` reference sneaking
# back into the wasm surface). Tests need a browser harness (wasm-bindgen-test)
# and are out of scope for this job — this guards compilation and lints only.
steps:
- uses: actions/checkout@v6
- name: Install Rust (with wasm32 target)
uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown
components: clippy
- name: Cache
uses: Swatinem/rust-cache@v2
with:
key: wasm32
- name: Clippy (warnings denied)
# The wasm build is HTTP/WebSocket-only: `idn` for host normalisation,
# no default features (which would pull the native-only ssh/tls/bittorrent
# stack). `--lib` because test/bin targets don't link on bare wasm32.
run: cargo clippy --target wasm32-unknown-unknown --no-default-features --features idn --lib -- -D warnings
- name: Build (lib + bin)
# Also builds the bin wrapper (an empty `main` on wasm) to prove the whole
# crate — not just the lib — compiles for the browser target.
run: cargo build --target wasm32-unknown-unknown --no-default-features --features idn