feat(transport): HttpTransport abstraction for generated clients + in-process e2e tests#18
Merged
Merged
Conversation
Generated REST/JSON-RPC/File clients hard-coded reqwest::Client and were never exercised against a server in tests. Introduce ras-transport-core, an object-safe HttpTransport trait (dyn dispatch, conditional Send mirroring WebSocketTransport) with two impls: ReqwestTransport (production) and AxumTestTransport (in-process, wraps axum_test::TestServer). - Typed TransportError replaces Box<dyn Error> in generated client returns. - Streaming RequestBody/TransportResponse; hand-rolled RFC 7578 MultipartBuilder replaces reqwest::multipart::Form (uploads stream from disk on native, downloads stream the response body). - Generated clients store Arc<dyn HttpTransport> and gain build_with_transport; query serialization moves to serde_urlencoded (reqwest .query() parity). - REST/JSON-RPC/File migrated; bidirectional left unchanged (WebSocket, already has WebSocketTransport). Breaking (pre-1.0): file download methods now return TransportResponse instead of reqwest::Response; generated methods return Result<T, TransportError>. Adds client->server e2e tests over AxumTestTransport for every macro (incl. the from-disk streaming upload under --features fs) plus transport-core unit tests (multipart framing, query parity, request/response/transport at ~100% lines). Closes #17 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ep-free CI runs `cargo clippy --workspace --all-targets --all-features --locked -D warnings` plus rustfmt; this fixes the gates: - rustfmt: apply `cargo fmt --all`. - clippy: elide the redundant lifetime on the QueryValueCollector Serializer impl. - Move the tokio::fs -> ReaderStream conversion fully into MultipartBuilder::file_path (now takes an optional filename override) so the generated file-upload client no longer emits futures_util/tokio/tokio-util references. This was breaking the file-service examples under --all-features (E0432 unresolved `futures_util`, E0599 ReaderStream is not an iterator), which only depend on ras-transport-core. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI's `dtolnay/rust-toolchain@stable` advanced to rustc 1.96.0 (2026-05-25), which broke the workspace independently of the transport refactor: - E0446 "private type in public interface" is now a hard error. Many existing ras-jsonrpc-macro test/example/bench fixtures declared non-pub request/ response structs that the generated `pub` service trait exposes. Make those fixture types `pub` (bare visibility flips only). - New lint clippy::manual_noop_waker fires on ras-auth-core's test NoopWaker; replace it with `std::task::Waker::noop().clone()`. No library logic, public APIs, features, deps, or CI config changed. Verified on 1.96.0 (exact CI commands): - cargo clippy --workspace --all-targets --all-features --locked -- -D warnings - cargo test --workspace --all-targets --all-features --locked - cargo test --doc --workspace --all-features --locked - cargo fmt --all -- --check Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- multipart: generate boundary from CSPRNG entropy (getrandom) instead of a predictable timestamp+counter, and correct the stale doc comment. The boundary is the only thing protecting multipart framing from injection when part bodies carry caller-forwarded bytes. - error: add TransportError::Timeout and route reqwest timeouts to it so a per-request timeout no longer masquerades as a connection error. - response: error_for_status surfaces a failed body read instead of silently blanking the diagnostic. - lib: serialize_query_pairs now returns Result (matching serialize_query_value) rather than swallowing failure into an empty string after the codegen has written a '?'/'&' separator; rest client codegen propagates it with '?'. - ras-jsonrpc-macro: make ras-transport-core an optional dep, matching the rest/file macro crates (it is only referenced by generated client code). - docs: fix broken intra-doc links in generated clients and the inaccurate "only transport that buffers" note on the axum-test transport. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ort abstraction Harden the new reqwest-free request construction in ras-transport-core and the generated REST/JSON-RPC/file clients. Each fix ships with a red-then-green regression test. - Multipart Content-Type CRLF injection: the hand-rolled multipart builder wrote a part's Content-Type verbatim (unlike name/filename, which were escaped), letting a caller-supplied content type inject extra header lines or break the part header block. Sanitize it at the framing sink. - Bearer token fail-open: TransportRequest::bearer() silently dropped a token that could not be encoded as a header value, sending the request unauthenticated. It now fails closed (returns TransportError::InvalidHeader); header() stays best-effort. Propagated through the rest/jsonrpc/file generators (file build_request now returns Result). - Unencoded path parameters: client generators substituted path params raw, so '/', '?', '#', etc. could escape the URL segment. Add and wire encode_path_segment (RFC 3986 unreserved pass-through, percent-encode rest). Tests: multipart/bearer regressions in ras-transport-core; path-encoding unit test plus a capturing-transport integration test in ras-rest-macro. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #17.
What
Introduces
ras-transport-core— an object-safeHttpTransporttrait (dyn dispatch, conditionalSendmirroring the existingWebSocketTransportpattern) — and migrates the generated REST / JSON-RPC / File clients to dispatch through it. This lets generated clients run over reqwest (production) or axum-test (in-process), so they can finally be exercised end-to-end against a server with no sockets.Highlights
Result<T, TransportError>instead ofBox<dyn std::error::Error + Send + Sync>.RequestBody/TransportResponsecarry streaming bodies; a hand-rolled RFC 7578MultipartBuilderreplacesreqwest::multipart::Form. Uploads stream from disk on native (fsfeature); downloads stream the response body.ReqwestTransport(reqwestfeature) andAxumTestTransport(axum-testfeature, wrapsArc<TestServer>).Arc<dyn HttpTransport>and gain an ungatedbuild_with_transport(...)alongside the default reqwest-backedbuild()..query()toserde_urlencodedwith byte-for-byte parity (Option-skip,Vecrepeated keys, enum#[serde(rename)]).ras-jsonrpc-bidirectional-macro) is intentionally untouched — WebSocket, not HTTP, already has its ownWebSocketTransport.Breaking (pre-1.0)
ras_transport_core::TransportResponseinstead ofreqwest::Response(consume viainto_body_stream()/bytes()).Result<T, TransportError>.Testing
AxumTestTransportfor every macro, including the from-disk streaming upload path (--features fs).ras-transport-coreunit/integration tests: byte-exact multipart framing, query.query()parity, request/response types, and the in-process transport.request.rs/response.rs/axum_test_transport.rsat 100% line coverage;lib.rs92.6%,multipart.rs98.2%.Verification
cargo test --workspace— 100 test groups, 0 failurescargo build --workspace --all-targetscargo build -p ras-transport-core --target wasm32-unknown-unknown --features reqwest,fscargo build -p wasm-ui-demo --target wasm32-unknown-unknown