Merged
Conversation
- Detect Content-Type: application/grpc* in ingress and tag stream as Protocol::GRPC (variant was already reserved in the protocol crate) - Use hyper::client::conn::http2::handshake over VirtualStream for gRPC streams so HTTP/2 framing and trailers are preserved end-to-end - Add use_h2 field + with_pool_h2() to LocalProxyService; h2 path calls pool.acquire_h2() instead of acquire_h1() - Add HttpProxy::handle_grpc_stream() that serves VirtualStream via hyper::server::conn::http2::Builder with a prefer_h2 pool - CLI StreamHandler::handle dispatches Protocol::GRPC to handle_grpc_stream automatically — no new flags required - Integration tests: test_grpc_tunnel (end-to-end) and test_non_grpc_not_classified_as_grpc (regression guard for HTTP path) - Example: examples/basic/grpc_tunnel.rs - Update CHANGELOG.md (Unreleased v1.0.5) and ROADMAP.md
) rustls 0.23 requires rustls::crypto::ring::default_provider().install_default() to be called before any ClientConfig/ServerConfig is built. The feature flag makes the provider available but does NOT auto-install it. The test suite already calls this in test helpers, so tests passed. The release binary did not, causing a panic on the first TLS connection attempt in a tokio worker thread. Add the install_default() call at the top of main() and add rustls as a direct dependency of ferrotunnel-cli so the call compiles without relying on a transitive re-export. Closes #98
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Pull request overview
This PR addresses issue #98 (rustls 0.23 CryptoProvider panic in the CLI) and adds end-to-end gRPC tunneling support by detecting gRPC requests at the HTTP ingress and proxying them over HTTP/2 through the tunnel.
Changes:
- Fix CLI TLS panic by explicitly installing rustls’s ring CryptoProvider at startup and adding
rustlsas a direct CLI dependency. - Add gRPC detection (
Content-Type: application/grpc*) and an HTTP/2 forwarding path to preserve gRPC trailer semantics end-to-end. - Add a gRPC example and integration tests; bump workspace/crate versions to
1.0.6.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/soak/Cargo.toml | Bumps ferrotunnel dependency version to align with workspace versioning. |
| tests/integration/mod.rs | Registers new gRPC integration test module. |
| tests/integration/grpc_test.rs | Adds end-to-end gRPC tunneling tests (currently not asserting trailers). |
| ferrotunnel/Cargo.toml | Updates internal crate dependency versions to 1.0.6. |
| ferrotunnel-observability/Cargo.toml | Updates dependency version to 1.0.6. |
| ferrotunnel-http/src/proxy.rs | Adds HTTP/2 local forwarding mode + handle_grpc_stream() for tunnel streams. |
| ferrotunnel-http/src/ingress.rs | Detects gRPC and uses HTTP/2 client handshake over VirtualStream when detected. |
| ferrotunnel-http/Cargo.toml | Updates internal crate dependency versions to 1.0.6. |
| ferrotunnel-core/Cargo.toml | Updates internal crate dependency versions to 1.0.6. |
| ferrotunnel-cli/src/main.rs | Installs rustls ring CryptoProvider early to prevent TLS panic. |
| ferrotunnel-cli/src/commands/client.rs | Dispatches Protocol::GRPC streams to the new HTTP/2 handling path. |
| ferrotunnel-cli/Cargo.toml | Bumps internal deps to 1.0.6 and adds direct rustls dependency. |
| examples/basic/grpc_tunnel.rs | Adds a runnable gRPC tunneling example. |
| examples/Cargo.toml | Registers the new grpc_tunnel example. |
| ROADMAP.md | Updates roadmap items to reference v1.0.6 (contains duplicate v1.0.6 headings). |
| Cargo.toml | Bumps workspace version to 1.0.6 and updates workspace dependency version pinning. |
| CHANGELOG.md | Adds 1.0.6 “Unreleased” notes; updates compare links (currently inconsistent for an unreleased tag). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
154
to
156
| let is_ws = is_websocket_upgrade(req.headers()); | ||
| let is_grpc = is_grpc(req.headers()); | ||
|
|
ferrotunnel-http/src/proxy.rs
Outdated
Comment on lines
+326
to
+330
| let grpc_pool = Arc::new(ConnectionPool::new( | ||
| self.target_addr.clone(), | ||
| PoolConfig { | ||
| prefer_h2: true, | ||
| ..PoolConfig::default() |
Comment on lines
+19
to
+24
| /// Accepts any POST request and responds with: | ||
| /// - status 200 | ||
| /// - `content-type: application/grpc` | ||
| /// - body: empty gRPC message frame (5-byte prefix + 0 bytes) | ||
| /// - trailer: `grpc-status: 0` | ||
| async fn start_grpc_server(addr: SocketAddr) -> tokio::task::JoinHandle<()> { |
ROADMAP.md
Outdated
Comment on lines
+49
to
+53
| - **v1.0.6** - gRPC Support (in development) | ||
| - Native gRPC tunneling over HTTP/2 with automatic detection and trailer preservation | ||
| - **Target Audience**: Enterprise and microservices developers | ||
|
|
||
| - **v1.0.5** - QUIC Transport (HTTP/3) | ||
| - **v1.0.6** - QUIC Transport (HTTP/3) |
CHANGELOG.md
Outdated
| | `ferrotunnel-common` | Shared types and errors | | ||
|
|
||
| [Unreleased]: https://github.com/ferro-labs/ferrotunnel/compare/v1.0.4...HEAD | ||
| [Unreleased]: https://github.com/ferro-labs/ferrotunnel/compare/v1.0.6...HEAD |
- ingress: recompute is_grpc from parts.headers after plugin request hooks, so plugins that modify Content-Type are respected - ingress: reconstruct absolute HTTP/2 URI (scheme + authority) from the Host header when the forwarded request has a path-only URI — hyper's h2 client requires :scheme/:authority pseudo-headers (User(MissingUriSchemeAndAuthority) was causing 502 in tests) - client (library): dispatch Protocol::GRPC streams to handle_grpc_stream() instead of always calling handle_stream(); fixes the integration test 502 and mirrors the CLI dispatch logic - proxy: remove unused prefer_h2:true from handle_grpc_stream's PoolConfig (PoolConfig::prefer_h2 is not wired into acquire routing); update doc comment accordingly - grpc_test: mock server now emits a real grpc-status:0 HTTP/2 trailer via StreamBody; test asserts the trailer is preserved end-to-end through the tunnel - ROADMAP: disambiguate duplicate v1.0.6 entries — QUIC → v1.0.7, Multi-region → v1.0.8, Custom Domains → v1.0.9 - CHANGELOG: restore [Unreleased] compare link to v1.0.4...HEAD until the v1.0.6 tag is published
- Add ### gRPC Tunneling section after HTTP/2 & Connection Pooling - Add gRPC row to features table - Remove examples/basic/grpc_tunnel.rs and its Cargo.toml entry - Fix clippy::map_unwrap_or lint in ferrotunnel-http/src/ingress.rs
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.
fix #98