diff -Nru rust-tonic-0.12.3+dfsg/Cargo.toml rust-tonic-0.13.0+dfsg/Cargo.toml --- rust-tonic-0.12.3+dfsg/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -30,3 +30,15 @@ "tests/skip_debug", ] resolver = "2" + +[workspace.package] +rust-version = "1.75" + +[workspace.lints.rust] +missing_debug_implementations = "warn" +missing_docs = "warn" +rust_2018_idioms = "warn" +unreachable_pub = "warn" + +[workspace.lints.rustdoc] +broken_intra_doc_links = "deny" diff -Nru rust-tonic-0.12.3+dfsg/CHANGELOG.md rust-tonic-0.13.0+dfsg/CHANGELOG.md --- rust-tonic-0.12.3+dfsg/CHANGELOG.md 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/CHANGELOG.md 2025-03-25 18:49:19.000000000 +0000 @@ -1,3 +1,5 @@ +# NOTE: ths changelog is no longer used and from version `v0.13.0` onward we will be using github releases and the changes can be found [here](https://github.com/hyperium/tonic/releases). + # [0.12.3](https://github.com/hyperium/tonic/compare/v0.12.2...v0.12.3) (2024-08-29) ### Features diff -Nru rust-tonic-0.12.3+dfsg/codegen/Cargo.toml rust-tonic-0.13.0+dfsg/codegen/Cargo.toml --- rust-tonic-0.12.3+dfsg/codegen/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/codegen/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -3,10 +3,11 @@ authors = ["Lucio Franco "] license = "MIT" edition = "2021" -publish = false -version = "0.1.0" [dependencies] -tempfile = "3.8.0" protox = "0.7" +prettyplease = "0.2" +quote = "1" +syn = "2" +tempfile = "3.8.0" tonic-build = {path = "../tonic-build", default-features = false, features = ["prost", "cleanup-markdown"]} diff -Nru rust-tonic-0.12.3+dfsg/codegen/src/main.rs rust-tonic-0.13.0+dfsg/codegen/src/main.rs --- rust-tonic-0.12.3+dfsg/codegen/src/main.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/codegen/src/main.rs 2025-03-25 18:49:19.000000000 +0000 @@ -4,7 +4,8 @@ path::{Path, PathBuf}, }; -use protox::prost::{bytes::BytesMut, Message as _}; +use protox::prost::Message as _; +use quote::quote; use tonic_build::FileDescriptorSet; fn main() { @@ -17,7 +18,7 @@ &["proto/health.proto"], &["proto"], &PathBuf::from("src/generated"), - &PathBuf::from("src/generated/grpc_health_v1.bin"), + &PathBuf::from("src/generated/grpc_health_v1_fds.rs"), true, true, ); @@ -31,7 +32,7 @@ &["proto/reflection_v1.proto"], &["proto"], &PathBuf::from("src/generated"), - &PathBuf::from("src/generated/reflection_v1.bin"), + &PathBuf::from("src/generated/reflection_v1_fds.rs"), true, true, ); @@ -43,7 +44,7 @@ &["proto/reflection_v1alpha.proto"], &["proto"], &PathBuf::from("src/generated"), - &PathBuf::from("src/generated/reflection_v1alpha1.bin"), + &PathBuf::from("src/generated/reflection_v1alpha1_fds.rs"), true, true, ); @@ -57,7 +58,7 @@ &["proto/status.proto", "proto/error_details.proto"], &["proto"], &PathBuf::from("src/generated"), - &PathBuf::from("src/generated/types.bin"), + &PathBuf::from("src/generated/types_fds.rs"), false, false, ); @@ -77,19 +78,12 @@ .tempdir() .unwrap(); - let iface_files: Vec = iface_files - .iter() - .map(|&path| root_dir.join(path)) - .collect(); - - let include_dirs: Vec = include_dirs - .iter() - .map(|&path| root_dir.join(path)) - .collect(); + let iface_files = iface_files.iter().map(|&path| root_dir.join(path)); + let include_dirs = include_dirs.iter().map(|&path| root_dir.join(path)); let out_dir = root_dir.join(out_dir); let file_descriptor_set_path = root_dir.join(file_descriptor_set_path); - let fds = protox::compile(&iface_files, &include_dirs).unwrap(); + let fds = protox::compile(iface_files, include_dirs).unwrap(); write_fds(&fds, &file_descriptor_set_path); @@ -117,8 +111,53 @@ } fn write_fds(fds: &FileDescriptorSet, path: &Path) { + const GENERATED_COMMENT: &str = "// This file is @generated by codegen."; + + let mut file_header = String::new(); + + let mut fds = fds.clone(); + + for fd in fds.file.iter() { + let Some(source_code_info) = &fd.source_code_info else { + continue; + }; + + for location in &source_code_info.location { + for comment in &location.leading_detached_comments { + file_header += comment; + } + } + } + + for fd in fds.file.iter_mut() { + fd.source_code_info = None; + } + + let fds_raw = fds.encode_to_vec(); + let tokens = quote! { + /// Byte encoded FILE_DESCRIPTOR_SET. + pub const FILE_DESCRIPTOR_SET: &[u8] = &[#(#fds_raw),*]; + }; + let ast = syn::parse2(tokens).unwrap(); + let formatted = prettyplease::unparse(&ast); + let mut writer = BufWriter::new(File::create(path).unwrap()); - let mut buf = BytesMut::with_capacity(fds.encoded_len()); - fds.encode(&mut buf).unwrap(); - writer.write_all(&buf).unwrap(); + + writer.write_all(GENERATED_COMMENT.as_bytes()).unwrap(); + writer.write_all(b"\n").unwrap(); + + if !file_header.is_empty() { + let file_header = comment_out(&file_header); + writer.write_all(file_header.as_bytes()).unwrap(); + writer.write_all(b"\n").unwrap(); + } + + writer.write_all(formatted.as_bytes()).unwrap() +} + +fn comment_out(s: &str) -> String { + s.split('\n') + .map(|line| format!("// {line}")) + .collect::>() + .join("\n") } diff -Nru rust-tonic-0.12.3+dfsg/debian/changelog rust-tonic-0.13.0+dfsg/debian/changelog --- rust-tonic-0.12.3+dfsg/debian/changelog 2025-04-08 11:19:59.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/debian/changelog 2025-04-10 16:41:03.000000000 +0000 @@ -1,3 +1,19 @@ +rust-tonic (0.13.0+dfsg-1) UNRELEASED; urgency=medium + + [ upstream ] + * new release + + [ Blair Noctis ] + * drop 2001_prost and 2001_tower-http, versions updated in Debian + * fix patch 2001_protox fds generation + * add patch 2001_axum to user older branch in Debian + * add patch 201_tokio-rustls to adapt to removal of aws-lc-rs + feature of tokio-rustls in Debian + * add patch 2005_matchit_route_catch-all to adapt to matchit syntax + change + + -- Blair Noctis Thu, 10 Apr 2025 16:41:03 +0000 + rust-tonic (0.12.3+dfsg-4) unstable; urgency=medium * wrap and sort control files diff -Nru rust-tonic-0.12.3+dfsg/debian/control rust-tonic-0.13.0+dfsg/debian/control --- rust-tonic-0.12.3+dfsg/debian/control 2025-04-08 11:19:13.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/debian/control 2025-04-10 16:41:03.000000000 +0000 @@ -31,9 +31,9 @@ librust-pin-project-1+default-dev, librust-prettyplease-0.2+default-dev, librust-proc-macro2-1+default-dev, - librust-prost-0.12+default-dev, - librust-prost-build-0.12+default-dev, - librust-prost-types-0.12+default-dev, + librust-prost-0.13+default-dev, + librust-prost-build-0.13+default-dev, + librust-prost-types-0.13+default-dev, librust-quickcheck-1+default-dev, librust-quickcheck-macros-1+default-dev, librust-quote-1+default-dev, @@ -90,16 +90,16 @@ Depends: librust-prettyplease-0.2+default-dev, librust-proc-macro2-1+default-dev, - librust-prost-build-0.12+default-dev, + librust-prost-build-0.13+default-dev, librust-quote-1+default-dev, librust-syn-2+default-dev, ${misc:Depends}, Provides: - librust-tonic-build-0.12+default-dev (= ${binary:Version}), - librust-tonic-build-0.12+prost-dev (= ${binary:Version}), - librust-tonic-build-0.12+transport-dev (= ${binary:Version}), - librust-tonic-build-0.12-dev (= ${binary:Version}), - librust-tonic-build-0.12.3-dev (= ${binary:Version}), + librust-tonic-build-0.13+default-dev (= ${binary:Version}), + librust-tonic-build-0.13+prost-dev (= ${binary:Version}), + librust-tonic-build-0.13+transport-dev (= ${binary:Version}), + librust-tonic-build-0.13-dev (= ${binary:Version}), + librust-tonic-build-0.13.0-dev (= ${binary:Version}), Description: codegen module of tonic gRPC implementation - Rust source code tonic-build compiles proto files via prost and generates service stubs and proto definitiones for use with tonic. @@ -138,8 +138,8 @@ librust-hyper-util-0.1+tokio-dev, librust-percent-encoding-2+default-dev, librust-pin-project-1+default-dev, - librust-prost-0.12+default-dev, - librust-prost-types-0.12+default-dev, + librust-prost-0.13+default-dev, + librust-prost-types-0.13+default-dev, librust-rustls-native-certs-0.6+default-dev, librust-rustls-pemfile-2+default-dev, librust-socket2-0.5+all-dev, @@ -168,23 +168,24 @@ librust-zstd-0.13+default-dev, ${misc:Depends}, Provides: - librust-tonic-0.12+channel-dev (= ${binary:Version}), - librust-tonic-0.12+codegen-dev (= ${binary:Version}), - librust-tonic-0.12+default-dev (= ${binary:Version}), - librust-tonic-0.12+gzip-dev (= ${binary:Version}), - librust-tonic-0.12+prost-dev (= ${binary:Version}), - librust-tonic-0.12+router-dev (= ${binary:Version}), - librust-tonic-0.12+server-dev (= ${binary:Version}), - librust-tonic-0.12+tls-dev (= ${binary:Version}), - librust-tonic-0.12+tls-native-roots-dev (= ${binary:Version}), - librust-tonic-0.12+tls-roots-dev (= ${binary:Version}), - librust-tonic-0.12+transport-dev (= ${binary:Version}), - librust-tonic-0.12+zstd-dev (= ${binary:Version}), - librust-tonic-0.12-dev (= ${binary:Version}), - librust-tonic-0.12.3-dev (= ${binary:Version}), - librust-tonic-types-0.12+default-dev (= ${rust:Version:librust-tonic-types-dev}), - librust-tonic-types-0.12-dev (= ${rust:Version:librust-tonic-types-dev}), - librust-tonic-types-0.12.3-dev (= ${rust:Version:librust-tonic-types-dev}), + librust-tonic-0.13+channel-dev (= ${binary:Version}), + librust-tonic-0.13+codegen-dev (= ${binary:Version}), + librust-tonic-0.13+default-dev (= ${binary:Version}), + librust-tonic-0.13+deflate-dev (= ${binary:Version}), + librust-tonic-0.13+gzip-dev (= ${binary:Version}), + librust-tonic-0.13+prost-dev (= ${binary:Version}), + librust-tonic-0.13+router-dev (= ${binary:Version}), + librust-tonic-0.13+server-dev (= ${binary:Version}), + librust-tonic-0.13+tls-dev (= ${binary:Version}), + librust-tonic-0.13+tls-native-roots-dev (= ${binary:Version}), + librust-tonic-0.13+tls-roots-dev (= ${binary:Version}), + librust-tonic-0.13+transport-dev (= ${binary:Version}), + librust-tonic-0.13+zstd-dev (= ${binary:Version}), + librust-tonic-0.13-dev (= ${binary:Version}), + librust-tonic-0.13.0-dev (= ${binary:Version}), + librust-tonic-types-0.13+default-dev (= ${rust:Version:librust-tonic-types-dev}), + librust-tonic-types-0.13-dev (= ${rust:Version:librust-tonic-types-dev}), + librust-tonic-types-0.13.0-dev (= ${rust:Version:librust-tonic-types-dev}), librust-tonic-types-dev (= ${rust:Version:librust-tonic-types-dev}), Description: gRPC over HTTP/2 - Rust source code tonic is a gRPC over HTTP/2 implementation diff -Nru rust-tonic-0.12.3+dfsg/debian/copyright rust-tonic-0.13.0+dfsg/debian/copyright --- rust-tonic-0.12.3+dfsg/debian/copyright 2025-03-15 15:46:13.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/debian/copyright 2025-04-10 13:10:59.000000000 +0000 @@ -30,7 +30,8 @@ interop/proto/grpc/testing/messages.proto interop/proto/grpc/testing/test.proto tonic-health/proto/health.proto - tonic-reflection/proto/reflection.proto + tonic-reflection/proto/reflection_v1.proto + tonic-reflection/proto/reflection_v1alpha.proto tonic/benches-disabled/proto/helloworld/helloworld.proto Copyright: 2015-2016, 2018 gRPC authors diff -Nru rust-tonic-0.12.3+dfsg/debian/copyright_hints rust-tonic-0.13.0+dfsg/debian/copyright_hints --- rust-tonic-0.12.3+dfsg/debian/copyright_hints 2025-04-07 15:52:43.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/debian/copyright_hints 2025-04-10 16:41:03.000000000 +0000 @@ -11,22 +11,24 @@ codegen/Cargo.toml codegen/src/main.rs debian/TODO + debian/changelog.dch debian/clean debian/control debian/copyright-check debian/gbp.conf debian/librust-tonic-dev.examples debian/patches/1002_fence_tests.patch - debian/patches/2001_prost.patch + debian/patches/2001_axum.patch debian/patches/2001_protox.patch debian/patches/2001_rustls-native-certs_1.patch debian/patches/2001_rustls-native-certs_2.patch - debian/patches/2001_tower-http.patch + debian/patches/2001_tokio-rustls.patch debian/patches/2002_no_feature_cleanup-markdown.patch debian/patches/2002_no_net.patch debian/patches/2002_no_webpki-roots.patch debian/patches/2003_no_bench.patch debian/patches/2004_reduce_tests.patch + debian/patches/2005_matchit_route_catch-all.patch debian/patches/README debian/patches/series debian/rules @@ -97,8 +99,6 @@ examples/src/routeguide/server.rs examples/src/streaming/client.rs examples/src/streaming/server.rs - examples/src/timeout/client.rs - examples/src/timeout/server.rs examples/src/tls/client.rs examples/src/tls/server.rs examples/src/tls_client_auth/client.rs @@ -109,7 +109,8 @@ examples/src/tower/server.rs examples/src/tracing/client.rs examples/src/tracing/server.rs - examples/src/uds/client.rs + examples/src/uds/client_standard.rs + examples/src/uds/client_with_connector.rs examples/src/uds/server.rs interop/Cargo.toml interop/build.rs @@ -128,11 +129,10 @@ interop/update_binaries.sh prepare-release.sh publish-release.sh - rustfmt.toml tests/ambiguous_methods/Cargo.toml tests/ambiguous_methods/build.rs tests/ambiguous_methods/proto/ambiguous_methods.proto - tests/ambiguous_methods/src/main.rs + tests/ambiguous_methods/src/lib.rs tests/compression/Cargo.toml tests/compression/build.rs tests/compression/proto/test.proto @@ -195,7 +195,7 @@ tests/root-crate-path/Cargo.toml tests/root-crate-path/build.rs tests/root-crate-path/foo.proto - tests/root-crate-path/src/main.rs + tests/root-crate-path/src/lib.rs tests/same_name/Cargo.toml tests/same_name/build.rs tests/same_name/proto/foo.proto @@ -216,7 +216,7 @@ tests/stream_conflict/Cargo.toml tests/stream_conflict/build.rs tests/stream_conflict/proto/stream_conflict.proto - tests/stream_conflict/src/main.rs + tests/stream_conflict/src/lib.rs tests/use_arc_self/Cargo.toml tests/use_arc_self/build.rs tests/use_arc_self/proto/test.proto @@ -319,7 +319,9 @@ tonic/src/server/mod.rs tonic/src/server/service.rs tonic/src/service/interceptor.rs + tonic/src/service/layered.rs tonic/src/service/mod.rs + tonic/src/service/recover_error.rs tonic/src/service/router.rs tonic/src/transport/channel/endpoint.rs tonic/src/transport/channel/mod.rs @@ -334,14 +336,15 @@ tonic/src/transport/channel/service/tls.rs tonic/src/transport/channel/service/user_agent.rs tonic/src/transport/channel/tls.rs + tonic/src/transport/channel/uds_connector.rs tonic/src/transport/error.rs tonic/src/transport/mod.rs tonic/src/transport/server/conn.rs tonic/src/transport/server/incoming.rs + tonic/src/transport/server/io_stream.rs tonic/src/transport/server/mod.rs tonic/src/transport/server/service/io.rs tonic/src/transport/server/service/mod.rs - tonic/src/transport/server/service/recover_error.rs tonic/src/transport/server/service/tls.rs tonic/src/transport/server/tls.rs tonic/src/transport/server/unix.rs @@ -354,34 +357,6 @@ License: UNKNOWN FIXME -Files: LICENSE - examples/LICENSE - interop/LICENSE - tests/ambiguous_methods/LICENSE - tests/compression/LICENSE - tests/extern_path/my_application/LICENSE - tests/extern_path/uuid/LICENSE - tests/included_service/LICENSE - tests/integration_tests/LICENSE - tests/root-crate-path/LICENSE - tests/same_name/LICENSE - tests/service_named_result/LICENSE - tests/service_named_service/LICENSE - tests/stream_conflict/LICENSE - tests/use_arc_self/LICENSE - tests/web/LICENSE - tests/wellknown-compiled/LICENSE - tests/wellknown/LICENSE - tonic-build/LICENSE - tonic-health/LICENSE - tonic-reflection/LICENSE - tonic-types/LICENSE - tonic-web/LICENSE - tonic/LICENSE -Copyright: 2020, Lucio Franco -License: Expat - FIXME - Files: examples/proto/echo/echo.proto examples/proto/helloworld/helloworld.proto examples/proto/routeguide/route_guide.proto @@ -410,6 +385,18 @@ License: Apache-2.0 FIXME +Files: LICENSE + tonic-build/LICENSE + tonic-health/LICENSE + tonic-reflection/LICENSE + tonic-types/LICENSE + tonic-web/LICENSE + tonic/LICENSE +Copyright: 2020, Lucio Franco + 2025, Lucio Franco +License: Expat + FIXME + Files: tonic-health/proto/health.proto tonic-reflection/proto/reflection_v1.proto Copyright: 2015, The gRPC Authors diff -Nru rust-tonic-0.12.3+dfsg/debian/patches/1002_fence_tests.patch rust-tonic-0.13.0+dfsg/debian/patches/1002_fence_tests.patch --- rust-tonic-0.12.3+dfsg/debian/patches/1002_fence_tests.patch 2025-03-15 15:50:58.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/debian/patches/1002_fence_tests.patch 2025-04-10 16:41:03.000000000 +0000 @@ -5,11 +5,11 @@ This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ --- a/tonic/src/service/interceptor.rs +++ b/tonic/src/service/interceptor.rs -@@ -306,6 +306,7 @@ +@@ -327,6 +327,7 @@ } #[tokio::test] + #[cfg(feature = "transport")] async fn doesnt_change_http_method() { - let svc = tower::service_fn(|request: http::Request>| async move { + let svc = tower::service_fn(|request: http::Request<()>| async move { assert_eq!(request.method(), http::Method::OPTIONS); diff -Nru rust-tonic-0.12.3+dfsg/debian/patches/1003_test_features.patch rust-tonic-0.13.0+dfsg/debian/patches/1003_test_features.patch --- rust-tonic-0.12.3+dfsg/debian/patches/1003_test_features.patch 1970-01-01 00:00:00.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/debian/patches/1003_test_features.patch 2025-04-10 16:41:03.000000000 +0000 @@ -0,0 +1,17 @@ +Description: enable dependency features needed by tests +Author: Blair Noctis +Last-Update: 2025-04-10 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/tonic/Cargo.toml ++++ b/tonic/Cargo.toml +@@ -101,7 +101,7 @@ + quickcheck_macros = "1.0" + static_assertions = "1.0" + tokio = {version = "1.0", features = ["rt", "macros"]} +-tower = {version = "0.5", features = ["load-shed", "timeout"]} ++tower = {version = "0.5", features = ["load-shed", "timeout", "util", "limit"]} + + [lints] + workspace = true + diff -Nru rust-tonic-0.12.3+dfsg/debian/patches/2001_axum.patch rust-tonic-0.13.0+dfsg/debian/patches/2001_axum.patch --- rust-tonic-0.12.3+dfsg/debian/patches/2001_axum.patch 1970-01-01 00:00:00.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/debian/patches/2001_axum.patch 2025-04-10 16:41:03.000000000 +0000 @@ -0,0 +1,16 @@ +Description: use older branch of crate axum +Forwarded: not-needed +Last-Update: 2025-04-10 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/tonic/Cargo.toml ++++ b/tonic/Cargo.toml +@@ -81,7 +81,7 @@ + socket2 = { version = "0.5", optional = true, features = ["all"] } + tokio = {version = "1", default-features = false, optional = true} + tower = {version = "0.5", default-features = false, optional = true} +-axum = {version = "0.8", default-features = false, optional = true} ++axum = {version = "0.7.9", default-features = false, optional = true} + + # rustls + rustls-native-certs = { version = "0.8", optional = true } diff -Nru rust-tonic-0.12.3+dfsg/debian/patches/2001_prost.patch rust-tonic-0.13.0+dfsg/debian/patches/2001_prost.patch --- rust-tonic-0.12.3+dfsg/debian/patches/2001_prost.patch 2025-03-15 15:50:58.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/debian/patches/2001_prost.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,327 +0,0 @@ -Description: accept older branch of crates prost prost-build prost-types -Author: Jonas Smedegaard -Forwarded: not-needed -Last-Update: 2025-03-10 ---- -This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ ---- a/examples/Cargo.toml -+++ b/examples/Cargo.toml -@@ -290,7 +290,7 @@ - [dependencies] - # Common dependencies - tokio = { version = "1.0", features = ["rt-multi-thread", "macros"] } --prost = "0.13" -+prost = ">= 0.12.6, <= 0.13" - tonic = { path = "../tonic" } - # Optional dependencies - tonic-web = { path = "../tonic-web", optional = true } -@@ -306,7 +306,7 @@ - serde_json = { version = "1.0", optional = true } - tracing = { version = "0.1.16", optional = true } - tracing-subscriber = { version = "0.3", features = ["tracing-log", "fmt"], optional = true } --prost-types = { version = "0.13", optional = true } -+prost-types = { version = ">= 0.12.6, <= 0.13", optional = true } - http = { version = "1", optional = true } - http-body = { version = "1", optional = true } - http-body-util = { version = "0.1", optional = true } ---- a/interop/Cargo.toml -+++ b/interop/Cargo.toml -@@ -22,7 +22,7 @@ - http = "1" - http-body = "1" - hyper = "1" --prost = "0.13" -+prost = ">= 0.12.6, <= 0.13" - tokio = {version = "1.0", features = ["rt-multi-thread", "time", "macros"]} - tokio-stream = "0.1" - tonic = {path = "../tonic", features = ["tls"]} ---- a/tests/ambiguous_methods/Cargo.toml -+++ b/tests/ambiguous_methods/Cargo.toml -@@ -9,7 +9,7 @@ - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - - [dependencies] --prost = "0.13" -+prost = ">= 0.12.6, <= 0.13" - tonic = {path = "../../tonic"} - - [build-dependencies] ---- a/tests/compression/Cargo.toml -+++ b/tests/compression/Cargo.toml -@@ -15,7 +15,7 @@ - hyper-util = "0.1" - paste = "1.0.12" - pin-project = "1.0" --prost = "0.13" -+prost = ">= 0.12.6, <= 0.13" - tokio = {version = "1.0", features = ["macros", "rt-multi-thread", "net"]} - tokio-stream = "0.1" - tonic = {path = "../../tonic", features = ["gzip", "zstd"]} ---- a/tests/default_stubs/Cargo.toml -+++ b/tests/default_stubs/Cargo.toml -@@ -9,7 +9,7 @@ - [dependencies] - tokio = {version = "1.0", features = ["macros", "rt-multi-thread", "net"]} - tokio-stream = {version = "0.1", features = ["net"]} --prost = "0.13" -+prost = ">= 0.12.6, <= 0.13" - tonic = {path = "../../tonic"} - - [build-dependencies] ---- a/tests/deprecated_methods/Cargo.toml -+++ b/tests/deprecated_methods/Cargo.toml -@@ -8,9 +8,9 @@ - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - - [dependencies] --prost = "0.13" -+prost = ">= 0.12.6, <= 0.13" - tonic = { path = "../../tonic" } - - [build-dependencies] --prost-build = "0.13" -+prost-build = ">= 0.12.6, <= 0.13" - tonic-build = { path = "../../tonic-build" } ---- a/tests/disable_comments/Cargo.toml -+++ b/tests/disable_comments/Cargo.toml -@@ -9,9 +9,9 @@ - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - - [dependencies] --prost = "0.13" -+prost = ">= 0.12.6, <= 0.13" - tonic = { path = "../../tonic" } - - [build-dependencies] --prost-build = "0.13" -+prost-build = ">= 0.12.6, <= 0.13" - tonic-build = { path = "../../tonic-build" } ---- a/tests/extern_path/my_application/Cargo.toml -+++ b/tests/extern_path/my_application/Cargo.toml -@@ -9,7 +9,7 @@ - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - - [dependencies] --prost = "0.13" -+prost = ">= 0.12.6, <= 0.13" - tonic = {path = "../../../tonic"} - uuid = {package = "uuid1", path = "../uuid"} - ---- a/tests/extern_path/uuid/Cargo.toml -+++ b/tests/extern_path/uuid/Cargo.toml -@@ -9,6 +9,6 @@ - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - - [dependencies] --prost = "0.13" -+prost = ">= 0.12.6, <= 0.13" - [build-dependencies] --prost-build = "0.13" -+prost-build = ">= 0.12.6, <= 0.13" ---- a/tests/included_service/Cargo.toml -+++ b/tests/included_service/Cargo.toml -@@ -9,7 +9,7 @@ - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - - [dependencies] --prost = "0.13" -+prost = ">= 0.12.6, <= 0.13" - tonic = {path = "../../tonic"} - - [build-dependencies] ---- a/tests/integration_tests/Cargo.toml -+++ b/tests/integration_tests/Cargo.toml -@@ -10,7 +10,7 @@ - - [dependencies] - bytes = "1.0" --prost = "0.13" -+prost = ">= 0.12.6, <= 0.13" - tokio = {version = "1.0", features = ["macros", "rt-multi-thread", "net", "sync"]} - tonic = {path = "../../tonic"} - tracing-subscriber = {version = "0.3"} ---- a/tests/root-crate-path/Cargo.toml -+++ b/tests/root-crate-path/Cargo.toml -@@ -7,7 +7,7 @@ - version = "0.1.0" - - [dependencies] --prost = "0.13" -+prost = ">= 0.12.6, <= 0.13" - tonic = {path = "../../tonic"} - - [build-dependencies] ---- a/tests/same_name/Cargo.toml -+++ b/tests/same_name/Cargo.toml -@@ -9,7 +9,7 @@ - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - - [dependencies] --prost = "0.13" -+prost = ">= 0.12.6, <= 0.13" - tonic = {path = "../../tonic"} - - [build-dependencies] ---- a/tests/service_named_result/Cargo.toml -+++ b/tests/service_named_result/Cargo.toml -@@ -7,7 +7,7 @@ - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - - [dependencies] --prost = "0.13" -+prost = ">= 0.12.6, <= 0.13" - tonic = {path = "../../tonic"} - - [build-dependencies] ---- a/tests/service_named_service/Cargo.toml -+++ b/tests/service_named_service/Cargo.toml -@@ -9,7 +9,7 @@ - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - - [dependencies] --prost = "0.13" -+prost = ">= 0.12.6, <= 0.13" - tonic = {path = "../../tonic"} - - [build-dependencies] ---- a/tests/skip_debug/Cargo.toml -+++ b/tests/skip_debug/Cargo.toml -@@ -9,7 +9,7 @@ - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - - [dependencies] --prost = "0.13" -+prost = ">= 0.12.6, <= 0.13" - tonic = { path = "../../tonic" } - - [build-dependencies] ---- a/tests/stream_conflict/Cargo.toml -+++ b/tests/stream_conflict/Cargo.toml -@@ -9,7 +9,7 @@ - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - - [dependencies] --prost = "0.13" -+prost = ">= 0.12.6, <= 0.13" - tonic = { path = "../../tonic" } - - [build-dependencies] ---- a/tests/use_arc_self/Cargo.toml -+++ b/tests/use_arc_self/Cargo.toml -@@ -8,7 +8,7 @@ - - [dependencies] - tokio-stream = "0.1" --prost = "0.13" -+prost = ">= 0.12.6, <= 0.13" - tonic = {path = "../../tonic", features = ["gzip"]} - - [build-dependencies] ---- a/tests/web/Cargo.toml -+++ b/tests/web/Cargo.toml -@@ -13,7 +13,7 @@ - http-body-util = "0.1" - hyper = "1" - hyper-util = "0.1" --prost = "0.13" -+prost = ">= 0.12.6, <= 0.13" - tokio = { version = "1", features = ["macros", "rt", "net"] } - tokio-stream = { version = "0.1", features = ["net"] } - tonic = { path = "../../tonic" } ---- a/tests/wellknown-compiled/Cargo.toml -+++ b/tests/wellknown-compiled/Cargo.toml -@@ -12,9 +12,9 @@ - doctest = false - - [dependencies] --prost = "0.13" -+prost = ">= 0.12.6, <= 0.13" - tonic = {path = "../../tonic"} - - [build-dependencies] --prost-build = "0.13" -+prost-build = ">= 0.12.6, <= 0.13" - tonic-build = {path = "../../tonic-build"} ---- a/tests/wellknown/Cargo.toml -+++ b/tests/wellknown/Cargo.toml -@@ -9,8 +9,8 @@ - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - - [dependencies] --prost = "0.13" --prost-types = "0.13" -+prost = ">= 0.12.6, <= 0.13" -+prost-types = ">= 0.12.6, <= 0.13" - tonic = {path = "../../tonic"} - - [build-dependencies] ---- a/tonic-build/Cargo.toml -+++ b/tonic-build/Cargo.toml -@@ -17,8 +17,8 @@ - [dependencies] - prettyplease = { version = "0.2" } - proc-macro2 = "1.0" --prost-build = { version = "0.13", optional = true } --prost-types = { version = "0.13", optional = true } -+prost-build = { version = ">= 0.12.6, <= 0.13", optional = true } -+prost-types = { version = ">= 0.12.6, <= 0.13", optional = true } - quote = "1.0" - syn = "2.0" - ---- a/tonic-health/Cargo.toml -+++ b/tonic-health/Cargo.toml -@@ -20,7 +20,7 @@ - - [dependencies] - async-stream = "0.3" --prost = "0.13" -+prost = ">= 0.12.6, <= 0.13" - tokio = {version = "1.0", features = ["sync"]} - tokio-stream = "0.1" - tonic = { version = "0.12", path = "../tonic", default-features = false, features = ["codegen", "prost"] } -@@ -28,7 +28,7 @@ - [dev-dependencies] - tokio = {version = "1.0", features = ["rt-multi-thread", "macros"]} - tokio-stream = "0.1" --prost-types = "0.13" -+prost-types = ">= 0.12.6, <= 0.13" - - [package.metadata.cargo_check_external_types] - allowed_external_types = [ ---- a/tonic-reflection/Cargo.toml -+++ b/tonic-reflection/Cargo.toml -@@ -26,8 +26,8 @@ - default = ["server"] - - [dependencies] --prost = "0.13" --prost-types = {version = "0.13", optional = true} -+prost = ">= 0.12.6, <= 0.13" -+prost-types = {version = ">= 0.12.6, <= 0.13", optional = true} - tokio = { version = "1.0", features = ["sync", "rt"], optional = true } - tokio-stream = {version = "0.1", features = ["net"], optional = true } - tonic = { version = "0.12", path = "../tonic", default-features = false, features = ["codegen", "prost"] } ---- a/tonic-types/Cargo.toml -+++ b/tonic-types/Cargo.toml -@@ -18,8 +18,8 @@ - version = "0.12.3" - - [dependencies] --prost = "0.13" --prost-types = "0.13" -+prost = ">= 0.12.6, <= 0.13" -+prost-types = ">= 0.12.6, <= 0.13" - tonic = { version = "0.12", path = "../tonic", default-features = false } - - [package.metadata.cargo_check_external_types] ---- a/tonic/Cargo.toml -+++ b/tonic/Cargo.toml -@@ -72,7 +72,7 @@ - tokio-stream = {version = "0.1.16", default-features = false} - - # prost --prost = {version = "0.13", default-features = false, features = ["std"], optional = true} -+prost = {version = ">= 0.12.6, <= 0.13", default-features = false, features = ["std"], optional = true} - - # codegen - async-trait = {version = "0.1.13", optional = true} diff -Nru rust-tonic-0.12.3+dfsg/debian/patches/2001_protox.patch rust-tonic-0.13.0+dfsg/debian/patches/2001_protox.patch --- rust-tonic-0.12.3+dfsg/debian/patches/2001_protox.patch 2025-03-15 18:14:07.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/debian/patches/2001_protox.patch 2025-04-10 16:41:03.000000000 +0000 @@ -2,7 +2,7 @@ This essentially reverts upstream git commit 6d93c1d. Author: Jonas Smedegaard Forwarded: not-needed -Last-Update: 2025-03-15 +Last-Update: 2025-04-10 --- This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ --- a/CONTRIBUTING.md @@ -19,32 +19,40 @@ cargo run --package codegen --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml -@@ -8,5 +8,4 @@ +@@ -5,9 +5,9 @@ + edition = "2021" [dependencies] - tempfile = "3.8.0" -protox = "0.7" + prettyplease = "0.2" + quote = "1" + syn = "2" + tempfile = "3.8.0" tonic-build = {path = "../tonic-build", default-features = false, features = ["prost", "cleanup-markdown"]} ++prost = "0.13" --- a/codegen/src/main.rs +++ b/codegen/src/main.rs -@@ -1,11 +1,4 @@ --use std::{ -- fs::File, -- io::{BufWriter, Write as _}, -- path::{Path, PathBuf}, --}; -- --use protox::prost::{bytes::BytesMut, Message as _}; --use tonic_build::FileDescriptorSet; -+use std::path::{Path, PathBuf}; - - fn main() { - // tonic-health -@@ -89,15 +82,12 @@ +@@ -4,7 +4,7 @@ + path::{Path, PathBuf}, + }; + +-use protox::prost::Message as _; ++use prost::Message as _; + use quote::quote; + use tonic_build::FileDescriptorSet; + +@@ -78,21 +78,21 @@ + .tempdir() + .unwrap(); + +- let iface_files = iface_files.iter().map(|&path| root_dir.join(path)); +- let include_dirs = include_dirs.iter().map(|&path| root_dir.join(path)); ++ let iface_files = iface_files.iter().map(|&path| root_dir.join(path)).collect::>(); ++ let include_dirs = include_dirs.iter().map(|&path| root_dir.join(path)).collect::>(); let out_dir = root_dir.join(out_dir); let file_descriptor_set_path = root_dir.join(file_descriptor_set_path); -- let fds = protox::compile(&iface_files, &include_dirs).unwrap(); +- let fds = protox::compile(iface_files, include_dirs).unwrap(); - - write_fds(&fds, &file_descriptor_set_path); - @@ -53,19 +61,12 @@ .build_server(build_server) .out_dir(&tempdir) - .compile_fds(fds) -+ .file_descriptor_set_path(file_descriptor_set_path) ++ .file_descriptor_set_path(&file_descriptor_set_path) + .compile_protos(&iface_files, &include_dirs) .unwrap(); ++ let fds_bytes = std::fs::read(&file_descriptor_set_path).unwrap(); ++ let fds = FileDescriptorSet::decode(&*fds_bytes).unwrap(); ++ write_fds(&fds, &file_descriptor_set_path); for path in std::fs::read_dir(tempdir.path()).unwrap() { -@@ -115,10 +105,3 @@ - std::fs::copy(&path, &to).unwrap(); - } - } -- --fn write_fds(fds: &FileDescriptorSet, path: &Path) { -- let mut writer = BufWriter::new(File::create(path).unwrap()); -- let mut buf = BytesMut::with_capacity(fds.encoded_len()); -- fds.encode(&mut buf).unwrap(); -- writer.write_all(&buf).unwrap(); --} + let path = path.unwrap().path(); diff -Nru rust-tonic-0.12.3+dfsg/debian/patches/2001_rustls-native-certs_1.patch rust-tonic-0.13.0+dfsg/debian/patches/2001_rustls-native-certs_1.patch --- rust-tonic-0.12.3+dfsg/debian/patches/2001_rustls-native-certs_1.patch 2025-03-16 19:50:26.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/debian/patches/2001_rustls-native-certs_1.patch 2025-04-10 16:41:03.000000000 +0000 @@ -7,31 +7,31 @@ This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ --- a/tonic/Cargo.toml +++ b/tonic/Cargo.toml -@@ -89,7 +89,7 @@ +@@ -83,7 +83,7 @@ + axum = {version = "0.7.9", default-features = false, optional = true} # rustls - rustls-pemfile = { version = "2.0", optional = true } -rustls-native-certs = { version = "0.8", optional = true } +rustls-native-certs = { version = "0.7", optional = true } - tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "tls12", "ring"], optional = true } + tokio-rustls = { version = "0.26.1", default-features = false, features = ["logging", "tls12"], optional = true } webpki-roots = { version = "0.26", optional = true } --- a/tonic/src/transport/channel/service/tls.rs +++ b/tonic/src/transport/channel/service/tls.rs -@@ -38,15 +38,7 @@ +@@ -60,12 +60,12 @@ #[cfg(feature = "tls-native-roots")] if with_native_roots { - let rustls_native_certs::CertificateResult { certs, errors, .. } = - rustls_native_certs::load_native_certs(); - if !errors.is_empty() { -- tracing::debug!("errors occured when loading native certs: {errors:?}"); -- } ++ let certs = //rustls_native_certs::CertificateResult { certs, errors, .. } = ++ rustls_native_certs::load_native_certs()?; ++ #[cfg(any())] if !errors.is_empty() { + tracing::debug!("errors occurred when loading native certs: {errors:?}"); + } - if certs.is_empty() { -- return Err(TlsError::NativeCertsNotFound.into()); -- } -- roots.add_parsable_certificates(certs); -+ roots.add_parsable_certificates(rustls_native_certs::load_native_certs()?); - } - - #[cfg(feature = "tls-webpki-roots")] ++ #[cfg(any())] if certs.is_empty() { + return Err(TlsError::NativeCertsNotFound.into()); + } + roots.add_parsable_certificates(certs); diff -Nru rust-tonic-0.12.3+dfsg/debian/patches/2001_rustls-native-certs_2.patch rust-tonic-0.13.0+dfsg/debian/patches/2001_rustls-native-certs_2.patch --- rust-tonic-0.12.3+dfsg/debian/patches/2001_rustls-native-certs_2.patch 2025-03-16 20:08:37.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/debian/patches/2001_rustls-native-certs_2.patch 2025-04-10 16:41:03.000000000 +0000 @@ -7,23 +7,23 @@ This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ --- a/tonic/Cargo.toml +++ b/tonic/Cargo.toml -@@ -89,7 +89,7 @@ +@@ -83,7 +83,7 @@ + axum = {version = "0.7.9", default-features = false, optional = true} # rustls - rustls-pemfile = { version = "2.0", optional = true } -rustls-native-certs = { version = "0.7", optional = true } +rustls-native-certs = { version = "0.6.3", optional = true } - tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "tls12", "ring"], optional = true } + tokio-rustls = { version = "0.26.1", default-features = false, features = ["logging", "tls12"], optional = true } webpki-roots = { version = "0.26", optional = true } --- a/tonic/src/transport/channel/service/tls.rs +++ b/tonic/src/transport/channel/service/tls.rs -@@ -38,7 +38,7 @@ - +@@ -61,7 +61,7 @@ #[cfg(feature = "tls-native-roots")] if with_native_roots { -- roots.add_parsable_certificates(rustls_native_certs::load_native_certs()?); -+ roots.add_parsable_certificates(rustls_native_certs::load_native_certs()?.into_iter().map(|cert| cert.0.into())); - } - - #[cfg(feature = "tls-webpki-roots")] + let certs = //rustls_native_certs::CertificateResult { certs, errors, .. } = +- rustls_native_certs::load_native_certs()?; ++ rustls_native_certs::load_native_certs()?.into_iter().map(|cert| cert.0.into()); + #[cfg(any())] if !errors.is_empty() { + tracing::debug!("errors occurred when loading native certs: {errors:?}"); + } diff -Nru rust-tonic-0.12.3+dfsg/debian/patches/2001_tokio-rustls.patch rust-tonic-0.13.0+dfsg/debian/patches/2001_tokio-rustls.patch --- rust-tonic-0.12.3+dfsg/debian/patches/2001_tokio-rustls.patch 1970-01-01 00:00:00.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/debian/patches/2001_tokio-rustls.patch 2025-04-10 16:41:03.000000000 +0000 @@ -0,0 +1,15 @@ +Description: adapt to removal of aws-lc-rs feature of tokio-rustls in Debian +Forwarded: not-needed +Last-Update: 2025-04-10 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/tonic/Cargo.toml ++++ b/tonic/Cargo.toml +@@ -28,7 +28,6 @@ + prost = ["dep:prost"] + _tls-any = ["dep:tokio-rustls", "dep:tokio", "tokio?/rt", "tokio?/macros"] # Internal. Please choose one of `tls-ring` or `tls-aws-lc` + tls-ring = ["_tls-any", "tokio-rustls/ring"] +-tls-aws-lc = ["_tls-any", "tokio-rustls/aws-lc-rs"] + tls-native-roots = ["_tls-any", "channel", "dep:rustls-native-certs"] + tls-webpki-roots = ["_tls-any","channel", "dep:webpki-roots"] + router = ["dep:axum", "dep:tower", "tower?/util"] diff -Nru rust-tonic-0.12.3+dfsg/debian/patches/2001_tower-http.patch rust-tonic-0.13.0+dfsg/debian/patches/2001_tower-http.patch --- rust-tonic-0.12.3+dfsg/debian/patches/2001_tower-http.patch 2025-03-15 18:14:07.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/debian/patches/2001_tower-http.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ ---- a/examples/Cargo.toml -+++ b/examples/Cargo.toml -@@ -318,7 +318,7 @@ - tokio-rustls = { version = "0.26", optional = true, features = ["ring", "tls12"], default-features = false } - hyper-rustls = { version = "0.27.0", features = ["http2", "ring", "tls12"], optional = true, default-features = false } - rustls-pemfile = { version = "2.0.0", optional = true } --tower-http = { version = "0.5", optional = true } -+tower-http = { version = ">= 0.5, <= 0.6", optional = true } - pin-project = { version = "1.0.11", optional = true } - - [build-dependencies] ---- a/tests/compression/Cargo.toml -+++ b/tests/compression/Cargo.toml -@@ -20,7 +20,7 @@ - tokio-stream = "0.1" - tonic = {path = "../../tonic", features = ["gzip", "zstd"]} - tower = {version = "0.4", features = []} --tower-http = {version = "0.5", features = ["map-response-body", "map-request-body"]} -+tower-http = {version = ">= 0.5, <= 0.6", features = ["map-response-body", "map-request-body"]} - - [build-dependencies] - tonic-build = {path = "../../tonic-build" } ---- a/tests/integration_tests/Cargo.toml -+++ b/tests/integration_tests/Cargo.toml -@@ -22,7 +22,7 @@ - hyper-util = "0.1" - tokio-stream = {version = "0.1.5", features = ["net"]} - tower = {version = "0.4", features = []} --tower-http = { version = "0.5", features = ["set-header", "trace"] } -+tower-http = { version = ">= 0.5, <= 0.6", features = ["set-header", "trace"] } - tower-service = "0.3" - tracing = "0.1" - ---- a/tonic-web/Cargo.toml -+++ b/tonic-web/Cargo.toml -@@ -25,7 +25,7 @@ - tonic = { version = "0.12", path = "../tonic", default-features = false } - tower-service = "0.3" - tower-layer = "0.3" --tower-http = { version = "0.5", features = ["cors"] } -+tower-http = { version = ">= 0.5, <= 0.6", features = ["cors"] } - tracing = "0.1" - - [dev-dependencies] diff -Nru rust-tonic-0.12.3+dfsg/debian/patches/2002_no_feature_cleanup-markdown.patch rust-tonic-0.13.0+dfsg/debian/patches/2002_no_feature_cleanup-markdown.patch --- rust-tonic-0.12.3+dfsg/debian/patches/2002_no_feature_cleanup-markdown.patch 2025-03-15 18:14:07.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/debian/patches/2002_no_feature_cleanup-markdown.patch 2025-04-10 16:41:03.000000000 +0000 @@ -11,15 +11,16 @@ [features] default = ["transport", "prost"] prost = ["prost-build", "dep:prost-types"] --cleanup-markdown = ["prost", "prost-build/cleanup-markdown"] +-cleanup-markdown = ["prost-build?/cleanup-markdown"] transport = [] - [package.metadata.docs.rs] + [lints] --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml -@@ -8,4 +8,4 @@ - - [dependencies] +@@ -9,5 +9,5 @@ + quote = "1" + syn = "2" tempfile = "3.8.0" -tonic-build = {path = "../tonic-build", default-features = false, features = ["prost", "cleanup-markdown"]} +tonic-build = {path = "../tonic-build", default-features = false, features = ["prost"]} + prost = "0.13" diff -Nru rust-tonic-0.12.3+dfsg/debian/patches/2002_no_net.patch rust-tonic-0.13.0+dfsg/debian/patches/2002_no_net.patch --- rust-tonic-0.12.3+dfsg/debian/patches/2002_no_net.patch 2025-03-15 18:14:07.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/debian/patches/2002_no_net.patch 2025-04-10 16:41:03.000000000 +0000 @@ -12,5 +12,5 @@ #[tokio::test] +#[ignore] async fn connect_handles_tls() { - TestClient::connect("https://example.com").await.unwrap(); - } + rustls::crypto::ring::default_provider() + .install_default() diff -Nru rust-tonic-0.12.3+dfsg/debian/patches/2002_no_webpki-roots.patch rust-tonic-0.13.0+dfsg/debian/patches/2002_no_webpki-roots.patch --- rust-tonic-0.12.3+dfsg/debian/patches/2002_no_webpki-roots.patch 2025-03-16 19:39:30.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/debian/patches/2002_no_webpki-roots.patch 2025-04-10 16:41:03.000000000 +0000 @@ -6,18 +6,18 @@ This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ --- a/tonic/Cargo.toml +++ b/tonic/Cargo.toml -@@ -31,7 +31,6 @@ - tls = ["dep:rustls-pemfile", "dep:tokio-rustls", "dep:tokio", "tokio?/rt", "tokio?/macros"] - tls-roots = ["tls-native-roots"] # Deprecated. Please use `tls-native-roots` instead. - tls-native-roots = ["tls", "channel", "dep:rustls-native-certs"] --tls-webpki-roots = ["tls", "channel", "dep:webpki-roots"] +@@ -29,7 +29,6 @@ + _tls-any = ["dep:tokio-rustls", "dep:tokio", "tokio?/rt", "tokio?/macros"] # Internal. Please choose one of `tls-ring` or `tls-aws-lc` + tls-ring = ["_tls-any", "tokio-rustls/ring"] + tls-native-roots = ["_tls-any", "channel", "dep:rustls-native-certs"] +-tls-webpki-roots = ["_tls-any","channel", "dep:webpki-roots"] router = ["dep:axum", "dep:tower", "tower?/util"] server = [ - "router", -@@ -91,7 +90,6 @@ - rustls-pemfile = { version = "2.0", optional = true } + "dep:h2", +@@ -85,7 +84,6 @@ + # rustls rustls-native-certs = { version = "0.6.3", optional = true } - tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "tls12", "ring"], optional = true } + tokio-rustls = { version = "0.26.1", default-features = false, features = ["logging", "tls12"], optional = true } -webpki-roots = { version = "0.26", optional = true } # compression diff -Nru rust-tonic-0.12.3+dfsg/debian/patches/2003_no_bench.patch rust-tonic-0.13.0+dfsg/debian/patches/2003_no_bench.patch --- rust-tonic-0.12.3+dfsg/debian/patches/2003_no_bench.patch 2025-03-15 15:50:58.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/debian/patches/2003_no_bench.patch 2025-04-10 16:41:03.000000000 +0000 @@ -12,7 +12,7 @@ name = "tonic" # When releasing to crates.io: # - Remove path dependencies -@@ -52,10 +53,6 @@ +@@ -48,10 +49,6 @@ ] transport = ["server", "channel"] @@ -23,15 +23,15 @@ [dependencies] base64 = "0.22" bytes = "1.0" -@@ -99,7 +96,6 @@ +@@ -93,7 +90,6 @@ hyper-timeout = {version = "0.5", optional = true} [dev-dependencies] -bencher = "0.1.5" quickcheck = "1.0" quickcheck_macros = "1.0" - rand = "0.8" -@@ -137,7 +133,3 @@ + static_assertions = "1.0" +@@ -131,7 +127,3 @@ "tower_layer::stack::Stack", "tower_layer::identity::Identity", ] diff -Nru rust-tonic-0.12.3+dfsg/debian/patches/2005_matchit_route_catch-all.patch rust-tonic-0.13.0+dfsg/debian/patches/2005_matchit_route_catch-all.patch --- rust-tonic-0.12.3+dfsg/debian/patches/2005_matchit_route_catch-all.patch 1970-01-01 00:00:00.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/debian/patches/2005_matchit_route_catch-all.patch 2025-04-10 16:41:03.000000000 +0000 @@ -0,0 +1,20 @@ +Description: adapt to syntax change in matchit + tonic 0.13 uses axum 0.8, but we have only 0.7 in Debian, + so tonic is patched to use 0.7. + axum 0.8 uses matchit 0.8 while 0.7 uses 0.7. + matchit 0.8 changed the "catch-all parameter" syntax + from "*rest" to "{*rest}". +Forwarded: not-needed +Author: Blair Noctis +Last-Update: 2025-04-11 +--- a/tonic/src/service/router.rs ++++ b/tonic/src/service/router.rs +@@ -87,7 +87,7 @@ + S::Future: Send + 'static, + { + self.router = self.router.route_service( +- &format!("/{}/{{*rest}}", S::NAME), ++ &format!("/{}/*rest", S::NAME), + svc.map_request(|req: Request| req.map(Body::new)), + ); + self diff -Nru rust-tonic-0.12.3+dfsg/debian/patches/series rust-tonic-0.13.0+dfsg/debian/patches/series --- rust-tonic-0.12.3+dfsg/debian/patches/series 2025-03-16 19:46:32.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/debian/patches/series 2025-04-10 16:41:03.000000000 +0000 @@ -1,11 +1,13 @@ 1002_fence_tests.patch -2001_prost.patch +1003_test_features.patch +2001_axum.patch +2001_tokio-rustls.patch 2001_protox.patch 2001_rustls-native-certs_1.patch 2001_rustls-native-certs_2.patch -2001_tower-http.patch 2002_no_feature_cleanup-markdown.patch 2002_no_net.patch 2002_no_webpki-roots.patch 2003_no_bench.patch 2004_reduce_tests.patch +2005_matchit_route_catch-all.patch diff -Nru rust-tonic-0.12.3+dfsg/debian/tests/control rust-tonic-0.13.0+dfsg/debian/tests/control --- rust-tonic-0.12.3+dfsg/debian/tests/control 2025-04-08 11:18:22.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/debian/tests/control 2025-04-10 16:41:03.000000000 +0000 @@ -1,6 +1,6 @@ # check single-feature tests only on amd64 -Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.12.3 +Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.13.0 --all-targets --all-features -- --skip codec::prost::tests::encode_too_big @@ -13,7 +13,7 @@ librust-quickcheck-macros-1+default-dev, librust-rand-0.8+default-dev, librust-static-assertions-1+default-dev, - librust-tonic-0.12-dev, + librust-tonic-0.13-dev, librust-tower-0.4+full-dev, librust-tower-http-0.6+default-dev, librust-tower-http-0.6+map-request-body-dev, @@ -23,7 +23,7 @@ Restrictions: allow-stderr, -Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.12.3 +Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.13.0 --all-targets --no-default-features -- --skip codec::prost::tests::encode_too_big @@ -36,7 +36,7 @@ librust-quickcheck-macros-1+default-dev, librust-rand-0.8+default-dev, librust-static-assertions-1+default-dev, - librust-tonic-0.12-dev, + librust-tonic-0.13-dev, librust-tower-0.4+full-dev, librust-tower-http-0.6+default-dev, librust-tower-http-0.6+map-request-body-dev, @@ -46,7 +46,7 @@ Restrictions: allow-stderr, -Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.12.3 +Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.13.0 --all-targets -- --skip codec::prost::tests::encode_too_big @@ -59,7 +59,7 @@ librust-quickcheck-macros-1+default-dev, librust-rand-0.8+default-dev, librust-static-assertions-1+default-dev, - librust-tonic-0.12+default-dev, + librust-tonic-0.13+default-dev, librust-tower-0.4+full-dev, librust-tower-http-0.6+default-dev, librust-tower-http-0.6+map-request-body-dev, @@ -69,7 +69,7 @@ Restrictions: allow-stderr, -Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.12.3 +Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.13.0 --all-targets --no-default-features --features channel -- --skip codec::prost::tests::encode_too_big @@ -82,7 +82,7 @@ librust-quickcheck-macros-1+default-dev, librust-rand-0.8+default-dev, librust-static-assertions-1+default-dev, - librust-tonic-0.12+channel-dev, + librust-tonic-0.13+channel-dev, librust-tower-0.4+full-dev, librust-tower-http-0.6+default-dev, librust-tower-http-0.6+map-request-body-dev, @@ -93,7 +93,7 @@ allow-stderr, Architecture: amd64 -Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.12.3 +Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.13.0 --all-targets --no-default-features --features codegen -- --skip codec::prost::tests::encode_too_big @@ -106,7 +106,7 @@ librust-quickcheck-macros-1+default-dev, librust-rand-0.8+default-dev, librust-static-assertions-1+default-dev, - librust-tonic-0.12+codegen-dev, + librust-tonic-0.13+codegen-dev, librust-tower-0.4+full-dev, librust-tower-http-0.6+default-dev, librust-tower-http-0.6+map-request-body-dev, @@ -117,7 +117,7 @@ allow-stderr, Architecture: amd64 -Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.12.3 +Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.13.0 --all-targets --no-default-features --features gzip -- --skip codec::prost::tests::encode_too_big @@ -130,7 +130,7 @@ librust-quickcheck-macros-1+default-dev, librust-rand-0.8+default-dev, librust-static-assertions-1+default-dev, - librust-tonic-0.12+gzip-dev, + librust-tonic-0.13+gzip-dev, librust-tower-0.4+full-dev, librust-tower-http-0.6+default-dev, librust-tower-http-0.6+map-request-body-dev, @@ -141,12 +141,12 @@ allow-stderr, Architecture: amd64 -Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.12.3 - --all-targets --no-default-features --features prost +Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.13.0 + --all-targets --no-default-features --features deflate -- --skip codec::prost::tests::encode_too_big Features: - test-name=rust-tonic:prost, + test-name=rust-tonic:deflate, Depends: dh-rust, librust-paste-1+default-dev, @@ -154,7 +154,7 @@ librust-quickcheck-macros-1+default-dev, librust-rand-0.8+default-dev, librust-static-assertions-1+default-dev, - librust-tonic-0.12+prost-dev, + librust-tonic-0.13+deflate-dev, librust-tower-0.4+full-dev, librust-tower-http-0.6+default-dev, librust-tower-http-0.6+map-request-body-dev, @@ -165,12 +165,12 @@ allow-stderr, Architecture: amd64 -Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.12.3 - --all-targets --no-default-features --features router +Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.13.0 + --all-targets --no-default-features --features prost -- --skip codec::prost::tests::encode_too_big Features: - test-name=rust-tonic:router, + test-name=rust-tonic:prost, Depends: dh-rust, librust-paste-1+default-dev, @@ -178,7 +178,7 @@ librust-quickcheck-macros-1+default-dev, librust-rand-0.8+default-dev, librust-static-assertions-1+default-dev, - librust-tonic-0.12+router-dev, + librust-tonic-0.13+prost-dev, librust-tower-0.4+full-dev, librust-tower-http-0.6+default-dev, librust-tower-http-0.6+map-request-body-dev, @@ -189,12 +189,12 @@ allow-stderr, Architecture: amd64 -Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.12.3 - --all-targets --no-default-features --features server +Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.13.0 + --all-targets --no-default-features --features router -- --skip codec::prost::tests::encode_too_big Features: - test-name=rust-tonic:server, + test-name=rust-tonic:router, Depends: dh-rust, librust-paste-1+default-dev, @@ -202,7 +202,7 @@ librust-quickcheck-macros-1+default-dev, librust-rand-0.8+default-dev, librust-static-assertions-1+default-dev, - librust-tonic-0.12+server-dev, + librust-tonic-0.13+router-dev, librust-tower-0.4+full-dev, librust-tower-http-0.6+default-dev, librust-tower-http-0.6+map-request-body-dev, @@ -213,12 +213,12 @@ allow-stderr, Architecture: amd64 -Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.12.3 - --all-targets --no-default-features --features tls +Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.13.0 + --all-targets --no-default-features --features server -- --skip codec::prost::tests::encode_too_big Features: - test-name=rust-tonic:tls, + test-name=rust-tonic:server, Depends: dh-rust, librust-paste-1+default-dev, @@ -226,7 +226,7 @@ librust-quickcheck-macros-1+default-dev, librust-rand-0.8+default-dev, librust-static-assertions-1+default-dev, - librust-tonic-0.12+tls-dev, + librust-tonic-0.13+server-dev, librust-tower-0.4+full-dev, librust-tower-http-0.6+default-dev, librust-tower-http-0.6+map-request-body-dev, @@ -237,12 +237,12 @@ allow-stderr, Architecture: amd64 -Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.12.3 - --all-targets --no-default-features --features tls-native-roots +Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.13.0 + --all-targets --no-default-features --features tls-ring -- --skip codec::prost::tests::encode_too_big Features: - test-name=rust-tonic:tls-native-roots, + test-name=rust-tonic:tls-ring, Depends: dh-rust, librust-paste-1+default-dev, @@ -250,7 +250,7 @@ librust-quickcheck-macros-1+default-dev, librust-rand-0.8+default-dev, librust-static-assertions-1+default-dev, - librust-tonic-0.12+tls-native-roots-dev, + librust-tonic-0.13+tls-dev, librust-tower-0.4+full-dev, librust-tower-http-0.6+default-dev, librust-tower-http-0.6+map-request-body-dev, @@ -261,12 +261,12 @@ allow-stderr, Architecture: amd64 -Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.12.3 - --all-targets --no-default-features --features tls-roots +Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.13.0 + --all-targets --no-default-features --features tls-native-roots -- --skip codec::prost::tests::encode_too_big Features: - test-name=rust-tonic:tls-roots, + test-name=rust-tonic:tls-native-roots, Depends: dh-rust, librust-paste-1+default-dev, @@ -274,7 +274,7 @@ librust-quickcheck-macros-1+default-dev, librust-rand-0.8+default-dev, librust-static-assertions-1+default-dev, - librust-tonic-0.12+tls-roots-dev, + librust-tonic-0.13+tls-native-roots-dev, librust-tower-0.4+full-dev, librust-tower-http-0.6+default-dev, librust-tower-http-0.6+map-request-body-dev, @@ -285,7 +285,7 @@ allow-stderr, Architecture: amd64 -Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.12.3 +Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.13.0 --all-targets --no-default-features --features transport -- --skip codec::prost::tests::encode_too_big @@ -299,7 +299,7 @@ librust-quickcheck-macros-1+default-dev, librust-rand-0.8+default-dev, librust-static-assertions-1+default-dev, - librust-tonic-0.12+transport-dev, + librust-tonic-0.13+transport-dev, librust-tower-0.4+full-dev, librust-tower-http-0.6+default-dev, librust-tower-http-0.6+map-request-body-dev, @@ -310,7 +310,7 @@ allow-stderr, Architecture: amd64 -Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.12.3 +Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic 0.13.0 --all-targets --no-default-features --features zstd -- --skip codec::prost::tests::encode_too_big @@ -324,7 +324,7 @@ librust-quickcheck-macros-1+default-dev, librust-rand-0.8+default-dev, librust-static-assertions-1+default-dev, - librust-tonic-0.12+zstd-dev, + librust-tonic-0.13+zstd-dev, librust-tower-0.4+full-dev, librust-tower-http-0.6+default-dev, librust-tower-http-0.6+map-request-body-dev, @@ -335,84 +335,64 @@ allow-stderr, Architecture: amd64 -Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic-build 0.12.3 +Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic-build 0.13.0 --all-targets --all-features Features: test-name=rust-tonic-build:@, Depends: dh-rust, - librust-tonic-build-0.12-dev, + librust-tonic-build-0.13-dev, Restrictions: allow-stderr, -Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic-build 0.12.3 +Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic-build 0.13.0 --all-targets --no-default-features Features: test-name=rust-tonic-build:, Depends: dh-rust, - librust-tonic-build-0.12-dev, + librust-tonic-build-0.13-dev, Restrictions: allow-stderr, -Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic-build 0.12.3 +Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic-build 0.13.0 --all-targets Features: test-name=rust-tonic-build:default, Depends: dh-rust, - librust-tonic-build-0.12+default-dev, + librust-tonic-build-0.13+default-dev, Restrictions: allow-stderr, -Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic-build 0.12.3 +Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic-build 0.13.0 --all-targets --no-default-features --features prost Features: test-name=rust-tonic-build:prost, Depends: dh-rust, - librust-tonic-build-0.12+prost-dev, + librust-tonic-build-0.13+prost-dev, Restrictions: allow-stderr, Architecture: amd64 -Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic-build 0.12.3 +Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic-build 0.13.0 --all-targets --no-default-features --features transport Features: test-name=rust-tonic-build:transport, Depends: dh-rust, - librust-tonic-build-0.12+transport-dev, + librust-tonic-build-0.13+transport-dev, Restrictions: allow-stderr, Architecture: amd64 -Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic-types 0.12.3 +Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic-types 0.13.0 --all-targets --all-features Features: test-name=rust-tonic-types:@, Depends: dh-rust, - librust-tonic-types-0.12-dev, -Restrictions: - allow-stderr, - -Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic-types 0.12.3 - --all-targets --no-default-features -Features: - test-name=rust-tonic-types:, -Depends: - dh-rust, - librust-tonic-types-0.12-dev, -Restrictions: - allow-stderr, - -Test-Command: /usr/share/dh-rust/bin/cargo-auto-test tonic-types 0.12.3 - --all-targets -Features: - test-name=rust-tonic-types:default, -Depends: - dh-rust, - librust-tonic-types-0.12+default-dev, + librust-tonic-types-0.13-dev, Restrictions: allow-stderr, diff -Nru rust-tonic-0.12.3+dfsg/deny.toml rust-tonic-0.13.0+dfsg/deny.toml --- rust-tonic-0.12.3+dfsg/deny.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/deny.toml 2025-03-25 18:49:19.000000000 +0000 @@ -1,26 +1,39 @@ [graph] all-features = true +exclude = ["examples"] + +[advisories] +ignore = [ + { id = "RUSTSEC-2024-0436", reason = "paste crate is unmaintained. aws-lc-rs, aws-lc-sys and compression test depend on it." }, +] [bans] multiple-versions = "deny" deny = [ # color-backtrace is nice but brings in too many dependencies and that are often outdated, so not worth it for us. - { name = "color-backtrace" }, + { crate = "color-backtrace" }, # dirs crate has a lot of dependencies and there are better alternatives - { name = "dirs" }, - { name = "dirs-sys" }, + { crate = "dirs" }, + { crate = "dirs-sys" }, # deprecated - { name = "quickersort" }, + { crate = "quickersort" }, # term is not fully maintained, and termcolor is replacing it - { name = "term" }, + { crate = "term" }, +] +skip = [ + { crate = "getrandom@0.2.15", reason = "quickcheck depends on rand which depends on it" }, + { crate = "wasi@0.11.0+wasi-snapshot-preview1", reason = "quickcheck depends on rand which depends on it" }, + { crate = "itertools@0.12.1", reason = "aws-lc-sys depends on bindgen which depends on it" }, + { crate = "unicode-width@0.1.14", reason = "protox depends on miette wich depends on it" }, + { crate = "rustix@0.38.44", reason = "aws-lc-sys depends on bindgen which depends on it" }, + { crate = "linux-raw-sys@0.4.15", reason = "rustix 0.38.44 depends on it" }, ] skip-tree = [ - { name = "windows-sys" }, - { name = "hermit-abi" }, - { name = "examples" }, + { crate = "windows-sys" }, + { crate = "hermit-abi" }, ] [licenses] @@ -32,14 +45,13 @@ "ISC", "MIT", "OpenSSL", - "Zlib", - "Unicode-DFS-2016", + "Unicode-3.0", "MPL-2.0", "BSD-3-Clause", ] [[licenses.clarify]] -name = "ring" +crate = "ring" # SPDX considers OpenSSL to encompass both the OpenSSL and SSLeay licenses # https://spdx.org/licenses/OpenSSL.html # ISC - Both BoringSSL and ring use this for their new files diff -Nru rust-tonic-0.12.3+dfsg/examples/Cargo.toml rust-tonic-0.13.0+dfsg/examples/Cargo.toml --- rust-tonic-0.12.3+dfsg/examples/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/examples/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -3,8 +3,6 @@ edition = "2021" license = "MIT" name = "examples" -publish = false -version = "0.1.0" [[bin]] name = "helloworld-server" @@ -51,12 +49,10 @@ [[bin]] name = "dynamic-load-balance-client" path = "src/dynamic_load_balance/client.rs" -required-features = ["dynamic-load-balance"] [[bin]] name = "dynamic-load-balance-server" path = "src/dynamic_load_balance/server.rs" -required-features = ["dynamic-load-balance"] [[bin]] name = "tls-client" @@ -99,16 +95,6 @@ required-features = ["tower"] [[bin]] -name = "timeout-server" -path = "src/timeout/server.rs" -required-features = ["timeout"] - -[[bin]] -name = "timeout-client" -path = "src/timeout/client.rs" -required-features = ["timeout"] - -[[bin]] name = "multiplex-server" path = "src/multiplex/server.rs" @@ -132,8 +118,13 @@ required-features = ["tracing"] [[bin]] -name = "uds-client" -path = "src/uds/client.rs" +name = "uds-client-standard" +path = "src/uds/client_standard.rs" +required-features = ["uds"] + +[[bin]] +name = "uds-client-with-connector" +path = "src/uds/client_with_connector.rs" required-features = ["uds"] [[bin]] @@ -262,29 +253,27 @@ [features] -gcp = ["dep:prost-types", "tonic/tls"] -routeguide = ["dep:async-stream", "tokio-stream", "dep:rand", "dep:serde", "dep:serde_json"] +gcp = ["dep:prost-types", "tonic/tls-ring"] +routeguide = ["dep:async-stream", "dep:tokio-stream", "dep:rand", "dep:serde", "dep:serde_json"] reflection = ["dep:tonic-reflection"] -autoreload = ["tokio-stream/net", "dep:listenfd"] +autoreload = ["dep:tokio-stream", "tokio-stream?/net", "dep:listenfd"] health = ["dep:tonic-health"] -grpc-web = ["dep:tonic-web", "dep:bytes", "dep:http", "dep:hyper", "dep:hyper-util", "dep:tracing-subscriber", "dep:tower"] +grpc-web = ["dep:tonic-web", "dep:bytes", "dep:http", "dep:hyper", "dep:hyper-util", "dep:tracing-subscriber", "dep:tower", "dep:tower-http", "tower-http?/cors"] tracing = ["dep:tracing", "dep:tracing-subscriber"] -uds = ["tokio-stream/net", "dep:tower", "dep:hyper", "dep:hyper-util"] -streaming = ["tokio-stream", "dep:h2"] -mock = ["tokio-stream", "dep:tower", "dep:hyper-util"] -tower = ["dep:hyper", "dep:hyper-util", "dep:tower", "tower?/timeout", "dep:http"] +uds = ["dep:tokio-stream", "tokio-stream?/net", "dep:tower", "dep:hyper", "dep:hyper-util"] +streaming = ["dep:tokio-stream", "dep:h2"] +mock = ["dep:tokio-stream", "dep:tower", "dep:hyper-util"] +tower = ["dep:tower", "dep:http"] json-codec = ["dep:serde", "dep:serde_json", "dep:bytes"] compression = ["tonic/gzip"] -tls = ["tonic/tls"] -tls-rustls = ["dep:http", "dep:hyper", "dep:hyper-util", "dep:hyper-rustls", "dep:tower", "tower-http/util", "tower-http/add-extension", "dep:rustls-pemfile", "dep:tokio-rustls", "dep:pin-project", "dep:http-body-util"] -dynamic-load-balance = ["dep:tower"] -timeout = ["tokio/time", "dep:tower", "tower?/timeout"] -tls-client-auth = ["tonic/tls"] +tls = ["tonic/tls-ring"] +tls-rustls = ["dep:http", "dep:hyper", "dep:hyper-util", "dep:hyper-rustls", "dep:tower", "tower-http/util", "tower-http/add-extension", "dep:tokio-rustls"] +tls-client-auth = ["tonic/tls-ring"] types = ["dep:tonic-types"] h2c = ["dep:hyper", "dep:tower", "dep:http", "dep:hyper-util"] cancellation = ["dep:tokio-util"] -full = ["gcp", "routeguide", "reflection", "autoreload", "health", "grpc-web", "tracing", "uds", "streaming", "mock", "tower", "json-codec", "compression", "tls", "tls-rustls", "dynamic-load-balance", "timeout", "tls-client-auth", "types", "cancellation", "h2c"] +full = ["gcp", "routeguide", "reflection", "autoreload", "health", "grpc-web", "tracing", "uds", "streaming", "mock", "tower", "json-codec", "compression", "tls", "tls-rustls", "tls-client-auth", "types", "cancellation", "h2c"] default = ["full"] [dependencies] @@ -300,8 +289,8 @@ async-stream = { version = "0.3", optional = true } tokio-stream = { version = "0.1", optional = true } tokio-util = { version = "0.7.8", optional = true } -tower = { version = "0.4", optional = true } -rand = { version = "0.8", optional = true } +tower = { version = "0.5", optional = true } +rand = { version = "0.9", optional = true } serde = { version = "1.0", features = ["derive"], optional = true } serde_json = { version = "1.0", optional = true } tracing = { version = "0.1.16", optional = true } @@ -309,17 +298,14 @@ prost-types = { version = "0.13", optional = true } http = { version = "1", optional = true } http-body = { version = "1", optional = true } -http-body-util = { version = "0.1", optional = true } hyper = { version = "1", optional = true } hyper-util = { version = "0.1.4", optional = true } listenfd = { version = "1.0", optional = true } bytes = { version = "1", optional = true } h2 = { version = "0.4", optional = true } -tokio-rustls = { version = "0.26", optional = true, features = ["ring", "tls12"], default-features = false } +tokio-rustls = { version = "0.26.1", optional = true, features = ["ring", "tls12"], default-features = false } hyper-rustls = { version = "0.27.0", features = ["http2", "ring", "tls12"], optional = true, default-features = false } -rustls-pemfile = { version = "2.0.0", optional = true } -tower-http = { version = "0.5", optional = true } -pin-project = { version = "1.0.11", optional = true } +tower-http = { version = "0.6", optional = true } [build-dependencies] tonic-build = { path = "../tonic-build", features = ["prost"] } diff -Nru rust-tonic-0.12.3+dfsg/examples/helloworld-tutorial.md rust-tonic-0.13.0+dfsg/examples/helloworld-tutorial.md --- rust-tonic-0.12.3+dfsg/examples/helloworld-tutorial.md 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/examples/helloworld-tutorial.md 2025-03-25 18:49:19.000000000 +0000 @@ -112,12 +112,12 @@ path = "src/client.rs" [dependencies] -tonic = "0.12" +tonic = "*" prost = "0.13" tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] } [build-dependencies] -tonic-build = "0.12" +tonic-build = "*" ``` We include `tonic-build` as a useful way to incorporate the generation of our client and server gRPC code into the build process of our application. We will setup this build process now: diff -Nru rust-tonic-0.12.3+dfsg/examples/LICENSE rust-tonic-0.13.0+dfsg/examples/LICENSE --- rust-tonic-0.12.3+dfsg/examples/LICENSE 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/examples/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -Copyright (c) 2020 Lucio Franco - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff -Nru rust-tonic-0.12.3+dfsg/examples/routeguide-tutorial.md rust-tonic-0.13.0+dfsg/examples/routeguide-tutorial.md --- rust-tonic-0.12.3+dfsg/examples/routeguide-tutorial.md 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/examples/routeguide-tutorial.md 2025-03-25 18:49:19.000000000 +0000 @@ -174,7 +174,7 @@ ```toml [dependencies] -tonic = "0.12" +tonic = "*" prost = "0.13" tokio = { version = "1.0", features = ["rt-multi-thread", "macros", "sync", "time"] } tokio-stream = "0.1" @@ -185,7 +185,7 @@ rand = "0.8" [build-dependencies] -tonic-build = "0.12" +tonic-build = "*" ``` Create a `build.rs` file at the root of your crate: @@ -401,7 +401,7 @@ &self, request: Request, ) -> Result, Status> { - let (mut tx, rx) = mpsc::channel(4); + let (tx, rx) = mpsc::channel(4); let features = self.features.clone(); tokio::spawn(async move { @@ -668,7 +668,7 @@ .into_inner(); while let Some(feature) = stream.message().await? { - println!("NOTE = {:?}", feature); + println!("FEATURE = {:?}", feature); } Ok(()) @@ -693,8 +693,8 @@ ```rust async fn run_record_route(client: &mut RouteGuideClient) -> Result<(), Box> { - let mut rng = rand::thread_rng(); - let point_count: i32 = rng.gen_range(2..100); + let mut rng = rand::rng(); + let point_count: i32 = rng.random_range(2..100); let mut points = vec![]; for _ in 0..=point_count { @@ -715,8 +715,8 @@ ```rust fn random_point(rng: &mut ThreadRng) -> Point { - let latitude = (rng.gen_range(0..180) - 90) * 10_000_000; - let longitude = (rng.gen_range(0..360) - 180) * 10_000_000; + let latitude = (rng.random_range(0..180) - 90) * 10_000_000; + let longitude = (rng.random_range(0..360) - 180) * 10_000_000; Point { latitude, longitude, diff -Nru rust-tonic-0.12.3+dfsg/examples/src/dynamic_load_balance/client.rs rust-tonic-0.13.0+dfsg/examples/src/dynamic_load_balance/client.rs --- rust-tonic-0.12.3+dfsg/examples/src/dynamic_load_balance/client.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/examples/src/dynamic_load_balance/client.rs 2025-03-25 18:49:19.000000000 +0000 @@ -3,15 +3,14 @@ } use pb::{echo_client::EchoClient, EchoRequest}; +use tonic::transport::channel::Change; use tonic::transport::Channel; - use tonic::transport::Endpoint; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering::SeqCst}; use tokio::time::timeout; -use tower::discover::Change; #[tokio::main] async fn main() -> Result<(), Box> { diff -Nru rust-tonic-0.12.3+dfsg/examples/src/grpc-web/server.rs rust-tonic-0.13.0+dfsg/examples/src/grpc-web/server.rs --- rust-tonic-0.12.3+dfsg/examples/src/grpc-web/server.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/examples/src/grpc-web/server.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,4 +1,4 @@ -use tonic::{transport::Server, Request, Response, Status}; +use tonic::{service::LayerExt as _, transport::Server, Request, Response, Status}; use hello_world::greeter_server::{Greeter, GreeterServer}; use hello_world::{HelloReply, HelloRequest}; @@ -32,14 +32,18 @@ let addr = "127.0.0.1:3000".parse().unwrap(); let greeter = MyGreeter::default(); - let greeter = GreeterServer::new(greeter); + let greeter = tower::ServiceBuilder::new() + .layer(tower_http::cors::CorsLayer::new()) + .layer(tonic_web::GrpcWebLayer::new()) + .into_inner() + .named_layer(GreeterServer::new(greeter)); println!("GreeterServer listening on {}", addr); Server::builder() // GrpcWeb is over http1 so we must enable it. .accept_http1(true) - .add_service(tonic_web::enable(greeter)) + .add_service(greeter) .serve(addr) .await?; diff -Nru rust-tonic-0.12.3+dfsg/examples/src/h2c/client.rs rust-tonic-0.13.0+dfsg/examples/src/h2c/client.rs --- rust-tonic-0.12.3+dfsg/examples/src/h2c/client.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/examples/src/h2c/client.rs 2025-03-25 18:49:19.000000000 +0000 @@ -39,14 +39,14 @@ client::legacy::{connect::HttpConnector, Client}, rt::TokioExecutor, }; - use tonic::body::{empty_body, BoxBody}; + use tonic::body::Body; use tower::Service; pub struct H2cChannel { - pub client: Client, + pub client: Client, } - impl Service> for H2cChannel { + impl Service> for H2cChannel { type Response = http::Response; type Error = hyper::Error; type Future = @@ -56,7 +56,7 @@ Poll::Ready(Ok(())) } - fn call(&mut self, request: http::Request) -> Self::Future { + fn call(&mut self, request: http::Request) -> Self::Future { let client = self.client.clone(); Box::pin(async move { @@ -65,7 +65,7 @@ let h2c_req = hyper::Request::builder() .uri(origin) .header(http::header::UPGRADE, "h2c") - .body(empty_body()) + .body(Body::default()) .unwrap(); let res = client.request(h2c_req).await.unwrap(); diff -Nru rust-tonic-0.12.3+dfsg/examples/src/h2c/server.rs rust-tonic-0.13.0+dfsg/examples/src/h2c/server.rs --- rust-tonic-0.12.3+dfsg/examples/src/h2c/server.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/examples/src/h2c/server.rs 2025-03-25 18:49:19.000000000 +0000 @@ -69,7 +69,7 @@ use http::{Request, Response}; use hyper::body::Incoming; use hyper_util::{rt::TokioExecutor, service::TowerToHyperService}; - use tonic::body::{empty_body, BoxBody}; + use tonic::body::Body; use tower::{Service, ServiceExt}; #[derive(Clone)] @@ -81,12 +81,11 @@ impl Service> for H2c where - S: Service, Response = Response> + Clone + Send + 'static, - S::Future: Send + 'static, - S::Error: Into + Sync + Send + 'static, - S::Response: Send + 'static, + S: Service, Response = Response> + Clone + Send + 'static, + S::Future: Send, + S::Error: Into + 'static, { - type Response = hyper::Response; + type Response = hyper::Response; type Error = hyper::Error; type Future = Pin> + Send>>; @@ -99,11 +98,11 @@ } fn call(&mut self, req: hyper::Request) -> Self::Future { - let mut req = req.map(tonic::body::boxed); + let mut req = req.map(Body::new); let svc = self .s .clone() - .map_request(|req: Request<_>| req.map(tonic::body::boxed)); + .map_request(|req: Request<_>| req.map(Body::new)); Box::pin(async move { tokio::spawn(async move { let upgraded_io = hyper::upgrade::on(&mut req).await.unwrap(); @@ -114,7 +113,7 @@ .unwrap(); }); - let mut res = hyper::Response::new(empty_body()); + let mut res = hyper::Response::new(Body::default()); *res.status_mut() = http::StatusCode::SWITCHING_PROTOCOLS; res.headers_mut().insert( hyper::header::UPGRADE, diff -Nru rust-tonic-0.12.3+dfsg/examples/src/health/server.rs rust-tonic-0.13.0+dfsg/examples/src/health/server.rs --- rust-tonic-0.12.3+dfsg/examples/src/health/server.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/examples/src/health/server.rs 2025-03-25 18:49:19.000000000 +0000 @@ -29,7 +29,7 @@ /// This function (somewhat improbably) flips the status of a service every second, in order /// that the effect of `tonic_health::HealthReporter::watch` can be easily observed. -async fn twiddle_service_status(mut reporter: HealthReporter) { +async fn twiddle_service_status(reporter: HealthReporter) { let mut iter = 0u64; loop { iter += 1; @@ -45,7 +45,7 @@ #[tokio::main] async fn main() -> Result<(), Box> { - let (mut health_reporter, health_service) = tonic_health::server::health_reporter(); + let (health_reporter, health_service) = tonic_health::server::health_reporter(); health_reporter .set_serving::>() .await; diff -Nru rust-tonic-0.12.3+dfsg/examples/src/mock/mock.rs rust-tonic-0.13.0+dfsg/examples/src/mock/mock.rs --- rust-tonic-0.12.3+dfsg/examples/src/mock/mock.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/examples/src/mock/mock.rs 2025-03-25 18:49:19.000000000 +0000 @@ -39,10 +39,7 @@ if let Some(client) = client { Ok(TokioIo::new(client)) } else { - Err(std::io::Error::new( - std::io::ErrorKind::Other, - "Client already taken", - )) + Err(std::io::Error::other("Client already taken")) } } })) diff -Nru rust-tonic-0.12.3+dfsg/examples/src/routeguide/client.rs rust-tonic-0.13.0+dfsg/examples/src/routeguide/client.rs --- rust-tonic-0.12.3+dfsg/examples/src/routeguide/client.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/examples/src/routeguide/client.rs 2025-03-25 18:49:19.000000000 +0000 @@ -32,15 +32,15 @@ .into_inner(); while let Some(feature) = stream.message().await? { - println!("NOTE = {:?}", feature); + println!("FEATURE = {:?}", feature); } Ok(()) } async fn run_record_route(client: &mut RouteGuideClient) -> Result<(), Box> { - let mut rng = rand::thread_rng(); - let point_count: i32 = rng.gen_range(2..100); + let mut rng = rand::rng(); + let point_count: i32 = rng.random_range(2..100); let mut points = vec![]; for _ in 0..=point_count { @@ -115,8 +115,8 @@ } fn random_point(rng: &mut ThreadRng) -> Point { - let latitude = (rng.gen_range(0..180) - 90) * 10_000_000; - let longitude = (rng.gen_range(0..360) - 180) * 10_000_000; + let latitude = (rng.random_range(0..180) - 90) * 10_000_000; + let longitude = (rng.random_range(0..360) - 180) * 10_000_000; Point { latitude, longitude, diff -Nru rust-tonic-0.12.3+dfsg/examples/src/streaming/server.rs rust-tonic-0.13.0+dfsg/examples/src/streaming/server.rs --- rust-tonic-0.12.3+dfsg/examples/src/streaming/server.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/examples/src/streaming/server.rs 2025-03-25 18:49:19.000000000 +0000 @@ -28,10 +28,7 @@ } } - err = match err.source() { - Some(err) => err, - None => return None, - }; + err = err.source()?; } } diff -Nru rust-tonic-0.12.3+dfsg/examples/src/timeout/client.rs rust-tonic-0.13.0+dfsg/examples/src/timeout/client.rs --- rust-tonic-0.12.3+dfsg/examples/src/timeout/client.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/examples/src/timeout/client.rs 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -use hello_world::greeter_client::GreeterClient; -use hello_world::HelloRequest; -use std::time::Duration; -use tower::timeout::Timeout; - -use tonic::transport::Channel; - -pub mod hello_world { - tonic::include_proto!("helloworld"); -} - -#[tokio::main] -async fn main() -> Result<(), Box> { - let channel = Channel::from_static("http://[::1]:50051").connect().await?; - let timeout_channel = Timeout::new(channel, Duration::from_millis(1000)); - - let mut client = GreeterClient::new(timeout_channel); - - let request = tonic::Request::new(HelloRequest { - name: "Tonic".into(), - }); - - let response = client.say_hello(request).await?; - - println!("RESPONSE={:?}", response); - - Ok(()) -} diff -Nru rust-tonic-0.12.3+dfsg/examples/src/timeout/server.rs rust-tonic-0.13.0+dfsg/examples/src/timeout/server.rs --- rust-tonic-0.12.3+dfsg/examples/src/timeout/server.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/examples/src/timeout/server.rs 1970-01-01 00:00:00.000000000 +0000 @@ -1,45 +0,0 @@ -use std::time::Duration; -use tokio::time::sleep; -use tonic::{transport::Server, Request, Response, Status}; - -use hello_world::greeter_server::{Greeter, GreeterServer}; -use hello_world::{HelloReply, HelloRequest}; - -pub mod hello_world { - tonic::include_proto!("helloworld"); -} - -#[derive(Default)] -pub struct MyGreeter {} - -#[tonic::async_trait] -impl Greeter for MyGreeter { - async fn say_hello( - &self, - request: Request, - ) -> Result, Status> { - println!("Got a request from {:?}", request.remote_addr()); - - sleep(Duration::from_millis(5000)).await; - - let reply = hello_world::HelloReply { - message: format!("Hello {}!", request.into_inner().name), - }; - Ok(Response::new(reply)) - } -} - -#[tokio::main] -async fn main() -> Result<(), Box> { - let addr = "[::1]:50051".parse().unwrap(); - let greeter = MyGreeter::default(); - - println!("GreeterServer listening on {}", addr); - - Server::builder() - .add_service(GreeterServer::new(greeter)) - .serve(addr) - .await?; - - Ok(()) -} diff -Nru rust-tonic-0.12.3+dfsg/examples/src/tls_rustls/client.rs rust-tonic-0.13.0+dfsg/examples/src/tls_rustls/client.rs --- rust-tonic-0.12.3+dfsg/examples/src/tls_rustls/client.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/examples/src/tls_rustls/client.rs 2025-03-25 18:49:19.000000000 +0000 @@ -8,7 +8,10 @@ use hyper::Uri; use hyper_util::{client::legacy::connect::HttpConnector, rt::TokioExecutor}; use pb::{echo_client::EchoClient, EchoRequest}; -use tokio_rustls::rustls::{ClientConfig, RootCertStore}; +use tokio_rustls::rustls::{ + pki_types::{pem::PemObject as _, CertificateDer}, + {ClientConfig, RootCertStore}, +}; #[tokio::main] async fn main() -> Result<(), Box> { @@ -18,7 +21,7 @@ let mut roots = RootCertStore::empty(); let mut buf = std::io::BufReader::new(&fd); - let certs = rustls_pemfile::certs(&mut buf).collect::, _>>()?; + let certs = CertificateDer::pem_reader_iter(&mut buf).collect::, _>>()?; roots.add_parsable_certificates(certs.into_iter()); let tls = ClientConfig::builder() diff -Nru rust-tonic-0.12.3+dfsg/examples/src/tls_rustls/server.rs rust-tonic-0.13.0+dfsg/examples/src/tls_rustls/server.rs --- rust-tonic-0.12.3+dfsg/examples/src/tls_rustls/server.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/examples/src/tls_rustls/server.rs 2025-03-25 18:49:19.000000000 +0000 @@ -11,10 +11,13 @@ use std::sync::Arc; use tokio::net::TcpListener; use tokio_rustls::{ - rustls::{pki_types::CertificateDer, ServerConfig}, + rustls::{ + pki_types::{pem::PemObject as _, CertificateDer, PrivateKeyDer}, + ServerConfig, + }, TlsAcceptor, }; -use tonic::{body::boxed, service::Routes, Request, Response, Status}; +use tonic::{body::Body, service::Routes, Request, Response, Status}; use tower::ServiceExt; use tower_http::ServiceBuilderExt; @@ -24,12 +27,12 @@ let certs = { let fd = std::fs::File::open(data_dir.join("tls/server.pem"))?; let mut buf = std::io::BufReader::new(&fd); - rustls_pemfile::certs(&mut buf).collect::, _>>()? + CertificateDer::pem_reader_iter(&mut buf).collect::, _>>()? }; let key = { let fd = std::fs::File::open(data_dir.join("tls/server.key"))?; let mut buf = std::io::BufReader::new(&fd); - rustls_pemfile::private_key(&mut buf)?.unwrap() + PrivateKeyDer::from_pem_reader(&mut buf)? }; let mut tls = ServerConfig::builder() @@ -79,7 +82,9 @@ http.serve_connection( TokioIo::new(conn), - TowerToHyperService::new(svc.map_request(|req: http::Request<_>| req.map(boxed))), + TowerToHyperService::new( + svc.map_request(|req: http::Request<_>| req.map(Body::new)), + ), ) .await .unwrap(); diff -Nru rust-tonic-0.12.3+dfsg/examples/src/tower/client.rs rust-tonic-0.13.0+dfsg/examples/src/tower/client.rs --- rust-tonic-0.12.3+dfsg/examples/src/tower/client.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/examples/src/tower/client.rs 2025-03-25 18:49:19.000000000 +0000 @@ -15,7 +15,7 @@ let channel = ServiceBuilder::new() // Interceptors can be also be applied as middleware - .layer(tonic::service::interceptor(intercept)) + .layer(tonic::service::InterceptorLayer::new(intercept)) .layer_fn(AuthSvc::new) .service(channel); @@ -43,7 +43,7 @@ use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; - use tonic::body::BoxBody; + use tonic::body::Body; use tonic::transport::Channel; use tower::Service; @@ -57,8 +57,8 @@ } } - impl Service> for AuthSvc { - type Response = Response; + impl Service> for AuthSvc { + type Response = Response; type Error = Box; #[allow(clippy::type_complexity)] type Future = Pin> + Send>>; @@ -67,10 +67,8 @@ self.inner.poll_ready(cx).map_err(Into::into) } - fn call(&mut self, req: Request) -> Self::Future { - // This is necessary because tonic internally uses `tower::buffer::Buffer`. - // See https://github.com/tower-rs/tower/issues/547#issuecomment-767629149 - // for details on why this is necessary + fn call(&mut self, req: Request) -> Self::Future { + // See: https://docs.rs/tower/latest/tower/trait.Service.html#be-careful-when-cloning-inner-services let clone = self.inner.clone(); let mut inner = std::mem::replace(&mut self.inner, clone); diff -Nru rust-tonic-0.12.3+dfsg/examples/src/tower/server.rs rust-tonic-0.13.0+dfsg/examples/src/tower/server.rs --- rust-tonic-0.12.3+dfsg/examples/src/tower/server.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/examples/src/tower/server.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,9 +1,8 @@ use std::{ pin::Pin, task::{Context, Poll}, - time::Duration, }; -use tonic::{body::BoxBody, transport::Server, Request, Response, Status}; +use tonic::{transport::Server, Request, Response, Status}; use tower::{Layer, Service}; use hello_world::greeter_server::{Greeter, GreeterServer}; @@ -42,12 +41,10 @@ // The stack of middleware that our service will be wrapped in let layer = tower::ServiceBuilder::new() - // Apply middleware from tower - .timeout(Duration::from_secs(30)) // Apply our own middleware .layer(MyMiddlewareLayer::default()) // Interceptors can be also be applied as middleware - .layer(tonic::service::interceptor(intercept)) + .layer(tonic::service::InterceptorLayer::new(intercept)) .into_inner(); Server::builder() @@ -83,13 +80,11 @@ type BoxFuture<'a, T> = Pin + Send + 'a>>; -impl Service> for MyMiddleware +impl Service> for MyMiddleware where - S: Service, Response = hyper::Response> - + Clone - + Send - + 'static, + S: Service, Response = http::Response> + Clone + Send + 'static, S::Future: Send + 'static, + ReqBody: Send + 'static, { type Response = S::Response; type Error = S::Error; @@ -99,10 +94,8 @@ self.inner.poll_ready(cx) } - fn call(&mut self, req: hyper::Request) -> Self::Future { - // This is necessary because tonic internally uses `tower::buffer::Buffer`. - // See https://github.com/tower-rs/tower/issues/547#issuecomment-767629149 - // for details on why this is necessary + fn call(&mut self, req: http::Request) -> Self::Future { + // See: https://docs.rs/tower/latest/tower/trait.Service.html#be-careful-when-cloning-inner-services let clone = self.inner.clone(); let mut inner = std::mem::replace(&mut self.inner, clone); diff -Nru rust-tonic-0.12.3+dfsg/examples/src/uds/client.rs rust-tonic-0.13.0+dfsg/examples/src/uds/client.rs --- rust-tonic-0.12.3+dfsg/examples/src/uds/client.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/examples/src/uds/client.rs 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -#![cfg_attr(not(unix), allow(unused_imports))] - -pub mod hello_world { - tonic::include_proto!("helloworld"); -} - -use hello_world::{greeter_client::GreeterClient, HelloRequest}; -use hyper_util::rt::TokioIo; -#[cfg(unix)] -use tokio::net::UnixStream; -use tonic::transport::{Endpoint, Uri}; -use tower::service_fn; - -#[cfg(unix)] -#[tokio::main] -async fn main() -> Result<(), Box> { - // We will ignore this uri because uds do not use it - // if your connector does use the uri it will be provided - // as the request to the `MakeConnection`. - - let channel = Endpoint::try_from("http://[::]:50051")? - .connect_with_connector(service_fn(|_: Uri| async { - let path = "/tmp/tonic/helloworld"; - - // Connect to a Uds socket - Ok::<_, std::io::Error>(TokioIo::new(UnixStream::connect(path).await?)) - })) - .await?; - - let mut client = GreeterClient::new(channel); - - let request = tonic::Request::new(HelloRequest { - name: "Tonic".into(), - }); - - let response = client.say_hello(request).await?; - - println!("RESPONSE={:?}", response); - - Ok(()) -} - -#[cfg(not(unix))] -fn main() { - panic!("The `uds` example only works on unix systems!"); -} diff -Nru rust-tonic-0.12.3+dfsg/examples/src/uds/client_standard.rs rust-tonic-0.13.0+dfsg/examples/src/uds/client_standard.rs --- rust-tonic-0.12.3+dfsg/examples/src/uds/client_standard.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/examples/src/uds/client_standard.rs 2025-03-25 18:49:19.000000000 +0000 @@ -0,0 +1,34 @@ +#![cfg_attr(not(unix), allow(unused_imports))] + +pub mod hello_world { + tonic::include_proto!("helloworld"); +} + +use hello_world::{greeter_client::GreeterClient, HelloRequest}; + +#[cfg(unix)] +#[tokio::main] +async fn main() -> Result<(), Box> { + // Unix socket URI follows [RFC-3986](https://datatracker.ietf.org/doc/html/rfc3986) + // which is aligned with [the gRPC naming convention](https://github.com/grpc/grpc/blob/master/doc/naming.md). + // - unix:relative_path + // - unix:///absolute_path + let path = "unix:///tmp/tonic/helloworld"; + + let mut client = GreeterClient::connect(path).await?; + + let request = tonic::Request::new(HelloRequest { + name: "Tonic".into(), + }); + + let response = client.say_hello(request).await?; + + println!("RESPONSE={:?}", response); + + Ok(()) +} + +#[cfg(not(unix))] +fn main() { + panic!("The `uds` example only works on unix systems!"); +} diff -Nru rust-tonic-0.12.3+dfsg/examples/src/uds/client_with_connector.rs rust-tonic-0.13.0+dfsg/examples/src/uds/client_with_connector.rs --- rust-tonic-0.12.3+dfsg/examples/src/uds/client_with_connector.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/examples/src/uds/client_with_connector.rs 2025-03-25 18:49:19.000000000 +0000 @@ -0,0 +1,46 @@ +#![cfg_attr(not(unix), allow(unused_imports))] + +pub mod hello_world { + tonic::include_proto!("helloworld"); +} + +use hello_world::{greeter_client::GreeterClient, HelloRequest}; +use hyper_util::rt::TokioIo; +#[cfg(unix)] +use tokio::net::UnixStream; +use tonic::transport::{Endpoint, Uri}; +use tower::service_fn; + +#[cfg(unix)] +#[tokio::main] +async fn main() -> Result<(), Box> { + // We will ignore this uri because uds do not use it + // if your connector does use the uri it will be provided + // as the request to the `MakeConnection`. + + let channel = Endpoint::try_from("http://[::]:50051")? + .connect_with_connector(service_fn(|_: Uri| async { + let path = "/tmp/tonic/helloworld"; + + // Connect to a Uds socket + Ok::<_, std::io::Error>(TokioIo::new(UnixStream::connect(path).await?)) + })) + .await?; + + let mut client = GreeterClient::new(channel); + + let request = tonic::Request::new(HelloRequest { + name: "Tonic".into(), + }); + + let response = client.say_hello(request).await?; + + println!("RESPONSE={:?}", response); + + Ok(()) +} + +#[cfg(not(unix))] +fn main() { + panic!("The `uds` example only works on unix systems!"); +} diff -Nru rust-tonic-0.12.3+dfsg/.github/workflows/CI.yml rust-tonic-0.13.0+dfsg/.github/workflows/CI.yml --- rust-tonic-0.12.3+dfsg/.github/workflows/CI.yml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/.github/workflows/CI.yml 2025-03-25 18:49:19.000000000 +0000 @@ -7,6 +7,10 @@ merge_group: branches: [ "master" ] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + env: RUSTFLAGS: "-D warnings" @@ -21,12 +25,13 @@ components: rustfmt - run: cargo fmt --all --check - deny-check: - name: cargo-deny check + deny: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: EmbarkStudios/cargo-deny-action@v2 + - uses: hecrj/setup-rust-action@v2 + - uses: taiki-e/install-action@cargo-deny + - run: cargo deny check clippy: runs-on: ubuntu-latest @@ -38,8 +43,6 @@ - uses: taiki-e/install-action@protoc - uses: Swatinem/rust-cache@v2 - run: cargo clippy --workspace --all-features --all-targets - env: - RUSTFLAGS: "-A warnings" codegen: runs-on: ubuntu-latest @@ -51,21 +54,23 @@ - run: git diff --exit-code udeps: - name: Check unused dependencies runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: - toolchain: nightly-2024-08-04 + toolchain: nightly-2024-12-01 - uses: taiki-e/install-action@cargo-hack - uses: taiki-e/install-action@cargo-udeps - uses: taiki-e/install-action@protoc - uses: Swatinem/rust-cache@v2 - - run: cargo hack udeps --workspace --exclude-features tls --each-feature - - run: cargo udeps --package tonic --features tls,transport - - run: cargo udeps --package tonic --features tls,server - - run: cargo udeps --package tonic --features tls,channel + - run: cargo hack udeps --workspace --exclude-features=_tls-any,tls,tls-aws-lc,tls-ring --each-feature + - run: cargo udeps --package tonic --features tls-ring,transport + - run: cargo udeps --package tonic --features tls-ring,server + - run: cargo udeps --package tonic --features tls-ring,channel + - run: cargo udeps --package tonic --features tls-aws-lc,transport + - run: cargo udeps --package tonic --features tls-aws-lc,server + - run: cargo udeps --package tonic --features tls-aws-lc,channel check: runs-on: ${{ matrix.os }} @@ -86,17 +91,24 @@ run: cargo check --workspace --all-targets --all-features msrv: - name: Check MSRV runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: hecrj/setup-rust-action@v2 + - name: Resolve MSRV aware dependencies + run: cargo update + env: + CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS: fallback + - name: Get MSRV from manifest file + id: msrv + run: echo "version=$(yq '.workspace.package.rust-version' Cargo.toml)" >> $GITHUB_OUTPUT + - uses: hecrj/setup-rust-action@v2 with: - rust-version: "1.71.1" # msrv + rust-version: ${{ steps.msrv.outputs.version }} - uses: taiki-e/install-action@cargo-no-dev-deps - uses: Swatinem/rust-cache@v2 - run: cargo no-dev-deps --no-private check --all-features - - run: cargo no-dev-deps --no-private doc --no-deps + - run: cargo no-dev-deps --no-private doc --no-deps --all-features env: RUSTDOCFLAGS: "-D warnings" @@ -109,11 +121,22 @@ - uses: actions/checkout@v4 - uses: hecrj/setup-rust-action@v2 - uses: taiki-e/install-action@protoc + - uses: taiki-e/install-action@cargo-hack + - uses: taiki-e/install-action@cargo-nextest - uses: Swatinem/rust-cache@v2 - - run: cargo test --workspace --all-features + - run: cargo nextest run --workspace --all-features env: QUICKCHECK_TESTS: 1000 # run a lot of quickcheck iterations + doc-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: hecrj/setup-rust-action@v2 + - uses: taiki-e/install-action@cargo-hack + - uses: Swatinem/rust-cache@v2 + - run: cargo hack --no-private test --doc --all-features + interop: name: Interop Tests runs-on: ${{ matrix.os }} @@ -140,17 +163,17 @@ with: feature-group: all-features - check-external-types: + external-types: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: - toolchain: nightly-2024-05-01 + toolchain: nightly-2024-06-30 - name: Install cargo-check-external-types uses: taiki-e/cache-cargo-install-action@v2 with: - tool: cargo-check-external-types@0.1.12 + tool: cargo-check-external-types@0.1.13 - uses: taiki-e/install-action@cargo-hack - uses: Swatinem/rust-cache@v2 - run: cargo hack --no-private check-external-types diff -Nru rust-tonic-0.12.3+dfsg/interop/Cargo.toml rust-tonic-0.13.0+dfsg/interop/Cargo.toml --- rust-tonic-0.12.3+dfsg/interop/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/interop/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -3,8 +3,6 @@ edition = "2021" license = "MIT" name = "interop" -publish = false -version = "0.1.0" [[bin]] name = "client" @@ -16,17 +14,18 @@ [dependencies] async-stream = "0.3" -strum = {version = "0.26", features = ["derive"]} +strum = {version = "0.27", features = ["derive"]} pico-args = {version = "0.5", features = ["eq-separator"]} console = "0.15" http = "1" http-body = "1" +http-body-util = "0.1" hyper = "1" prost = "0.13" tokio = {version = "1.0", features = ["rt-multi-thread", "time", "macros"]} tokio-stream = "0.1" -tonic = {path = "../tonic", features = ["tls"]} -tower = {version = "0.4"} +tonic = {path = "../tonic", features = ["tls-ring"]} +tower = "0.5" tracing-subscriber = {version = "0.3"} [build-dependencies] diff -Nru rust-tonic-0.12.3+dfsg/interop/data/ca.pem rust-tonic-0.13.0+dfsg/interop/data/ca.pem --- rust-tonic-0.12.3+dfsg/interop/data/ca.pem 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/interop/data/ca.pem 2025-03-25 18:49:19.000000000 +0000 @@ -1,20 +1,20 @@ -----BEGIN CERTIFICATE----- -MIIDRzCCAi+gAwIBAgIRAO7dzPqhReVW2U6D1V1DTYAwDQYJKoZIhvcNAQELBQAw -PTEOMAwGA1UEChMFVG9raW8xEDAOBgNVBAsTB1Rlc3RpbmcxGTAXBgNVBAMTEFRv -bmljIFRlc3RpbmcgQ0EwHhcNMTkxMTA5MTY0NzU0WhcNMjkxMTA2MTY0NzU0WjA9 +MIIDRjCCAi6gAwIBAgIQQS24If9oGkeiIDhCX3LptTANBgkqhkiG9w0BAQsFADA9 MQ4wDAYDVQQKEwVUb2tpbzEQMA4GA1UECxMHVGVzdGluZzEZMBcGA1UEAxMQVG9u -aWMgVGVzdGluZyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM+A -e+Y3wDdOm7yUi6xHINw1QE0k1D51U+DAMOLcUq17FYh3fp+OOaqq4znnEx8WkKVl -FuoW4xzIrv2ywn0hFADCxaVpjMuCxj313D7LMZExa98TuFF3Jg2GYScBRQKjfyRv -CV+cSHAvzEstd5ckdiz985Zqnepiy7R9k2CstO45ULG4UoVha+VgmYJ5qXqBXbso -LbDXzrjjQFSmbw1yh9lQvzsk0UyU5Hvi0Otka4LZJsNaNFWkltl8v37QWG6sH8+f -Ur7fELIDmbScGSiqvEm0EZLZHqLU669NvUoFtTKNfKghT83DW9kErzdg5EcVFQom -Ov8cIW9MkBwDJXe6D88CAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgIEMA8GA1UdEwEB -/wQFMAMBAf8wHQYDVR0OBBYEFE5N36NK1qLWGDxSJP3IF9H2oLO/MA0GCSqGSIb3 -DQEBCwUAA4IBAQAm27Wdq6+0IxvattJW0j7lnh9AC7aMQEbUQPYM8iO6WVhXshaN -d7YbLDGwRtATzLeA/0laovEL5pc/1NnMsqG4DspD3glXcASUiu5TrzPvBrAWByQn -8C9tGUbyP1yJMo42Mzs0hIDsaUsucynZcdxAErjJjKB9Ps3KeaA6LUr/igN0649S -qQ6bHZdNEIhjyCY7TDJArjPTgdSYjx13XkVbZ1cN/ssZI7Ag8WByhVEHeoE13fqw -ue5oTNat0qNFP0Yo8XB0whPhN2wbCbHoc5khXJiBrMEjAAgJUiWRy3ITG88SIak+ -1dtqgxS2T0nmdRlXBYNNhYKdgWYJq1PwDp9a +aWMgVGVzdGluZyBDQTAeFw0yNDExMTMxOTQ0MzBaFw0zNDExMTExOTQ0MzBaMD0x +DjAMBgNVBAoTBVRva2lvMRAwDgYDVQQLEwdUZXN0aW5nMRkwFwYDVQQDExBUb25p +YyBUZXN0aW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuRMq +OMDsXvkEc/ArFxHhNNd1qIRhgPElLR/de091WVEKGGQI7OEJLE5/dfD2RMe0PdvZ +tEhURM/SUkreYeJhC7AFbAVM6cDC7Lj+GB1v63EbpFTDkkrJ5+GN7DtID/FNYg4M +sBlHCMys2ZWByAy6/fBZ4PKyJbMX4gtySLAyCTqI/BV1TC/7tgxiNrSNv/MZiqqO +FOdAfpkdb/mGWkxOB+JYWT8QBjIWcWaoWJcwmlY4ZC6U5aaNxO3VKDdGyghKkPS7 +nDAMe5H3Cl/rR1TKY3A9TVcE4qQ514647NQpCoOJZ39PbXJy+S4v3qI61IvFBPFh +C3tgZABh8h9dO7ddGQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAgQwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQU6k4lLdVI7YCNA9eQBb8hO3nIFVQwDQYJKoZIhvcN +AQELBQADggEBADhJOxK2tPlai1pSwT0ud7oXXfAQpI1PZ9FxWR9qx1UsTLb3sfTW ++20RQdguGXTby5wkHIiVJ6vAEDRd8X9oOf8vk2zvnmWLgiDdOh3e3OkOme6Qs+V9 +mXIA8JZnYGaqmAPcmZU5uY2lrf0oXURqb9ZNL5DCc1yJ1rVC3jCQLZZ1v6GGRNB5 +YhTWlQYUcu2nw8fN1scrV9322gU5siJzrNrjO9yabIYZyMpwcNwarvoxcVT8zLM5 +ZLUTjG2Yr8PJ13Bn2dyw8mDuL0ixXU34vQNIHBk1UmbzyWL9mDfu9RfpIDIDkEzK +sO/HXgh4vzETaIhkEuW9porYX5d1QyVED8I= -----END CERTIFICATE----- diff -Nru rust-tonic-0.12.3+dfsg/interop/data/cert-generator/ca.tf rust-tonic-0.13.0+dfsg/interop/data/cert-generator/ca.tf --- rust-tonic-0.12.3+dfsg/interop/data/cert-generator/ca.tf 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/interop/data/cert-generator/ca.tf 2025-03-25 18:49:19.000000000 +0000 @@ -4,7 +4,6 @@ } resource "tls_self_signed_cert" "root" { - key_algorithm = tls_private_key.root.algorithm private_key_pem = tls_private_key.root.private_key_pem validity_period_hours = 87600 @@ -24,4 +23,4 @@ resource "local_file" "ca_cert" { filename = "../ca.pem" content = tls_self_signed_cert.root.cert_pem -} \ No newline at end of file +} diff -Nru rust-tonic-0.12.3+dfsg/interop/data/cert-generator/server_certs.tf rust-tonic-0.13.0+dfsg/interop/data/cert-generator/server_certs.tf --- rust-tonic-0.12.3+dfsg/interop/data/cert-generator/server_certs.tf 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/interop/data/cert-generator/server_certs.tf 2025-03-25 18:49:19.000000000 +0000 @@ -4,7 +4,6 @@ } resource "tls_cert_request" "server" { - key_algorithm = tls_private_key.server.algorithm private_key_pem = tls_private_key.server.private_key_pem subject { @@ -19,7 +18,6 @@ resource "tls_locally_signed_cert" "server" { cert_request_pem = tls_cert_request.server.cert_request_pem - ca_key_algorithm = tls_private_key.root.algorithm ca_private_key_pem = tls_private_key.root.private_key_pem ca_cert_pem = tls_self_signed_cert.root.cert_pem diff -Nru rust-tonic-0.12.3+dfsg/interop/data/server1.key rust-tonic-0.13.0+dfsg/interop/data/server1.key --- rust-tonic-0.12.3+dfsg/interop/data/server1.key 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/interop/data/server1.key 2025-03-25 18:49:19.000000000 +0000 @@ -1,27 +1,27 @@ -----BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAvSo8ttodDg4wwjwSPURcTgVPh7Iv9E+PsCTHW7b1Dov/tjxs -nhgLZkySicSLIhHv+/17DYHaMd7alx5AYb+Bxk/KvSuR/sqg0QJcuQ4prpzccQHY -mqJTeMWFCOvAUSSOZXwOxKHFNoLCVQsZMMs1JmUwAoun9ocyuoXuNFa/2GmNO+cN -tBSa1ZnEyhuW9R8lFdp41e9OG1c6dgdh3Gq6NEqDaRN/FbaBAQJO6u8jDClcqeB5 -uPfXKEENk8ed71zRRJ7u4Pjn+qCgy9daz6hRuP7MGKnrXt1j5f73JnZF5bFuM+Bg -6MqG6XVvrIVDdvod2qWpMTjCpINp45i0lkZpAQIDAQABAoIBAQCu50LD/uAmgtBq -h4iFxZNjQF3MpeDZEEdXImqCTqQ/EwsYwL3dX3YK3HoRj/zlP5iZckI4tvu8aMXM -PFhjCONBLb3TM1oGL+yJ1JlPMd0wajEY/A/+ymBLprXfDbwASsCu7QnqnXjvce+l -GmHsT7eRDLZbZC2lMFSjSfp5wkwYF8lBfg2tDswgBX2mT4lZ/nUIjpr1x1y34BQL -yPYHSsSnoTSC6dqWkpuiQyyTraeknCsE4lw03C2XWcpCFaNt6ZrzAmSeX7/MK+Xp -blQ4bQmeydCwfsXFBdE/lOWUIjc8mkBN0c9wbiBJwyAH09QzUVcXlgjH7IBwKSMc -mFpLvt3BAoGBAOGNHc1URPiSJaIc86nkWvmNiISxqHUVOrZPwOLJMciJz75XSmVs -QlydXIepunU5WZJJiIV2fL6FyZ7RnCGLCvFEymewobexAjL3ffN4oN5s2g2e8fs+ -vhu3xLxo3fmhcRLbxub0qOJUu//2lcBYRXHVu+lb4qvisnn4BDMxbAFNAoGBANaz -o5Zo6uL5WHcNjwKa4YtWA4/pzAzsXwNqPPUyWL9uvAP3YS7ikQvzLJXDUTCtopZ5 -xTvuShRPfvrIUkvK9XOok3PNeyPeBoRQ8W9sP6n/5gSULy7sHALJxZscMf7GD81+ -yfH+R+67vwW1etwP3LHuC1NirCcpvLZfWyfUcqyFAoGBAKeqzXKrqDHYAp3GQ+QR -WweUDN4HayDOTTzlgI+V3KokuAfYv/cxSQur9vLqWy91GH7EpvX/pK/EqKKlUxkk -UVgVORlnlnAE54uXq0toar2t0VK6y0tn0s6sB1W/5vMA7huEwRFC4qCNOMwIND4t -4EHFDtFketYnyWEd25Fqtc0pAoGATpvJClnxnhbDMBuzv7VrXPOqLDfisNyeUQbF -uNStL7HgfudFGsBzcNeg/Fhd0p/QRp3g+/dcAiG1ESblEsEFq0oOarjSHCi/ZBSq -wSv2B00dL5H90IU8ID0173ucRnbH9Go2kDaUqbDt2K5AhG/+UtsgJHCdLV2XrYIu -QuAC+G0CgYBW2O4FjDLG16kivtEziCW+Z/iLeUM3r8lDzf8iz1Dg+72AnlmAFSzz -QaY2GxdatZHBCMeZ0eaMxxFyuT2IPKOiyIHQ6diJBX+CFNHxgPfCAQ8DTVi6aJtS -NiY/yUYTsQbh6Mey8QAC5wdoyuVUKQO2SwyNiHFWP+i9aFXr/rv98w== +MIIEogIBAAKCAQEA3IN57h6uzOii4giaDPPMhjc1Dm0gpX4GiwKCeo8jhhN37YjK +OLCLGEYOoyBoMiYr/A5neJ6eixpp9qHGlq3youOHW81dEGqqtasTH7jdXFbnIY72 +Nz8DftzxD8XXrQu8ZjzHWRbFcmvApWEt/7GE1mhUAIDURTdf4JkqeOCVRnGWNat1 +owjypqjhpXGYG/TTfqT+Tw5987O3ZotBjcv//WLEmx1GhKKYKCFeDiRH9ozEtInH +vBOP7u2kE1kU+7V1ijG/vdHSS5/E/KrXMVqxzoES58XNtUqsRH09Gji8ad/cD5GC +k/3f9ZzUFVO33pnnyY5by6QbBevKQe1Jki0WTwIDAQABAoIBAHXbI0jcR0qnL58l +P8iaaP529Tlvo9ovgCm9vqToafEX6KogyQwBd2YS03HmOSpMcoe13yF9jXkFNgsm +LbCM6bibaNXs7cd/axvLgl4a/NyEaeXqtbeTSzf7uC9Y60vGkPwHkfgQjpj39C+v +v9kANOIvQm4+bLVNwkWVNzkBt2a9AlyQJYaeKLWkVKPeoDF9UpdS9CovMQSJ5kZ/ +3I1old4C/AwC7WyyvNikSKwFGH+Sf/2v3RwwsTz4OJbi208A9riwfNJ4YMYC5vFQ +Y28xOPSYhXjuHMeKZV6KP5WKr+bELOzoj1zl1x3SZSZbTszAi8Gz1FTXRVPpCGOQ +HrK+RakCgYEA+kv99xw+zM+YvXYdlj5dF/EofSiD7eU+ASQ64+gnfhZiUeAWqssD +4VK6sS4DYlS0h1TkLtrO3iCYOUD+S9RsqOzUXft5a1wqWYiBa9QOpa6MV3s8DeS9 +wBK8BzMOtrPEsqKwrOe302MV2Aw1GabNVi0aLQPZQvYZBt7Hhe4eXasCgYEA4YnB +W8aPVS3953pQHH9Z6l39oy25GktRzHH3HpnFuSTgumvOvOE9LSLKmMoDpN2Krv4T +1cK2WwLOb+SjOkcY0A1j4+AVLsP9PJbRJbsP7OOVRB1Do7kFzQ9bEGHFpGD3lAkz +ClQSRkivLMsd86m1ivc80cmhNnOgpygteRJcHe0CgYAhhlAr6wKWWC/zIIDyAMRj +Uo/Dw8t3776QVJP2tr+jacgdg1BF7A9G/Ne4p5sYbpQHlF1D0Vbn9aGt+YCWE4vC +TIZdWDN5J80cVOZQ1QRpOKnfhcgTbFHmChxZMoOEASwVaSkU36yFib4BRBFQsEDM +jBn3cY6GI4RSoUBENhKnJQKBgEYAUKBgl5ozhSv0XasKp+jDNXcROPN9Ty0qbi30 +Qlc9p/aUgX1EV42Lz9/uS4U/Mc0wlQ1yutCypUo7Z6It8PiaP1e59DkooY/Nq6qP +TdkTpf+XKahGRBOqYXRLNGHZqt4qoMni4C0qYByCCpDXKr6wEBN5Bm11I/bd1IdQ +eIDdAoGAKAS/R07REX3klh/TV31YdfhaQfrhtVLQ4LArfEE13XOHF2zILkO849Gf +XpKtcIXDh9JVK7XagreHnYThBigEnmQckwfc1AoJf/KmCa+PSrsDMTdR0IMHIcr3 +zgchjL3chUpgvj2ckaqXf4qxUml0Lgm/qF45mJ7ut5k9TcKiKyY= -----END RSA PRIVATE KEY----- diff -Nru rust-tonic-0.12.3+dfsg/interop/data/server1.pem rust-tonic-0.13.0+dfsg/interop/data/server1.pem --- rust-tonic-0.12.3+dfsg/interop/data/server1.pem 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/interop/data/server1.pem 2025-03-25 18:49:19.000000000 +0000 @@ -1,20 +1,20 @@ -----BEGIN CERTIFICATE----- -MIIDSzCCAjOgAwIBAgIQQY5jNBnC4CbgToZB9CzLYzANBgkqhkiG9w0BAQsFADA9 -MQ4wDAYDVQQKEwVUb2tpbzEQMA4GA1UECxMHVGVzdGluZzEZMBcGA1UEAxMQVG9u -aWMgVGVzdGluZyBDQTAeFw0xOTExMDkxNjQ3NTRaFw0yNDExMDcxNjQ3NTRaMCEx -HzAdBgNVBAMTFlRvbmljIFRlc3QgU2VydmVyIENlcnQwggEiMA0GCSqGSIb3DQEB -AQUAA4IBDwAwggEKAoIBAQC9Kjy22h0ODjDCPBI9RFxOBU+Hsi/0T4+wJMdbtvUO -i/+2PGyeGAtmTJKJxIsiEe/7/XsNgdox3tqXHkBhv4HGT8q9K5H+yqDRAly5Dimu -nNxxAdiaolN4xYUI68BRJI5lfA7EocU2gsJVCxkwyzUmZTACi6f2hzK6he40Vr/Y -aY075w20FJrVmcTKG5b1HyUV2njV704bVzp2B2Hcaro0SoNpE38VtoEBAk7q7yMM -KVyp4Hm499coQQ2Tx53vXNFEnu7g+Of6oKDL11rPqFG4/swYqete3WPl/vcmdkXl -sW4z4GDoyobpdW+shUN2+h3apakxOMKkg2njmLSWRmkBAgMBAAGjYzBhMBMGA1Ud -JQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUTk3fo0rW -otYYPFIk/cgX0fags78wGwYDVR0RBBQwEoIQKi50ZXN0Lmdvb2dsZS5mcjANBgkq -hkiG9w0BAQsFAAOCAQEATypihaP3RfHar9DZGeKwPz25BQBhGJE2lEwlehUuNBkG -M1+yQw0p3Yj2n5tR87Qp+G5rDN51e2OCWbEMTo0iAiGdic4N4FXnmA7R9QDcBcKw -YmKIXd48F+Ceh5XYGFuR14dTshu2Zajwcw4OW3dhvEbv9h5pMCJXplhAPHvhTKTM -TTl0fjDkBcOdKWswdQCtt2xOqcQhpYdVYClYym22WYDcMDr7CqiC/y3jLl6eVpsA -hPDAVZqpZSpnV6isihoUyDeSVT830/E/n8e756l7gBNxpsYF+PqDBVPiAU7Gjp99 -EegYn7LiB+s4tgHeSMdNclffWQJ3PameoaLHdikGLA== +MIIDTDCCAjSgAwIBAgIRAOV5RsOfZrNiE0WwnPv5MSswDQYJKoZIhvcNAQELBQAw +PTEOMAwGA1UEChMFVG9raW8xEDAOBgNVBAsTB1Rlc3RpbmcxGTAXBgNVBAMTEFRv +bmljIFRlc3RpbmcgQ0EwHhcNMjQxMTEzMTk0NDMwWhcNMjkxMTEyMTk0NDMwWjAh +MR8wHQYDVQQDExZUb25pYyBUZXN0IFNlcnZlciBDZXJ0MIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA3IN57h6uzOii4giaDPPMhjc1Dm0gpX4GiwKCeo8j +hhN37YjKOLCLGEYOoyBoMiYr/A5neJ6eixpp9qHGlq3youOHW81dEGqqtasTH7jd +XFbnIY72Nz8DftzxD8XXrQu8ZjzHWRbFcmvApWEt/7GE1mhUAIDURTdf4JkqeOCV +RnGWNat1owjypqjhpXGYG/TTfqT+Tw5987O3ZotBjcv//WLEmx1GhKKYKCFeDiRH +9ozEtInHvBOP7u2kE1kU+7V1ijG/vdHSS5/E/KrXMVqxzoES58XNtUqsRH09Gji8 +ad/cD5GCk/3f9ZzUFVO33pnnyY5by6QbBevKQe1Jki0WTwIDAQABo2MwYTATBgNV +HSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaAFOpOJS3V +SO2AjQPXkAW/ITt5yBVUMBsGA1UdEQQUMBKCECoudGVzdC5nb29nbGUuZnIwDQYJ +KoZIhvcNAQELBQADggEBABdvT3D98vfZxGPXddVG6SYMXLzoWrvF4R0joI2xzaiZ +XlLwo2pPiYY/mUNjNEQCbz9cV4E+EWFzP36YSiFpiQHH65zhgMRMBTlwe6ma+ksJ +kiBwwcDaugDGBrC5YGAypMB+a/8PZiUkODp6S/A4DgNCO4RO+c+lG1QCO335i41A +hVJZVQUKQ2t8SbkVQmugDfZFrp/fDJVRGKwNKWM1d3B40/6TaUc0z9jHBGOzy3GI +ZcO2Di5vqE+gY0oSb4MLlR3PIavPQvNhHCejD6qxIpivOMEpdNv2oAkcfoePuJQ3 +TfqJO8ybedfAGYf/p70Dw45lY7+/aGLIhnQ8nHQmUZE= -----END CERTIFICATE----- diff -Nru rust-tonic-0.12.3+dfsg/interop/LICENSE rust-tonic-0.13.0+dfsg/interop/LICENSE --- rust-tonic-0.12.3+dfsg/interop/LICENSE 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/interop/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -Copyright (c) 2020 Lucio Franco - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff -Nru rust-tonic-0.12.3+dfsg/interop/src/lib.rs rust-tonic-0.13.0+dfsg/interop/src/lib.rs --- rust-tonic-0.12.3+dfsg/interop/src/lib.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/interop/src/lib.rs 2025-03-25 18:49:19.000000000 +0000 @@ -18,14 +18,14 @@ pub fn client_payload(size: usize) -> pb::Payload { pb::Payload { r#type: default::Default::default(), - body: iter::repeat(0u8).take(size).collect(), + body: iter::repeat_n(0u8, size).collect(), } } pub fn server_payload(size: usize) -> pb::Payload { pb::Payload { r#type: default::Default::default(), - body: iter::repeat(0u8).take(size).collect(), + body: iter::repeat_n(0u8, size).collect(), } } diff -Nru rust-tonic-0.12.3+dfsg/interop/src/server.rs rust-tonic-0.13.0+dfsg/interop/src/server.rs --- rust-tonic-0.12.3+dfsg/interop/src/server.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/interop/src/server.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,14 +1,14 @@ use crate::pb::{self, *}; use async_stream::try_stream; -use http::header::{HeaderName, HeaderValue}; -use http_body::Body; +use http::header::{HeaderMap, HeaderName}; +use http_body_util::BodyExt; use std::future::Future; use std::pin::Pin; use std::result::Result as StdResult; -use std::task::{ready, Context, Poll}; +use std::task::{Context, Poll}; use std::time::Duration; use tokio_stream::StreamExt; -use tonic::{body::BoxBody, server::NamedService, Code, Request, Response, Status}; +use tonic::{body::Body, server::NamedService, Code, Request, Response, Status}; use tower::Service; pub use pb::test_service_server::TestServiceServer; @@ -180,9 +180,9 @@ } } -impl Service> for EchoHeadersSvc +impl Service> for EchoHeadersSvc where - S: Service, Response = http::Response> + Send, + S: Service, Response = http::Response> + Send, S::Future: Send + 'static, { type Response = S::Response; @@ -193,14 +193,15 @@ Ok(()).into() } - fn call(&mut self, req: http::Request) -> Self::Future { + fn call(&mut self, req: http::Request) -> Self::Future { let echo_header = req.headers().get("x-grpc-test-echo-initial").cloned(); + let trailer_name = HeaderName::from_static("x-grpc-test-echo-trailing-bin"); let echo_trailer = req .headers() - .get("x-grpc-test-echo-trailing-bin") + .get(&trailer_name) .cloned() - .map(|v| (HeaderName::from_static("x-grpc-test-echo-trailing-bin"), v)); + .map(|v| HeaderMap::from_iter(std::iter::once((trailer_name, v)))); let call = self.inner.call(req); @@ -211,43 +212,11 @@ res.headers_mut() .insert("x-grpc-test-echo-initial", echo_header); Ok(res - .map(|b| MergeTrailers::new(b, echo_trailer)) - .map(BoxBody::new)) + .map(|b| b.with_trailers(async move { echo_trailer.map(Ok) })) + .map(Body::new)) } else { Ok(res) } }) } } - -pub struct MergeTrailers { - inner: B, - trailer: Option<(HeaderName, HeaderValue)>, -} - -impl MergeTrailers { - pub fn new(inner: B, trailer: Option<(HeaderName, HeaderValue)>) -> Self { - Self { inner, trailer } - } -} - -impl Body for MergeTrailers { - type Data = B::Data; - type Error = B::Error; - - fn poll_frame( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll, Self::Error>>> { - let this = self.get_mut(); - let mut frame = ready!(Pin::new(&mut this.inner).poll_frame(cx)?); - if let Some(frame) = frame.as_mut() { - if let Some(trailers) = frame.trailers_mut() { - if let Some((key, value)) = &this.trailer { - trailers.insert(key.clone(), value.clone()); - } - } - } - Poll::Ready(frame.map(Ok)) - } -} diff -Nru rust-tonic-0.12.3+dfsg/LICENSE rust-tonic-0.13.0+dfsg/LICENSE --- rust-tonic-0.12.3+dfsg/LICENSE 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/LICENSE 2025-03-25 18:49:19.000000000 +0000 @@ -1,4 +1,4 @@ -Copyright (c) 2020 Lucio Franco +Copyright (c) 2025 Lucio Franco Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff -Nru rust-tonic-0.12.3+dfsg/prepare-release.sh rust-tonic-0.13.0+dfsg/prepare-release.sh --- rust-tonic-0.12.3+dfsg/prepare-release.sh 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/prepare-release.sh 2025-03-25 18:49:19.000000000 +0000 @@ -33,14 +33,6 @@ ) for CRATE in "${CRATES[@]}"; do - # Update html_root_url attributes. - sed -i -E "s~html_root_url = \"https://docs\.rs/${TONIC_CRATE_MATCHER}/$VERSION_MATCHER\"~html_root_url = \"https://docs.rs/\1/${VERSION}\"~" \ - "$DIR/$CRATE/src/lib.rs" - - # Update documentation url in Cargo.toml - sed -i -E "s~documentation = \"https://docs\.rs/$CRATE/$VERSION_MATCHER\"~documentation = \"https://docs.rs/${CRATE}/${VERSION}\"~" \ - "$DIR/$CRATE/Cargo.toml" - # Update Cargo.toml version fields. sed -i -E "s/^version = \"${VERSION_MATCHER}\"$/version = \"${VERSION}\"/" \ "$DIR/$CRATE/Cargo.toml" diff -Nru rust-tonic-0.12.3+dfsg/README.md rust-tonic-0.13.0+dfsg/README.md --- rust-tonic-0.12.3+dfsg/README.md 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/README.md 2025-03-25 18:49:19.000000000 +0000 @@ -10,7 +10,7 @@ [![Crates.io](https://img.shields.io/crates/l/tonic)](LICENSE) -[Examples] | [Website] | [Docs] | [Chat] +[Examples] | [Website] | [Docs] | [Chat][discord] ## Overview @@ -33,86 +33,42 @@ ## Getting Started +- The [`helloworld`][helloworld-tutorial] tutorial provides a basic example of using `tonic`, perfect for first time users! +- The [`routeguide`][routeguide-tutorial] tutorial provides a complete example of using `tonic` and all its features. + Examples can be found in [`examples`] and for more complex scenarios [`interop`] may be a good resource as it shows examples of many of the gRPC features. -If you're using [rust-analyzer] we recommend you set `"rust-analyzer.cargo.buildScripts.enable": true` to correctly load -the generated code. - -For IntelliJ IDEA users, please refer to [this](https://github.com/intellij-rust/intellij-rust/pull/8056) and enable -`org.rust.cargo.evaluate.build.scripts` -[experimental feature](https://plugins.jetbrains.com/plugin/8182-rust/docs/rust-faq.html#experimental-features). - ### Rust Version -`tonic`'s MSRV is `1.71.1`. - -```bash -$ rustup update -$ cargo build -``` +`tonic`'s MSRV is `1.75`. ### Dependencies -In order to build `tonic` >= 0.8.0, you need the `protoc` Protocol Buffers compiler, along with Protocol Buffers resource files. - -#### Ubuntu - -```bash -sudo apt update && sudo apt upgrade -y -sudo apt install -y protobuf-compiler libprotobuf-dev -``` - -#### Alpine Linux +[`tonic-build`] uses `protoc` [Protocol Buffers compiler] in some APIs which compile Protocol Buffers resource files such as [`tonic_build::compile_protos()`]. -```sh -sudo apk add protoc protobuf-dev -``` - -#### macOS - -Assuming [Homebrew](https://brew.sh/) is already installed. (If not, see instructions for installing Homebrew on [the Homebrew website](https://brew.sh/).) - -```zsh -brew install protobuf -``` - -#### Windows - -- Download the latest version of `protoc-xx.y-win64.zip` from [HERE](https://github.com/protocolbuffers/protobuf/releases/latest) -- Extract the file `bin\protoc.exe` and put it somewhere in the `PATH` -- Verify installation by opening a command prompt and enter `protoc --version` - -### Tutorials - -- The [`helloworld`][helloworld-tutorial] tutorial provides a basic example of using `tonic`, perfect for first time users! -- The [`routeguide`][routeguide-tutorial] tutorial provides a complete example of using `tonic` and all its -features. +[Protocol Buffers compiler]: https://protobuf.dev/downloads/ +[`tonic_build::compile_protos()`]: https://docs.rs/tonic-build/latest/tonic_build/fn.compile_protos.html ## Getting Help First, see if the answer to your question can be found in the API documentation. If the answer is not there, there is an active community in -the [Tonic Discord channel][chat]. We would be happy to try to answer your +the [Tonic Discord channel][discord]. We would be happy to try to answer your question. If that doesn't work, try opening an [issue] with the question. -[chat]: https://discord.gg/6yGkFeN -[issue]: https://github.com/hyperium/tonic/issues/new +[issue]: https://github.com/hyperium/tonic/issues/new/choose ## Project Layout -- [`tonic`](https://github.com/hyperium/tonic/tree/master/tonic): Generic gRPC and HTTP/2 client/server -implementation. -- [`tonic-build`](https://github.com/hyperium/tonic/tree/master/tonic-build): [`prost`] based service codegen. -- [`tonic-types`](https://github.com/hyperium/tonic/tree/master/tonic-types): [`prost`] based grpc utility types - including support for gRPC Well Known Types. -- [`tonic-health`](https://github.com/hyperium/tonic/tree/master/tonic-health): Implementation of the standard [gRPC -health checking service][healthcheck]. Also serves as an example of both unary and response streaming. -- [`tonic-reflection`](https://github.com/hyperium/tonic/tree/master/tonic-reflection): A tonic based gRPC -reflection implementation. -- [`examples`](https://github.com/hyperium/tonic/tree/master/examples): Example gRPC implementations showing off -tls, load balancing and bi-directional streaming. -- [`interop`](https://github.com/hyperium/tonic/tree/master/interop): Interop tests implementation. +- [`tonic`]: Generic gRPC and HTTP/2 client/server implementation. +- [`tonic-build`]: [`prost`] based service codegen. +- [`tonic-types`]: [`prost`] based grpc utility types including support for gRPC Well Known Types. +- [`tonic-health`]: Implementation of the standard [gRPC health checking service][healthcheck]. + Also serves as an example of both unary and response streaming. +- [`tonic-reflection`]: A tonic based gRPC reflection implementation. +- [`examples`]: Example gRPC implementations showing off tls, load balancing and bi-directional streaming. +- [`interop`]: Interop tests implementation. ## Contributing @@ -134,19 +90,23 @@ [gRPC]: https://grpc.io -[`tonic`]: https://github.com/hyperium/tonic +[`tonic`]: ./tonic +[`tonic-build`]: ./tonic-build +[`tonic-types`]: ./tonic-types +[`tonic-health`]: ./tonic-health +[`tonic-reflection`]: ./tonic-reflection +[`examples`]: ./examples +[`interop`]: ./interop [`tokio`]: https://github.com/tokio-rs/tokio [`hyper`]: https://github.com/hyperium/hyper [`prost`]: https://github.com/tokio-rs/prost -[`protobuf`]: https://developers.google.com/protocol-buffers +[`protobuf`]: https://protobuf.dev/ [`rustls`]: https://github.com/rustls/rustls -[`examples`]: https://github.com/hyperium/tonic/tree/master/examples [`interop`]: https://github.com/hyperium/tonic/tree/master/interop [Examples]: https://github.com/hyperium/tonic/tree/master/examples [Website]: https://github.com/hyperium/tonic [Docs]: https://docs.rs/tonic -[Chat]: https://discord.gg/6yGkFeN +[discord]: https://discord.gg/6yGkFeN [routeguide-tutorial]: https://github.com/hyperium/tonic/blob/master/examples/routeguide-tutorial.md [helloworld-tutorial]: https://github.com/hyperium/tonic/blob/master/examples/helloworld-tutorial.md -[healthcheck]: https://github.com/grpc/grpc/blob/master/doc/health-checking.md -[rust-analyzer]: https://rust-analyzer.github.io +[healthcheck]: https://grpc.io/docs/guides/health-checking/ diff -Nru rust-tonic-0.12.3+dfsg/rustfmt.toml rust-tonic-0.13.0+dfsg/rustfmt.toml --- rust-tonic-0.12.3+dfsg/rustfmt.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/rustfmt.toml 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -edition = "2021" diff -Nru rust-tonic-0.12.3+dfsg/tests/ambiguous_methods/Cargo.toml rust-tonic-0.13.0+dfsg/tests/ambiguous_methods/Cargo.toml --- rust-tonic-0.12.3+dfsg/tests/ambiguous_methods/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/ambiguous_methods/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -3,8 +3,6 @@ edition = "2021" license = "MIT" name = "ambiguous_methods" -publish = false -version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff -Nru rust-tonic-0.12.3+dfsg/tests/ambiguous_methods/LICENSE rust-tonic-0.13.0+dfsg/tests/ambiguous_methods/LICENSE --- rust-tonic-0.12.3+dfsg/tests/ambiguous_methods/LICENSE 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/ambiguous_methods/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -Copyright (c) 2020 Lucio Franco - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff -Nru rust-tonic-0.12.3+dfsg/tests/ambiguous_methods/src/lib.rs rust-tonic-0.13.0+dfsg/tests/ambiguous_methods/src/lib.rs --- rust-tonic-0.12.3+dfsg/tests/ambiguous_methods/src/lib.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/ambiguous_methods/src/lib.rs 2025-03-25 18:49:19.000000000 +0000 @@ -0,0 +1 @@ +tonic::include_proto!("ambiguous_methods"); diff -Nru rust-tonic-0.12.3+dfsg/tests/ambiguous_methods/src/main.rs rust-tonic-0.13.0+dfsg/tests/ambiguous_methods/src/main.rs --- rust-tonic-0.12.3+dfsg/tests/ambiguous_methods/src/main.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/ambiguous_methods/src/main.rs 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -#[macro_use] -extern crate tonic; - -tonic::include_proto!("ambiguous_methods"); - -fn main() { - println!("Hello, world!"); -} diff -Nru rust-tonic-0.12.3+dfsg/tests/compression/Cargo.toml rust-tonic-0.13.0+dfsg/tests/compression/Cargo.toml --- rust-tonic-0.12.3+dfsg/tests/compression/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/compression/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -3,8 +3,6 @@ edition = "2021" license = "MIT" name = "compression" -publish = false -version = "0.1.0" [dependencies] bytes = "1" @@ -18,9 +16,9 @@ prost = "0.13" tokio = {version = "1.0", features = ["macros", "rt-multi-thread", "net"]} tokio-stream = "0.1" -tonic = {path = "../../tonic", features = ["gzip", "zstd"]} -tower = {version = "0.4", features = []} -tower-http = {version = "0.5", features = ["map-response-body", "map-request-body"]} +tonic = {path = "../../tonic", features = ["gzip", "deflate", "zstd"]} +tower = "0.5" +tower-http = {version = "0.6", features = ["map-response-body", "map-request-body"]} [build-dependencies] tonic-build = {path = "../../tonic-build" } diff -Nru rust-tonic-0.12.3+dfsg/tests/compression/LICENSE rust-tonic-0.13.0+dfsg/tests/compression/LICENSE --- rust-tonic-0.12.3+dfsg/tests/compression/LICENSE 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/compression/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -Copyright (c) 2020 Lucio Franco - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff -Nru rust-tonic-0.12.3+dfsg/tests/compression/src/bidirectional_stream.rs rust-tonic-0.13.0+dfsg/tests/compression/src/bidirectional_stream.rs --- rust-tonic-0.12.3+dfsg/tests/compression/src/bidirectional_stream.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/compression/src/bidirectional_stream.rs 2025-03-25 18:49:19.000000000 +0000 @@ -6,6 +6,7 @@ client_enabled_server_enabled, zstd: CompressionEncoding::Zstd, gzip: CompressionEncoding::Gzip, + deflate: CompressionEncoding::Deflate, } #[allow(dead_code)] @@ -34,6 +35,7 @@ let expected = match self.encoding { CompressionEncoding::Gzip => "gzip", CompressionEncoding::Zstd => "zstd", + CompressionEncoding::Deflate => "deflate", _ => panic!("unexpected encoding {:?}", self.encoding), }; assert_eq!(req.headers().get("grpc-encoding").unwrap(), expected); @@ -86,6 +88,7 @@ let expected = match encoding { CompressionEncoding::Gzip => "gzip", CompressionEncoding::Zstd => "zstd", + CompressionEncoding::Deflate => "deflate", _ => panic!("unexpected encoding {:?}", encoding), }; assert_eq!(res.metadata().get("grpc-encoding").unwrap(), expected); diff -Nru rust-tonic-0.12.3+dfsg/tests/compression/src/client_stream.rs rust-tonic-0.13.0+dfsg/tests/compression/src/client_stream.rs --- rust-tonic-0.12.3+dfsg/tests/compression/src/client_stream.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/compression/src/client_stream.rs 2025-03-25 18:49:19.000000000 +0000 @@ -6,6 +6,7 @@ client_enabled_server_enabled, zstd: CompressionEncoding::Zstd, gzip: CompressionEncoding::Gzip, + deflate: CompressionEncoding::Deflate, } #[allow(dead_code)] @@ -31,6 +32,7 @@ let expected = match self.encoding { CompressionEncoding::Gzip => "gzip", CompressionEncoding::Zstd => "zstd", + CompressionEncoding::Deflate => "deflate", _ => panic!("unexpected encoding {:?}", self.encoding), }; assert_eq!(req.headers().get("grpc-encoding").unwrap(), expected); @@ -77,6 +79,7 @@ client_disabled_server_enabled, zstd: CompressionEncoding::Zstd, gzip: CompressionEncoding::Gzip, + deflate: CompressionEncoding::Deflate, } #[allow(dead_code)] @@ -127,6 +130,7 @@ client_enabled_server_disabled, zstd: CompressionEncoding::Zstd, gzip: CompressionEncoding::Gzip, + deflate: CompressionEncoding::Deflate, } #[allow(dead_code)] @@ -156,6 +160,7 @@ let expected = match encoding { CompressionEncoding::Gzip => "gzip", CompressionEncoding::Zstd => "zstd", + CompressionEncoding::Deflate => "deflate", _ => panic!("unexpected encoding {:?}", encoding), }; assert_eq!( @@ -171,6 +176,7 @@ compressing_response_from_client_stream, zstd: CompressionEncoding::Zstd, gzip: CompressionEncoding::Gzip, + deflate: CompressionEncoding::Deflate, } #[allow(dead_code)] @@ -211,6 +217,7 @@ let expected = match encoding { CompressionEncoding::Gzip => "gzip", CompressionEncoding::Zstd => "zstd", + CompressionEncoding::Deflate => "deflate", _ => panic!("unexpected encoding {:?}", encoding), }; assert_eq!(res.metadata().get("grpc-encoding").unwrap(), expected); diff -Nru rust-tonic-0.12.3+dfsg/tests/compression/src/compressing_request.rs rust-tonic-0.13.0+dfsg/tests/compression/src/compressing_request.rs --- rust-tonic-0.12.3+dfsg/tests/compression/src/compressing_request.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/compression/src/compressing_request.rs 2025-03-25 18:49:19.000000000 +0000 @@ -6,6 +6,7 @@ client_enabled_server_enabled, zstd: CompressionEncoding::Zstd, gzip: CompressionEncoding::Gzip, + deflate: CompressionEncoding::Deflate, } #[allow(dead_code)] @@ -31,6 +32,7 @@ let expected = match self.encoding { CompressionEncoding::Gzip => "gzip", CompressionEncoding::Zstd => "zstd", + CompressionEncoding::Deflate => "deflate", _ => panic!("unexpected encoding {:?}", self.encoding), }; assert_eq!(req.headers().get("grpc-encoding").unwrap(), expected); @@ -81,6 +83,7 @@ client_enabled_server_enabled_multi_encoding, zstd: CompressionEncoding::Zstd, gzip: CompressionEncoding::Gzip, + deflate: CompressionEncoding::Deflate, } #[allow(dead_code)] @@ -89,12 +92,13 @@ let svc = test_server::TestServer::new(Svc::default()) .accept_compressed(CompressionEncoding::Gzip) - .accept_compressed(CompressionEncoding::Zstd); + .accept_compressed(CompressionEncoding::Zstd) + .accept_compressed(CompressionEncoding::Deflate); let request_bytes_counter = Arc::new(AtomicUsize::new(0)); fn assert_right_encoding(req: http::Request) -> http::Request { - let supported_encodings = ["gzip", "zstd"]; + let supported_encodings = ["gzip", "zstd", "deflate"]; let req_encoding = req.headers().get("grpc-encoding").unwrap(); assert!(supported_encodings.iter().any(|e| e == req_encoding)); @@ -141,6 +145,7 @@ client_enabled_server_disabled, zstd: CompressionEncoding::Zstd, gzip: CompressionEncoding::Gzip, + deflate: CompressionEncoding::Deflate, } #[allow(dead_code)] @@ -171,6 +176,7 @@ let expected = match encoding { CompressionEncoding::Gzip => "gzip", CompressionEncoding::Zstd => "zstd", + CompressionEncoding::Deflate => "deflate", _ => panic!("unexpected encoding {:?}", encoding), }; assert_eq!( @@ -190,6 +196,7 @@ client_mark_compressed_without_header_server_enabled, zstd: CompressionEncoding::Zstd, gzip: CompressionEncoding::Gzip, + deflate: CompressionEncoding::Deflate, } #[allow(dead_code)] diff -Nru rust-tonic-0.12.3+dfsg/tests/compression/src/compressing_response.rs rust-tonic-0.13.0+dfsg/tests/compression/src/compressing_response.rs --- rust-tonic-0.12.3+dfsg/tests/compression/src/compressing_response.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/compression/src/compressing_response.rs 2025-03-25 18:49:19.000000000 +0000 @@ -5,6 +5,7 @@ client_enabled_server_enabled, zstd: CompressionEncoding::Zstd, gzip: CompressionEncoding::Gzip, + deflate: CompressionEncoding::Deflate, } #[allow(dead_code)] @@ -36,6 +37,7 @@ let expected = match self.encoding { CompressionEncoding::Gzip => "gzip", CompressionEncoding::Zstd => "zstd", + CompressionEncoding::Deflate => "deflate", _ => panic!("unexpected encoding {:?}", self.encoding), }; assert_eq!( @@ -85,6 +87,7 @@ let expected = match encoding { CompressionEncoding::Gzip => "gzip", CompressionEncoding::Zstd => "zstd", + CompressionEncoding::Deflate => "deflate", _ => panic!("unexpected encoding {:?}", encoding), }; @@ -100,6 +103,7 @@ client_enabled_server_disabled, zstd: CompressionEncoding::Zstd, gzip: CompressionEncoding::Gzip, + deflate: CompressionEncoding::Deflate, } #[allow(dead_code)] @@ -175,7 +179,8 @@ let mut client = test_client::TestClient::new(mock_io_channel(client).await) .accept_compressed(CompressionEncoding::Gzip) - .accept_compressed(CompressionEncoding::Zstd); + .accept_compressed(CompressionEncoding::Zstd) + .accept_compressed(CompressionEncoding::Deflate); let res = client.compress_output_unary(()).await.unwrap(); @@ -189,6 +194,7 @@ client_disabled, zstd: CompressionEncoding::Zstd, gzip: CompressionEncoding::Gzip, + deflate: CompressionEncoding::Deflate, } #[allow(dead_code)] @@ -259,6 +265,7 @@ server_replying_with_unsupported_encoding, zstd: CompressionEncoding::Zstd, gzip: CompressionEncoding::Gzip, + deflate: CompressionEncoding::Deflate, } #[allow(dead_code)] @@ -302,6 +309,7 @@ disabling_compression_on_single_response, zstd: CompressionEncoding::Zstd, gzip: CompressionEncoding::Gzip, + deflate: CompressionEncoding::Deflate, } #[allow(dead_code)] @@ -344,6 +352,7 @@ let expected = match encoding { CompressionEncoding::Gzip => "gzip", CompressionEncoding::Zstd => "zstd", + CompressionEncoding::Deflate => "deflate", _ => panic!("unexpected encoding {:?}", encoding), }; assert_eq!(res.metadata().get("grpc-encoding").unwrap(), expected); @@ -356,6 +365,7 @@ disabling_compression_on_response_but_keeping_compression_on_stream, zstd: CompressionEncoding::Zstd, gzip: CompressionEncoding::Gzip, + deflate: CompressionEncoding::Deflate, } #[allow(dead_code)] @@ -400,6 +410,7 @@ let expected = match encoding { CompressionEncoding::Gzip => "gzip", CompressionEncoding::Zstd => "zstd", + CompressionEncoding::Deflate => "deflate", _ => panic!("unexpected encoding {:?}", encoding), }; assert_eq!(res.metadata().get("grpc-encoding").unwrap(), expected); @@ -425,6 +436,7 @@ disabling_compression_on_response_from_client_stream, zstd: CompressionEncoding::Zstd, gzip: CompressionEncoding::Gzip, + deflate: CompressionEncoding::Deflate, } #[allow(dead_code)] @@ -469,6 +481,7 @@ let expected = match encoding { CompressionEncoding::Gzip => "gzip", CompressionEncoding::Zstd => "zstd", + CompressionEncoding::Deflate => "deflate", _ => panic!("unexpected encoding {:?}", encoding), }; assert_eq!(res.metadata().get("grpc-encoding").unwrap(), expected); diff -Nru rust-tonic-0.12.3+dfsg/tests/compression/src/server_stream.rs rust-tonic-0.13.0+dfsg/tests/compression/src/server_stream.rs --- rust-tonic-0.12.3+dfsg/tests/compression/src/server_stream.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/compression/src/server_stream.rs 2025-03-25 18:49:19.000000000 +0000 @@ -6,6 +6,7 @@ client_enabled_server_enabled, zstd: CompressionEncoding::Zstd, gzip: CompressionEncoding::Gzip, + deflate: CompressionEncoding::Deflate, } #[allow(dead_code)] @@ -45,6 +46,7 @@ let expected = match encoding { CompressionEncoding::Gzip => "gzip", CompressionEncoding::Zstd => "zstd", + CompressionEncoding::Deflate => "deflate", _ => panic!("unexpected encoding {:?}", encoding), }; assert_eq!(res.metadata().get("grpc-encoding").unwrap(), expected); @@ -121,6 +123,7 @@ client_enabled_server_disabled, zstd: CompressionEncoding::Zstd, gzip: CompressionEncoding::Gzip, + deflate: CompressionEncoding::Deflate, } #[allow(dead_code)] diff -Nru rust-tonic-0.12.3+dfsg/tests/compression/src/util.rs rust-tonic-0.13.0+dfsg/tests/compression/src/util.rs --- rust-tonic-0.12.3+dfsg/tests/compression/src/util.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/compression/src/util.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,6 +1,6 @@ use super::*; use bytes::{Buf, Bytes}; -use http_body::{Body, Frame}; +use http_body::{Body as HttpBody, Frame}; use http_body_util::BodyExt as _; use hyper_util::rt::TokioIo; use pin_project::pin_project; @@ -13,7 +13,7 @@ task::{ready, Context, Poll}, }; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; -use tonic::body::BoxBody; +use tonic::body::Body; use tonic::codec::CompressionEncoding; use tonic::transport::{server::Connected, Channel}; use tower_http::map_request_body::MapRequestBodyLayer; @@ -42,9 +42,9 @@ pub counter: Arc, } -impl Body for CountBytesBody +impl HttpBody for CountBytesBody where - B: Body, + B: HttpBody, { type Data = B::Data; type Error = B::Error; @@ -95,7 +95,7 @@ } } -impl Body for ChannelBody +impl HttpBody for ChannelBody where T: Buf, { @@ -114,8 +114,8 @@ #[allow(dead_code)] pub fn measure_request_body_size_layer( bytes_sent_counter: Arc, -) -> MapRequestBodyLayer BoxBody + Clone> { - MapRequestBodyLayer::new(move |mut body: BoxBody| { +) -> MapRequestBodyLayer Body + Clone> { + MapRequestBodyLayer::new(move |mut body: Body| { let (tx, new_body) = ChannelBody::new(); let bytes_sent_counter = bytes_sent_counter.clone(); @@ -128,7 +128,7 @@ } }); - new_body.boxed_unsync() + Body::new(new_body) }) } @@ -157,10 +157,11 @@ Self { encoding } } - pub fn call(self, req: http::Request) -> http::Request { + pub fn call(self, req: http::Request) -> http::Request { let expected = match self.encoding { CompressionEncoding::Gzip => "gzip", CompressionEncoding::Zstd => "zstd", + CompressionEncoding::Deflate => "deflate", _ => panic!("unexpected encoding {:?}", self.encoding), }; assert_eq!(req.headers().get("grpc-encoding").unwrap(), expected); diff -Nru rust-tonic-0.12.3+dfsg/tests/default_stubs/Cargo.toml rust-tonic-0.13.0+dfsg/tests/default_stubs/Cargo.toml --- rust-tonic-0.12.3+dfsg/tests/default_stubs/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/default_stubs/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -3,13 +3,12 @@ edition = "2021" license = "MIT" name = "default_stubs" -publish = false -version = "0.1.0" [dependencies] tokio = {version = "1.0", features = ["macros", "rt-multi-thread", "net"]} tokio-stream = {version = "0.1", features = ["net"]} prost = "0.13" +rand = "0.8" tonic = {path = "../../tonic"} [build-dependencies] diff -Nru rust-tonic-0.12.3+dfsg/tests/default_stubs/src/test_defaults.rs rust-tonic-0.13.0+dfsg/tests/default_stubs/src/test_defaults.rs --- rust-tonic-0.12.3+dfsg/tests/default_stubs/src/test_defaults.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/default_stubs/src/test_defaults.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,8 +1,13 @@ #![allow(unused_imports)] +use crate::test_client::TestClient; use crate::*; +use rand::Rng as _; +use std::env; +use std::fs; use std::net::SocketAddr; use tokio::net::TcpListener; +use tonic::transport::Channel; use tonic::transport::Server; #[cfg(test)] @@ -10,16 +15,14 @@ tokio_stream::iter(1..usize::MAX).map(|_| ()) } -#[tokio::test()] -async fn test_default_stubs() { +#[cfg(test)] +async fn test_default_stubs( + mut client: TestClient, + mut client_default_stubs: TestClient, +) { use tonic::Code; - let addrs = run_services_in_background().await; - // First validate pre-existing functionality (trait has no default implementation, we explicitly return PermissionDenied in lib.rs). - let mut client = test_client::TestClient::connect(format!("http://{}", addrs.0)) - .await - .unwrap(); assert_eq!( client.unary(()).await.unwrap_err().code(), Code::PermissionDenied @@ -46,9 +49,6 @@ ); // Then validate opt-in new functionality (trait has default implementation of returning Unimplemented). - let mut client_default_stubs = test_client::TestClient::connect(format!("http://{}", addrs.1)) - .await - .unwrap(); assert_eq!( client_default_stubs.unary(()).await.unwrap_err().code(), Code::Unimplemented @@ -79,6 +79,27 @@ ); } +#[tokio::test()] +async fn test_default_stubs_tcp() { + let addrs = run_services_in_background().await; + let client = test_client::TestClient::connect(format!("http://{}", addrs.0)) + .await + .unwrap(); + let client_default_stubs = test_client::TestClient::connect(format!("http://{}", addrs.1)) + .await + .unwrap(); + test_default_stubs(client, client_default_stubs).await; +} + +#[tokio::test()] +#[cfg(not(target_os = "windows"))] +async fn test_default_stubs_uds() { + let addrs = run_services_in_background_uds().await; + let client = test_client::TestClient::connect(addrs.0).await.unwrap(); + let client_default_stubs = test_client::TestClient::connect(addrs.1).await.unwrap(); + test_default_stubs(client, client_default_stubs).await; +} + #[cfg(test)] async fn run_services_in_background() -> (SocketAddr, SocketAddr) { let svc = test_server::TestServer::new(Svc {}); @@ -110,3 +131,48 @@ (addr, addr_default_stubs) } + +#[cfg(all(test, not(target_os = "windows")))] +async fn run_services_in_background_uds() -> (String, String) { + use tokio::net::UnixListener; + + let svc = test_server::TestServer::new(Svc {}); + let svc_default_stubs = test_default_server::TestDefaultServer::new(Svc {}); + + let mut rng = rand::thread_rng(); + let suffix: String = (0..8) + .map(|_| rng.sample(rand::distributions::Alphanumeric) as char) + .collect(); + let tmpdir = fs::canonicalize(env::temp_dir()) + .unwrap() + .join(format!("tonic_test_{}", suffix)); + fs::create_dir(&tmpdir).unwrap(); + + let uds_filepath = tmpdir.join("impl.sock").to_str().unwrap().to_string(); + let listener = UnixListener::bind(uds_filepath.as_str()).unwrap(); + let uds_addr = format!("unix://{}", uds_filepath); + + let uds_default_stubs_filepath = tmpdir.join("stub.sock").to_str().unwrap().to_string(); + let listener_default_stubs = UnixListener::bind(uds_default_stubs_filepath.as_str()).unwrap(); + let uds_default_stubs_addr = format!("unix://{}", uds_default_stubs_filepath); + + tokio::spawn(async move { + Server::builder() + .add_service(svc) + .serve_with_incoming(tokio_stream::wrappers::UnixListenerStream::new(listener)) + .await + .unwrap(); + }); + + tokio::spawn(async move { + Server::builder() + .add_service(svc_default_stubs) + .serve_with_incoming(tokio_stream::wrappers::UnixListenerStream::new( + listener_default_stubs, + )) + .await + .unwrap(); + }); + + (uds_addr, uds_default_stubs_addr) +} diff -Nru rust-tonic-0.12.3+dfsg/tests/deprecated_methods/Cargo.toml rust-tonic-0.13.0+dfsg/tests/deprecated_methods/Cargo.toml --- rust-tonic-0.12.3+dfsg/tests/deprecated_methods/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/deprecated_methods/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -2,8 +2,6 @@ name = "deprecated_methods" edition = "2021" license = "MIT" -publish = false -version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff -Nru rust-tonic-0.12.3+dfsg/tests/disable_comments/Cargo.toml rust-tonic-0.13.0+dfsg/tests/disable_comments/Cargo.toml --- rust-tonic-0.12.3+dfsg/tests/disable_comments/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/disable_comments/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -3,8 +3,6 @@ edition = "2021" license = "MIT" name = "disable-comments" -publish = false -version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff -Nru rust-tonic-0.12.3+dfsg/tests/extern_path/my_application/Cargo.toml rust-tonic-0.13.0+dfsg/tests/extern_path/my_application/Cargo.toml --- rust-tonic-0.12.3+dfsg/tests/extern_path/my_application/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/extern_path/my_application/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -3,8 +3,6 @@ edition = "2021" license = "MIT" name = "my_application" -publish = false -version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff -Nru rust-tonic-0.12.3+dfsg/tests/extern_path/my_application/LICENSE rust-tonic-0.13.0+dfsg/tests/extern_path/my_application/LICENSE --- rust-tonic-0.12.3+dfsg/tests/extern_path/my_application/LICENSE 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/extern_path/my_application/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -Copyright (c) 2020 Lucio Franco - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff -Nru rust-tonic-0.12.3+dfsg/tests/extern_path/uuid/Cargo.toml rust-tonic-0.13.0+dfsg/tests/extern_path/uuid/Cargo.toml --- rust-tonic-0.12.3+dfsg/tests/extern_path/uuid/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/extern_path/uuid/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -3,8 +3,6 @@ edition = "2021" license = "MIT" name = "uuid1" -publish = false -version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff -Nru rust-tonic-0.12.3+dfsg/tests/extern_path/uuid/LICENSE rust-tonic-0.13.0+dfsg/tests/extern_path/uuid/LICENSE --- rust-tonic-0.12.3+dfsg/tests/extern_path/uuid/LICENSE 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/extern_path/uuid/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -Copyright (c) 2020 Lucio Franco - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff -Nru rust-tonic-0.12.3+dfsg/tests/included_service/Cargo.toml rust-tonic-0.13.0+dfsg/tests/included_service/Cargo.toml --- rust-tonic-0.12.3+dfsg/tests/included_service/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/included_service/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -3,8 +3,6 @@ edition = "2021" license = "MIT" name = "included_service" -publish = false -version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff -Nru rust-tonic-0.12.3+dfsg/tests/included_service/LICENSE rust-tonic-0.13.0+dfsg/tests/included_service/LICENSE --- rust-tonic-0.12.3+dfsg/tests/included_service/LICENSE 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/included_service/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -Copyright (c) 2020 Lucio Franco - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff -Nru rust-tonic-0.12.3+dfsg/tests/integration_tests/Cargo.toml rust-tonic-0.13.0+dfsg/tests/integration_tests/Cargo.toml --- rust-tonic-0.12.3+dfsg/tests/integration_tests/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/integration_tests/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -3,8 +3,6 @@ edition = "2021" license = "MIT" name = "integration-tests" -publish = false -version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -20,9 +18,10 @@ http = "1" http-body = "1" hyper-util = "0.1" +rustls = {version = "0.23", features = ["ring"]} tokio-stream = {version = "0.1.5", features = ["net"]} -tower = {version = "0.4", features = []} -tower-http = { version = "0.5", features = ["set-header", "trace"] } +tower = "0.5" +tower-http = { version = "0.6", features = ["set-header", "trace"] } tower-service = "0.3" tracing = "0.1" diff -Nru rust-tonic-0.12.3+dfsg/tests/integration_tests/LICENSE rust-tonic-0.13.0+dfsg/tests/integration_tests/LICENSE --- rust-tonic-0.12.3+dfsg/tests/integration_tests/LICENSE 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/integration_tests/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -Copyright (c) 2020 Lucio Franco - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff -Nru rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/client_layer.rs rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/client_layer.rs --- rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/client_layer.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/client_layer.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,9 +1,9 @@ use http::{header::HeaderName, HeaderValue}; use integration_tests::pb::{test_client::TestClient, test_server, Input, Output}; use std::time::Duration; -use tokio::sync::oneshot; +use tokio::{net::TcpListener, sync::oneshot}; use tonic::{ - transport::{Endpoint, Server}, + transport::{server::TcpIncoming, Endpoint, Server}, Request, Response, Status, }; use tower::ServiceBuilder; @@ -26,16 +26,22 @@ let (tx, rx) = oneshot::channel(); let svc = test_server::TestServer::new(Svc); + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let incoming = TcpIncoming::from(listener).with_nodelay(Some(true)); + // Start the server now, second call should succeed let jh = tokio::spawn(async move { Server::builder() .add_service(svc) - .serve_with_shutdown("127.0.0.1:1340".parse().unwrap(), async { drop(rx.await) }) + .serve_with_incoming_shutdown(incoming, async { drop(rx.await) }) .await .unwrap(); }); - let channel = Endpoint::from_static("http://127.0.0.1:1340").connect_lazy(); + let channel = Endpoint::from_shared(format!("http://{addr}")) + .unwrap() + .connect_lazy(); // prior to https://github.com/hyperium/tonic/pull/974 // this would not compile. (specifically the `TraceLayer`) diff -Nru rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/complex_tower_middleware.rs rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/complex_tower_middleware.rs --- rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/complex_tower_middleware.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/complex_tower_middleware.rs 2025-03-25 18:49:19.000000000 +0000 @@ -26,7 +26,7 @@ Server::builder() .layer(MyServiceLayer::new()) .add_service(svc) - .serve("127.0.0.1:1322".parse().unwrap()) + .serve("127.0.0.1:0".parse().unwrap()) .await .unwrap(); } diff -Nru rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/connect_info.rs rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/connect_info.rs --- rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/connect_info.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/connect_info.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,8 +1,11 @@ use integration_tests::pb::{test_client, test_server, Input, Output}; use std::time::Duration; -use tokio::sync::oneshot; +use tokio::{net::TcpListener, sync::oneshot}; use tonic::{ - transport::{server::TcpConnectInfo, Endpoint, Server}, + transport::{ + server::{TcpConnectInfo, TcpIncoming}, + Endpoint, Server, + }, Request, Response, Status, }; @@ -25,17 +28,22 @@ let (tx, rx) = oneshot::channel::<()>(); + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let incoming = TcpIncoming::from(listener).with_nodelay(Some(true)); + let jh = tokio::spawn(async move { Server::builder() .add_service(svc) - .serve_with_shutdown("127.0.0.1:1400".parse().unwrap(), async { drop(rx.await) }) + .serve_with_incoming_shutdown(incoming, async { drop(rx.await) }) .await .unwrap(); }); tokio::time::sleep(Duration::from_millis(100)).await; - let channel = Endpoint::from_static("http://127.0.0.1:1400") + let channel = Endpoint::from_shared(format!("http://{addr}")) + .unwrap() .connect() .await .unwrap(); diff -Nru rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/connection.rs rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/connection.rs --- rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/connection.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/connection.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,9 +1,9 @@ use integration_tests::pb::{test_client::TestClient, test_server, Input, Output}; use std::sync::{Arc, Mutex}; use std::time::Duration; -use tokio::sync::oneshot; +use tokio::{net::TcpListener, sync::oneshot}; use tonic::{ - transport::{Endpoint, Server}, + transport::{server::TcpIncoming, Endpoint, Server}, Code, Request, Response, Status, }; @@ -21,14 +21,17 @@ #[tokio::test] async fn connect_returns_err() { - let res = TestClient::connect("http://thisdoesntexist").await; + let res = TestClient::connect("http://thisdoesntexist.test").await; assert!(res.is_err()); } #[tokio::test] async fn connect_handles_tls() { - TestClient::connect("https://example.com").await.unwrap(); + rustls::crypto::ring::default_provider() + .install_default() + .unwrap(); + TestClient::connect("https://github.com").await.unwrap(); } #[tokio::test] @@ -37,17 +40,21 @@ let sender = Arc::new(Mutex::new(Some(tx))); let svc = test_server::TestServer::new(Svc(sender)); + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let incoming = TcpIncoming::from(listener).with_nodelay(Some(true)); + let jh = tokio::spawn(async move { Server::builder() .add_service(svc) - .serve_with_shutdown("127.0.0.1:1338".parse().unwrap(), async { drop(rx.await) }) + .serve_with_incoming_shutdown(incoming, async { drop(rx.await) }) .await .unwrap(); }); tokio::time::sleep(Duration::from_millis(100)).await; - let mut client = TestClient::connect("http://127.0.0.1:1338").await.unwrap(); + let mut client = TestClient::connect(format!("http://{addr}")).await.unwrap(); // First call should pass, then shutdown the server client.unary_call(Request::new(Input {})).await.unwrap(); @@ -68,22 +75,33 @@ let sender = Arc::new(Mutex::new(Some(tx))); let svc = test_server::TestServer::new(Svc(sender)); - let channel = Endpoint::from_static("http://127.0.0.1:1339").connect_lazy(); + { + let channel = Endpoint::from_static("http://127.0.0.1:0").connect_lazy(); + let mut client = TestClient::new(channel); - let mut client = TestClient::new(channel); + // First call should fail, the server is not running + client.unary_call(Request::new(Input {})).await.unwrap_err(); + } - // First call should fail, the server is not running - client.unary_call(Request::new(Input {})).await.unwrap_err(); + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let incoming = TcpIncoming::from(listener).with_nodelay(Some(true)); // Start the server now, second call should succeed let jh = tokio::spawn(async move { Server::builder() .add_service(svc) - .serve_with_shutdown("127.0.0.1:1339".parse().unwrap(), async { drop(rx.await) }) + .serve_with_incoming_shutdown(incoming, async { drop(rx.await) }) .await .unwrap(); }); + let channel = Endpoint::from_shared(format!("http://{addr}")) + .unwrap() + .connect_lazy(); + + let mut client = TestClient::new(channel); + tokio::time::sleep(Duration::from_millis(100)).await; client.unary_call(Request::new(Input {})).await.unwrap(); diff -Nru rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/extensions.rs rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/extensions.rs --- rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/extensions.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/extensions.rs 2025-03-25 18:49:19.000000000 +0000 @@ -6,11 +6,11 @@ task::{Context, Poll}, time::Duration, }; -use tokio::sync::oneshot; +use tokio::{net::TcpListener, sync::oneshot}; use tonic::{ - body::BoxBody, + body::Body, server::NamedService, - transport::{Endpoint, Server}, + transport::{server::TcpIncoming, Endpoint, Server}, Request, Response, Status, }; use tower_service::Service; @@ -39,17 +39,22 @@ let (tx, rx) = oneshot::channel::<()>(); + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let incoming = TcpIncoming::from(listener).with_nodelay(Some(true)); + let jh = tokio::spawn(async move { Server::builder() .add_service(svc) - .serve_with_shutdown("127.0.0.1:1323".parse().unwrap(), async { drop(rx.await) }) + .serve_with_incoming_shutdown(incoming, async { drop(rx.await) }) .await .unwrap(); }); tokio::time::sleep(Duration::from_millis(100)).await; - let channel = Endpoint::from_static("http://127.0.0.1:1323") + let channel = Endpoint::from_shared(format!("http://{addr}")) + .unwrap() .connect() .await .unwrap(); @@ -83,17 +88,22 @@ let (tx, rx) = oneshot::channel::<()>(); + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let incoming = TcpIncoming::from(listener).with_nodelay(Some(true)); + let jh = tokio::spawn(async move { Server::builder() .add_service(svc) - .serve_with_shutdown("127.0.0.1:1324".parse().unwrap(), async { drop(rx.await) }) + .serve_with_incoming_shutdown(incoming, async { drop(rx.await) }) .await .unwrap(); }); tokio::time::sleep(Duration::from_millis(100)).await; - let channel = Endpoint::from_static("http://127.0.0.1:1324") + let channel = Endpoint::from_shared(format!("http://{addr}")) + .unwrap() .connect() .await .unwrap(); @@ -112,9 +122,9 @@ inner: S, } -impl Service> for InterceptedService +impl Service> for InterceptedService where - S: Service, Response = http::Response> + S: Service, Response = http::Response> + NamedService + Clone + Send @@ -129,7 +139,7 @@ self.inner.poll_ready(cx) } - fn call(&mut self, mut req: http::Request) -> Self::Future { + fn call(&mut self, mut req: http::Request) -> Self::Future { let clone = self.inner.clone(); let mut inner = std::mem::replace(&mut self.inner, clone); diff -Nru rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/http2_keep_alive.rs rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/http2_keep_alive.rs --- rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/http2_keep_alive.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/http2_keep_alive.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,9 +1,9 @@ use std::time::Duration; -use tokio::sync::oneshot; +use tokio::{net::TcpListener, sync::oneshot}; use integration_tests::pb::{test_client::TestClient, test_server, Input, Output}; -use tonic::transport::{Channel, Server}; +use tonic::transport::{server::TcpIncoming, Channel, Server}; use tonic::{Request, Response, Status}; struct Svc; @@ -19,18 +19,23 @@ async fn http2_keepalive_does_not_cause_panics() { let svc = test_server::TestServer::new(Svc {}); let (tx, rx) = oneshot::channel::<()>(); + + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let incoming = TcpIncoming::from(listener).with_nodelay(Some(true)); + let jh = tokio::spawn(async move { Server::builder() .http2_keepalive_interval(Some(Duration::from_secs(10))) .add_service(svc) - .serve_with_shutdown("127.0.0.1:5432".parse().unwrap(), async { drop(rx.await) }) + .serve_with_incoming_shutdown(incoming, async { drop(rx.await) }) .await .unwrap(); }); tokio::time::sleep(Duration::from_millis(100)).await; - let mut client = TestClient::connect("http://127.0.0.1:5432").await.unwrap(); + let mut client = TestClient::connect(format!("http://{addr}")).await.unwrap(); let res = client.unary_call(Request::new(Input {})).await; @@ -44,18 +49,24 @@ async fn http2_keepalive_does_not_cause_panics_on_client_side() { let svc = test_server::TestServer::new(Svc {}); let (tx, rx) = oneshot::channel::<()>(); + + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let incoming = TcpIncoming::from(listener).with_nodelay(Some(true)); + let jh = tokio::spawn(async move { Server::builder() .http2_keepalive_interval(Some(Duration::from_secs(5))) .add_service(svc) - .serve_with_shutdown("127.0.0.1:5431".parse().unwrap(), async { drop(rx.await) }) + .serve_with_incoming_shutdown(incoming, async { drop(rx.await) }) .await .unwrap(); }); tokio::time::sleep(Duration::from_millis(100)).await; - let channel = Channel::from_static("http://127.0.0.1:5431") + let channel = Channel::from_shared(format!("http://{addr}")) + .unwrap() .http2_keep_alive_interval(Duration::from_secs(5)) .connect() .await diff -Nru rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/http2_max_header_list_size.rs rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/http2_max_header_list_size.rs --- rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/http2_max_header_list_size.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/http2_max_header_list_size.rs 2025-03-25 18:49:19.000000000 +0000 @@ -34,10 +34,10 @@ let addr = format!("http://{}", listener.local_addr().unwrap()); let jh = tokio::spawn(async move { - let (nodelay, keepalive) = (true, Some(Duration::from_secs(1))); - let listener = - tonic::transport::server::TcpIncoming::from_listener(listener, nodelay, keepalive) - .unwrap(); + let (nodelay, keepalive) = (Some(true), Some(Duration::from_secs(1))); + let listener = tonic::transport::server::TcpIncoming::from(listener) + .with_nodelay(nodelay) + .with_keepalive(keepalive); Server::builder() .http2_max_pending_accept_reset_streams(Some(0)) .add_service(svc) diff -Nru rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/interceptor.rs rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/interceptor.rs --- rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/interceptor.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/interceptor.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,8 +1,8 @@ use integration_tests::pb::{test_client::TestClient, test_server, Input, Output}; use std::time::Duration; -use tokio::sync::oneshot; +use tokio::{net::TcpListener, sync::oneshot}; use tonic::{ - transport::{Endpoint, Server}, + transport::{server::TcpIncoming, Endpoint, Server}, GrpcMethod, Request, Response, Status, }; @@ -22,16 +22,23 @@ let svc = test_server::TestServer::new(Svc); let (tx, rx) = oneshot::channel(); + + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let incoming = TcpIncoming::from(listener).with_nodelay(Some(true)); + // Start the server now, second call should succeed let jh = tokio::spawn(async move { Server::builder() .add_service(svc) - .serve_with_shutdown("127.0.0.1:1340".parse().unwrap(), async { drop(rx.await) }) + .serve_with_incoming_shutdown(incoming, async { drop(rx.await) }) .await .unwrap(); }); - let channel = Endpoint::from_static("http://127.0.0.1:1340").connect_lazy(); + let channel = Endpoint::from_shared(format!("http://{addr}")) + .unwrap() + .connect_lazy(); fn client_intercept(req: Request<()>) -> Result, Status> { println!("Intercepting client request: {:?}", req); diff -Nru rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/max_message_size.rs rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/max_message_size.rs --- rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/max_message_size.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/max_message_size.rs 2025-03-25 18:49:19.000000000 +0000 @@ -166,10 +166,7 @@ if let Some(client) = client { Ok(TokioIo::new(client)) } else { - Err(std::io::Error::new( - std::io::ErrorKind::Other, - "Client already taken", - )) + Err(std::io::Error::other("Client already taken")) } } })) @@ -335,10 +332,7 @@ if let Some(client) = client { Ok(TokioIo::new(client)) } else { - Err(std::io::Error::new( - std::io::ErrorKind::Other, - "Client already taken", - )) + Err(std::io::Error::other("Client already taken")) } } })) diff -Nru rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/origin.rs rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/origin.rs --- rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/origin.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/origin.rs 2025-03-25 18:49:19.000000000 +0000 @@ -4,10 +4,10 @@ use std::task::Context; use std::task::Poll; use std::time::Duration; -use tokio::sync::oneshot; +use tokio::{net::TcpListener, sync::oneshot}; use tonic::codegen::http::Request; use tonic::{ - transport::{Endpoint, Server}, + transport::{server::TcpIncoming, Endpoint, Server}, Response, Status, }; use tower::Layer; @@ -31,18 +31,23 @@ let (tx, rx) = oneshot::channel::<()>(); + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let incoming = TcpIncoming::from(listener).with_nodelay(Some(true)); + let jh = tokio::spawn(async move { Server::builder() .layer(OriginLayer {}) .add_service(svc) - .serve_with_shutdown("127.0.0.1:1442".parse().unwrap(), async { drop(rx.await) }) + .serve_with_incoming_shutdown(incoming, async { drop(rx.await) }) .await .unwrap(); }); tokio::time::sleep(Duration::from_millis(100)).await; - let channel = Endpoint::from_static("http://127.0.0.1:1442") + let channel = Endpoint::from_shared(format!("http://{addr}")) + .unwrap() .origin("https://docs.rs".parse().expect("valid uri")) .connect() .await @@ -76,9 +81,9 @@ inner: S, } -impl Service> for OriginService +impl Service> for OriginService where - T: Service>, + T: Service>, T::Future: Send + 'static, T::Error: Into>, { @@ -90,7 +95,7 @@ self.inner.poll_ready(cx).map_err(Into::into) } - fn call(&mut self, req: Request) -> Self::Future { + fn call(&mut self, req: Request) -> Self::Future { assert_eq!(req.uri().host(), Some("docs.rs")); let fut = self.inner.call(req); diff -Nru rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/routes_builder.rs rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/routes_builder.rs --- rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/routes_builder.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/routes_builder.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,6 +1,6 @@ use std::time::Duration; -use tokio::sync::oneshot; +use tokio::{net::TcpListener, sync::oneshot}; use tokio_stream::StreamExt; use integration_tests::pb::{ @@ -8,6 +8,7 @@ }; use tonic::codegen::BoxStream; use tonic::service::RoutesBuilder; +use tonic::transport::server::TcpIncoming; use tonic::{ transport::{Endpoint, Server}, Request, Response, Status, @@ -56,17 +57,22 @@ let mut routes_builder = RoutesBuilder::default(); routes_builder.add_service(svc1).add_service(svc2); + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let incoming = TcpIncoming::from(listener).with_nodelay(Some(true)); + let jh = tokio::spawn(async move { Server::builder() .add_routes(routes_builder.routes()) - .serve_with_shutdown("127.0.0.1:1400".parse().unwrap(), async { drop(rx.await) }) + .serve_with_incoming_shutdown(incoming, async { drop(rx.await) }) .await .unwrap(); }); tokio::time::sleep(Duration::from_millis(100)).await; - let channel = Endpoint::from_static("http://127.0.0.1:1400") + let channel = Endpoint::from_shared(format!("http://{addr}")) + .unwrap() .connect() .await .unwrap(); diff -Nru rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/status.rs rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/status.rs --- rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/status.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/status.rs 2025-03-25 18:49:19.000000000 +0000 @@ -6,12 +6,17 @@ test_client, test_server, test_stream_client, test_stream_server, Input, InputStream, Output, OutputStream, }; +use integration_tests::BoxFuture; use std::error::Error; +use std::task::{Context, Poll}; use std::time::Duration; -use tokio::sync::oneshot; +use tokio::{net::TcpListener, sync::oneshot}; +use tonic::body::Body; use tonic::metadata::{MetadataMap, MetadataValue}; -use tonic::transport::Endpoint; -use tonic::{transport::Server, Code, Request, Response, Status}; +use tonic::{ + transport::{server::TcpIncoming, Endpoint, Server}, + Code, Request, Response, Status, +}; #[tokio::test] async fn status_with_details() { @@ -32,17 +37,21 @@ let (tx, rx) = oneshot::channel::<()>(); + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let incoming = TcpIncoming::from(listener).with_nodelay(Some(true)); + let jh = tokio::spawn(async move { Server::builder() .add_service(svc) - .serve_with_shutdown("127.0.0.1:1337".parse().unwrap(), async { drop(rx.await) }) + .serve_with_incoming_shutdown(incoming, async { drop(rx.await) }) .await .unwrap(); }); tokio::time::sleep(Duration::from_millis(100)).await; - let mut channel = test_client::TestClient::connect("http://127.0.0.1:1337") + let mut channel = test_client::TestClient::connect(format!("http://{addr}")) .await .unwrap(); @@ -86,17 +95,21 @@ let (tx, rx) = oneshot::channel::<()>(); + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let incoming = TcpIncoming::from(listener).with_nodelay(Some(true)); + let jh = tokio::spawn(async move { Server::builder() .add_service(svc) - .serve_with_shutdown("127.0.0.1:1338".parse().unwrap(), async { drop(rx.await) }) + .serve_with_incoming_shutdown(incoming, async { drop(rx.await) }) .await .unwrap(); }); tokio::time::sleep(Duration::from_millis(100)).await; - let mut channel = test_client::TestClient::connect("http://127.0.0.1:1338") + let mut channel = test_client::TestClient::connect(format!("http://{addr}")) .await .unwrap(); @@ -153,17 +166,21 @@ let svc = test_stream_server::TestStreamServer::new(Svc); + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let incoming = TcpIncoming::from(listener).with_nodelay(Some(true)); + tokio::spawn(async move { Server::builder() .add_service(svc) - .serve("127.0.0.1:1339".parse().unwrap()) + .serve_with_incoming(incoming) .await .unwrap(); }); tokio::time::sleep(Duration::from_millis(100)).await; - let mut client = test_stream_client::TestStreamClient::connect("http://127.0.0.1:1339") + let mut client = test_stream_client::TestStreamClient::connect(format!("http://{addr}")) .await .unwrap(); @@ -184,7 +201,7 @@ let channel = Endpoint::try_from("http://[::]:50051") .unwrap() .connect_with_connector_lazy(tower::service_fn(move |_: Uri| async move { - Err::, _>(std::io::Error::new(std::io::ErrorKind::Other, "WTF")) + Err::, _>(std::io::Error::other("WTF")) })); let mut client = test_stream_client::TestStreamClient::new(channel); @@ -196,6 +213,93 @@ } #[tokio::test] +async fn status_from_server_stream_with_inferred_status() { + integration_tests::trace_init(); + + struct Svc; + + #[tonic::async_trait] + impl test_stream_server::TestStream for Svc { + type StreamCallStream = Stream; + + async fn stream_call( + &self, + _: Request, + ) -> Result, Status> { + let s = tokio_stream::once(Ok(OutputStream {})); + Ok(Response::new(Box::pin(s) as Self::StreamCallStream)) + } + } + + #[derive(Clone)] + struct TestLayer; + + impl tower::Layer for TestLayer { + type Service = TestService; + + fn layer(&self, _: S) -> Self::Service { + TestService + } + } + + #[derive(Clone)] + struct TestService; + + impl tower::Service> for TestService { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture<'static, Result>; + + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, _: http::Request) -> Self::Future { + Box::pin(async { + Ok(http::Response::builder() + .status(http::StatusCode::BAD_GATEWAY) + .body(Body::empty()) + .unwrap()) + }) + } + } + + let svc = test_stream_server::TestStreamServer::new(Svc); + + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let incoming: TcpIncoming = TcpIncoming::from(listener).with_nodelay(Some(true)); + + tokio::spawn(async move { + Server::builder() + .layer(TestLayer) + .add_service(svc) + .serve_with_incoming(incoming) + .await + .unwrap(); + }); + + tokio::time::sleep(Duration::from_millis(100)).await; + + let mut client = test_stream_client::TestStreamClient::connect(format!("http://{addr}")) + .await + .unwrap(); + + let mut stream = client + .stream_call(InputStream {}) + .await + .unwrap() + .into_inner(); + + assert_eq!( + stream.message().await.unwrap_err().code(), + Code::Unavailable + ); + + assert_eq!(stream.message().await.unwrap(), None); +} + +#[tokio::test] async fn message_and_then_status_from_server_stream() { integration_tests::trace_init(); @@ -219,17 +323,21 @@ let svc = test_stream_server::TestStreamServer::new(Svc); + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let incoming = TcpIncoming::from(listener).with_nodelay(Some(true)); + tokio::spawn(async move { Server::builder() .add_service(svc) - .serve("127.0.0.1:1340".parse().unwrap()) + .serve_with_incoming(incoming) .await .unwrap(); }); tokio::time::sleep(Duration::from_millis(100)).await; - let mut client = test_stream_client::TestStreamClient::connect("http://127.0.0.1:1340") + let mut client = test_stream_client::TestStreamClient::connect(format!("http://{addr}")) .await .unwrap(); diff -Nru rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/streams.rs rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/streams.rs --- rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/streams.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/streams.rs 2025-03-25 18:49:19.000000000 +0000 @@ -31,7 +31,7 @@ let jh = tokio::spawn(async move { Server::builder() .add_service(svc) - .serve_with_shutdown("127.0.0.1:1339".parse().unwrap(), async { drop(rx.await) }) + .serve_with_shutdown("127.0.0.1:0".parse().unwrap(), async { drop(rx.await) }) .await .unwrap(); }); diff -Nru rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/user_agent.rs rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/user_agent.rs --- rust-tonic-0.12.3+dfsg/tests/integration_tests/tests/user_agent.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/integration_tests/tests/user_agent.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,8 +1,8 @@ use integration_tests::pb::{test_client, test_server, Input, Output}; use std::time::Duration; -use tokio::sync::oneshot; +use tokio::{net::TcpListener, sync::oneshot}; use tonic::{ - transport::{Endpoint, Server}, + transport::{server::TcpIncoming, Endpoint, Server}, Request, Response, Status, }; @@ -24,17 +24,22 @@ let (tx, rx) = oneshot::channel::<()>(); + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let incoming = TcpIncoming::from(listener).with_nodelay(Some(true)); + let jh = tokio::spawn(async move { Server::builder() .add_service(svc) - .serve_with_shutdown("127.0.0.1:1322".parse().unwrap(), async { drop(rx.await) }) + .serve_with_incoming_shutdown(incoming, async { drop(rx.await) }) .await .unwrap(); }); tokio::time::sleep(Duration::from_millis(100)).await; - let channel = Endpoint::from_static("http://127.0.0.1:1322") + let channel = Endpoint::from_shared(format!("http://{addr}")) + .unwrap() .user_agent("my-client") .expect("valid user agent") .connect() diff -Nru rust-tonic-0.12.3+dfsg/tests/root-crate-path/Cargo.toml rust-tonic-0.13.0+dfsg/tests/root-crate-path/Cargo.toml --- rust-tonic-0.12.3+dfsg/tests/root-crate-path/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/root-crate-path/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -3,8 +3,6 @@ edition = "2021" license = "MIT" name = "root-crate-path" -publish = false -version = "0.1.0" [dependencies] prost = "0.13" diff -Nru rust-tonic-0.12.3+dfsg/tests/root-crate-path/LICENSE rust-tonic-0.13.0+dfsg/tests/root-crate-path/LICENSE --- rust-tonic-0.12.3+dfsg/tests/root-crate-path/LICENSE 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/root-crate-path/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -Copyright (c) 2020 Lucio Franco - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff -Nru rust-tonic-0.12.3+dfsg/tests/root-crate-path/src/lib.rs rust-tonic-0.13.0+dfsg/tests/root-crate-path/src/lib.rs --- rust-tonic-0.12.3+dfsg/tests/root-crate-path/src/lib.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/root-crate-path/src/lib.rs 2025-03-25 18:49:19.000000000 +0000 @@ -0,0 +1,15 @@ +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Animal { + #[prost(string, optional, tag = "1")] + pub name: ::core::option::Option<::prost::alloc::string::String>, +} + +// pub mod foo; + +pub mod foo { + pub mod bar { + pub mod baz { + tonic::include_proto!("foo.bar.baz"); + } + } +} diff -Nru rust-tonic-0.12.3+dfsg/tests/root-crate-path/src/main.rs rust-tonic-0.13.0+dfsg/tests/root-crate-path/src/main.rs --- rust-tonic-0.12.3+dfsg/tests/root-crate-path/src/main.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/root-crate-path/src/main.rs 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Animal { - #[prost(string, optional, tag = "1")] - pub name: ::core::option::Option<::prost::alloc::string::String>, -} - -// pub mod foo; - -pub mod foo { - pub mod bar { - pub mod baz { - tonic::include_proto!("foo.bar.baz"); - } - } -} - -fn main() { - println!("Hello, world!"); -} diff -Nru rust-tonic-0.12.3+dfsg/tests/same_name/Cargo.toml rust-tonic-0.13.0+dfsg/tests/same_name/Cargo.toml --- rust-tonic-0.12.3+dfsg/tests/same_name/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/same_name/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -3,8 +3,6 @@ edition = "2021" license = "MIT" name = "same_name" -publish = false -version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff -Nru rust-tonic-0.12.3+dfsg/tests/same_name/LICENSE rust-tonic-0.13.0+dfsg/tests/same_name/LICENSE --- rust-tonic-0.12.3+dfsg/tests/same_name/LICENSE 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/same_name/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -Copyright (c) 2020 Lucio Franco - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff -Nru rust-tonic-0.12.3+dfsg/tests/service_named_result/Cargo.toml rust-tonic-0.13.0+dfsg/tests/service_named_result/Cargo.toml --- rust-tonic-0.12.3+dfsg/tests/service_named_result/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/service_named_result/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -1,8 +1,7 @@ [package] name = "service_named_result" -version = "0.1.0" edition = "2021" -publish = false +license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff -Nru rust-tonic-0.12.3+dfsg/tests/service_named_result/LICENSE rust-tonic-0.13.0+dfsg/tests/service_named_result/LICENSE --- rust-tonic-0.12.3+dfsg/tests/service_named_result/LICENSE 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/service_named_result/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -Copyright (c) 2020 Lucio Franco - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff -Nru rust-tonic-0.12.3+dfsg/tests/service_named_service/Cargo.toml rust-tonic-0.13.0+dfsg/tests/service_named_service/Cargo.toml --- rust-tonic-0.12.3+dfsg/tests/service_named_service/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/service_named_service/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -3,8 +3,6 @@ edition = "2021" license = "MIT" name = "service_named_service" -publish = false -version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff -Nru rust-tonic-0.12.3+dfsg/tests/service_named_service/LICENSE rust-tonic-0.13.0+dfsg/tests/service_named_service/LICENSE --- rust-tonic-0.12.3+dfsg/tests/service_named_service/LICENSE 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/service_named_service/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -Copyright (c) 2020 Lucio Franco - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff -Nru rust-tonic-0.12.3+dfsg/tests/skip_debug/Cargo.toml rust-tonic-0.13.0+dfsg/tests/skip_debug/Cargo.toml --- rust-tonic-0.12.3+dfsg/tests/skip_debug/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/skip_debug/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -3,8 +3,6 @@ edition = "2021" license = "MIT" name = "skip_debug" -publish = false -version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff -Nru rust-tonic-0.12.3+dfsg/tests/stream_conflict/Cargo.toml rust-tonic-0.13.0+dfsg/tests/stream_conflict/Cargo.toml --- rust-tonic-0.12.3+dfsg/tests/stream_conflict/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/stream_conflict/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -2,8 +2,6 @@ authors = ["Ben Sully "] edition = "2021" name = "stream_conflict" -publish = false -version = "0.1.0" license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff -Nru rust-tonic-0.12.3+dfsg/tests/stream_conflict/LICENSE rust-tonic-0.13.0+dfsg/tests/stream_conflict/LICENSE --- rust-tonic-0.12.3+dfsg/tests/stream_conflict/LICENSE 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/stream_conflict/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -Copyright (c) 2020 Lucio Franco - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff -Nru rust-tonic-0.12.3+dfsg/tests/stream_conflict/src/lib.rs rust-tonic-0.13.0+dfsg/tests/stream_conflict/src/lib.rs --- rust-tonic-0.12.3+dfsg/tests/stream_conflict/src/lib.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/stream_conflict/src/lib.rs 2025-03-25 18:49:19.000000000 +0000 @@ -0,0 +1,3 @@ +mod stream_conflict { + tonic::include_proto!("stream_conflict"); +} diff -Nru rust-tonic-0.12.3+dfsg/tests/stream_conflict/src/main.rs rust-tonic-0.13.0+dfsg/tests/stream_conflict/src/main.rs --- rust-tonic-0.12.3+dfsg/tests/stream_conflict/src/main.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/stream_conflict/src/main.rs 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -mod stream_conflict { - tonic::include_proto!("stream_conflict"); -} - -fn main() { - println!("Hello, world!"); -} diff -Nru rust-tonic-0.12.3+dfsg/tests/use_arc_self/Cargo.toml rust-tonic-0.13.0+dfsg/tests/use_arc_self/Cargo.toml --- rust-tonic-0.12.3+dfsg/tests/use_arc_self/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/use_arc_self/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -3,8 +3,6 @@ edition = "2021" license = "MIT" name = "use_arc_self" -publish = false -version = "0.1.0" [dependencies] tokio-stream = "0.1" diff -Nru rust-tonic-0.12.3+dfsg/tests/use_arc_self/LICENSE rust-tonic-0.13.0+dfsg/tests/use_arc_self/LICENSE --- rust-tonic-0.12.3+dfsg/tests/use_arc_self/LICENSE 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/use_arc_self/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -Copyright (c) 2020 Lucio Franco - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff -Nru rust-tonic-0.12.3+dfsg/tests/web/Cargo.toml rust-tonic-0.13.0+dfsg/tests/web/Cargo.toml --- rust-tonic-0.12.3+dfsg/tests/web/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/web/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -2,8 +2,6 @@ authors = ["Juan Alvarez "] edition = "2021" name = "test_web" -publish = false -version = "0.1.0" license = "MIT" [dependencies] diff -Nru rust-tonic-0.12.3+dfsg/tests/web/LICENSE rust-tonic-0.13.0+dfsg/tests/web/LICENSE --- rust-tonic-0.12.3+dfsg/tests/web/LICENSE 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/web/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -Copyright (c) 2020 Lucio Franco - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff -Nru rust-tonic-0.12.3+dfsg/tests/web/tests/grpc_web.rs rust-tonic-0.13.0+dfsg/tests/web/tests/grpc_web.rs --- rust-tonic-0.12.3+dfsg/tests/web/tests/grpc_web.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/web/tests/grpc_web.rs 2025-03-25 18:49:19.000000000 +0000 @@ -11,7 +11,7 @@ use prost::Message; use tokio::net::TcpListener; use tokio_stream::wrappers::TcpListenerStream; -use tonic::body::BoxBody; +use tonic::body::Body; use tonic::transport::Server; use test_web::pb::{test_server::TestServer, Input, Output}; @@ -108,7 +108,7 @@ buf.split_to(len + 5).freeze() } -fn build_request(base_uri: String, content_type: &str, accept: &str) -> Request { +fn build_request(base_uri: String, content_type: &str, accept: &str) -> Request { use header::{ACCEPT, CONTENT_TYPE, ORIGIN}; let request_uri = format!("{}/{}/{}", base_uri, "test.Test", "UnaryCall") @@ -129,7 +129,7 @@ .header(ORIGIN, "http://example.com") .header(ACCEPT, format!("application/{}", accept)) .uri(request_uri) - .body(BoxBody::new( + .body(Body::new( Full::new(bytes).map_err(|err| Status::internal(err.to_string())), )) .unwrap() diff -Nru rust-tonic-0.12.3+dfsg/tests/wellknown/Cargo.toml rust-tonic-0.13.0+dfsg/tests/wellknown/Cargo.toml --- rust-tonic-0.12.3+dfsg/tests/wellknown/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/wellknown/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -3,8 +3,6 @@ edition = "2021" license = "MIT" name = "wellknown" -publish = false -version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff -Nru rust-tonic-0.12.3+dfsg/tests/wellknown/LICENSE rust-tonic-0.13.0+dfsg/tests/wellknown/LICENSE --- rust-tonic-0.12.3+dfsg/tests/wellknown/LICENSE 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/wellknown/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -Copyright (c) 2020 Lucio Franco - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff -Nru rust-tonic-0.12.3+dfsg/tests/wellknown-compiled/Cargo.toml rust-tonic-0.13.0+dfsg/tests/wellknown-compiled/Cargo.toml --- rust-tonic-0.12.3+dfsg/tests/wellknown-compiled/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/wellknown-compiled/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -3,8 +3,6 @@ edition = "2021" license = "MIT" name = "wellknown-compiled" -publish = false -version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff -Nru rust-tonic-0.12.3+dfsg/tests/wellknown-compiled/LICENSE rust-tonic-0.13.0+dfsg/tests/wellknown-compiled/LICENSE --- rust-tonic-0.12.3+dfsg/tests/wellknown-compiled/LICENSE 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tests/wellknown-compiled/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -Copyright (c) 2020 Lucio Franco - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff -Nru rust-tonic-0.12.3+dfsg/tonic/benches/decode.rs rust-tonic-0.13.0+dfsg/tonic/benches/decode.rs --- rust-tonic-0.12.3+dfsg/tonic/benches/decode.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/benches/decode.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,3 +1,5 @@ +#![allow(missing_docs)] + use bencher::{benchmark_group, benchmark_main, Bencher}; use bytes::{Buf, BufMut, Bytes, BytesMut}; use http_body::{Body, Frame, SizeHint}; @@ -45,11 +47,11 @@ } impl MockBody { - pub fn new(data: Bytes, chunk_size: usize) -> Self { + fn new(data: Bytes, chunk_size: usize) -> Self { MockBody { data, chunk_size } } - pub fn len(&self) -> usize { + fn len(&self) -> usize { self.data.len() } } diff -Nru rust-tonic-0.12.3+dfsg/tonic/Cargo.toml rust-tonic-0.13.0+dfsg/tonic/Cargo.toml --- rust-tonic-0.12.3+dfsg/tonic/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -2,10 +2,6 @@ name = "tonic" # When releasing to crates.io: # - Remove path dependencies -# - Update html_root_url. -# - Update doc url -# - Cargo.toml -# - README.md # - Update CHANGELOG.md. # - Create "v0.11.x" git tag. authors = ["Lucio Franco "] @@ -13,29 +9,30 @@ description = """ A gRPC over HTTP/2 implementation focused on high performance, interoperability, and flexibility. """ -documentation = "https://docs.rs/tonic/0.12.3" edition = "2021" homepage = "https://github.com/hyperium/tonic" keywords = ["rpc", "grpc", "async", "futures", "protobuf"] license = "MIT" readme = "../README.md" repository = "https://github.com/hyperium/tonic" -version = "0.12.3" +version = "0.13.0" +rust-version = {workspace = true} +exclude = ["benches-disabled"] [features] codegen = ["dep:async-trait"] gzip = ["dep:flate2"] +deflate = ["dep:flate2"] zstd = ["dep:zstd"] -default = ["transport", "codegen", "prost"] +default = ["router", "transport", "codegen", "prost"] prost = ["dep:prost"] -tls = ["dep:rustls-pemfile", "dep:tokio-rustls", "dep:tokio", "tokio?/rt", "tokio?/macros"] -tls-roots = ["tls-native-roots"] # Deprecated. Please use `tls-native-roots` instead. -tls-native-roots = ["tls", "channel", "dep:rustls-native-certs"] -tls-webpki-roots = ["tls", "channel", "dep:webpki-roots"] +_tls-any = ["dep:tokio-rustls", "dep:tokio", "tokio?/rt", "tokio?/macros"] # Internal. Please choose one of `tls-ring` or `tls-aws-lc` +tls-ring = ["_tls-any", "tokio-rustls/ring"] +tls-aws-lc = ["_tls-any", "tokio-rustls/aws-lc-rs"] +tls-native-roots = ["_tls-any", "channel", "dep:rustls-native-certs"] +tls-webpki-roots = ["_tls-any","channel", "dep:webpki-roots"] router = ["dep:axum", "dep:tower", "tower?/util"] server = [ - "router", - "dep:async-stream", "dep:h2", "dep:hyper", "hyper?/server", "dep:hyper-util", "hyper-util?/service", "hyper-util?/server-auto", @@ -78,19 +75,17 @@ async-trait = {version = "0.1.13", optional = true} # transport -async-stream = {version = "0.3", optional = true} h2 = {version = "0.4", optional = true} hyper = {version = "1", features = ["http1", "http2"], optional = true} hyper-util = { version = "0.1.4", features = ["tokio"], optional = true } socket2 = { version = "0.5", optional = true, features = ["all"] } tokio = {version = "1", default-features = false, optional = true} -tower = {version = "0.4.7", default-features = false, optional = true} -axum = {version = "0.7", default-features = false, optional = true} +tower = {version = "0.5", default-features = false, optional = true} +axum = {version = "0.8", default-features = false, optional = true} # rustls -rustls-pemfile = { version = "2.0", optional = true } rustls-native-certs = { version = "0.8", optional = true } -tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "tls12", "ring"], optional = true } +tokio-rustls = { version = "0.26.1", default-features = false, features = ["logging", "tls12"], optional = true } webpki-roots = { version = "0.26", optional = true } # compression @@ -104,14 +99,15 @@ bencher = "0.1.5" quickcheck = "1.0" quickcheck_macros = "1.0" -rand = "0.8" static_assertions = "1.0" tokio = {version = "1.0", features = ["rt", "macros"]} -tower = {version = "0.4.7", features = ["full"]} +tower = {version = "0.5", features = ["load-shed", "timeout"]} + +[lints] +workspace = true [package.metadata.docs.rs] all-features = true -rustdoc-args = ["--cfg", "docsrs"] [package.metadata.cargo_check_external_types] allowed_external_types = [ @@ -129,11 +125,10 @@ "async_trait::async_trait", "axum_core::body::Body", + "axum_core::response::into_response::IntoResponse", "axum::routing::Router", "futures_core::stream::Stream", "h2::error::Error", - "http_body_util::combinators::box_body::UnsyncBoxBody", - "tower::discover::Change", "tower_service::Service", "tower_layer::Layer", "tower_layer::stack::Stack", diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/body.rs rust-tonic-0.13.0+dfsg/tonic/src/body.rs --- rust-tonic-0.12.3+dfsg/tonic/src/body.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/body.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,22 +1,94 @@ //! HTTP specific body utilities. -use http_body_util::BodyExt; +use std::{pin::Pin, task::Poll}; -/// A type erased HTTP body used for tonic services. -pub type BoxBody = http_body_util::combinators::UnsyncBoxBody; +use http_body_util::BodyExt as _; -/// Convert a [`http_body::Body`] into a [`BoxBody`]. -pub fn boxed(body: B) -> BoxBody -where - B: http_body::Body + Send + 'static, - B::Error: Into, -{ - body.map_err(crate::Status::map_error).boxed_unsync() +// A type erased HTTP body. +type BoxBody = http_body_util::combinators::UnsyncBoxBody; + +/// A body type used in `tonic`. +#[derive(Debug)] +pub struct Body { + kind: Kind, +} + +#[derive(Debug)] +enum Kind { + Empty, + Wrap(BoxBody), } -/// Create an empty `BoxBody` -pub fn empty_body() -> BoxBody { - http_body_util::Empty::new() - .map_err(|err| match err {}) - .boxed_unsync() +impl Body { + fn from_kind(kind: Kind) -> Self { + Self { kind } + } + + /// Create a new empty `Body`. + pub const fn empty() -> Self { + Self { kind: Kind::Empty } + } + + /// Create a new `Body` from an existing `Body`. + pub fn new(body: B) -> Self + where + B: http_body::Body + Send + 'static, + B::Error: Into, + { + if body.is_end_stream() { + return Self::empty(); + } + + let mut body = Some(body); + + if let Some(body) = ::downcast_mut::>(&mut body) { + return body.take().unwrap(); + } + + if let Some(body) = ::downcast_mut::>(&mut body) { + return Self::from_kind(Kind::Wrap(body.take().unwrap())); + } + + let body = body + .unwrap() + .map_err(crate::Status::map_error) + .boxed_unsync(); + + Self::from_kind(Kind::Wrap(body)) + } +} + +impl Default for Body { + fn default() -> Self { + Self::empty() + } +} + +impl http_body::Body for Body { + type Data = bytes::Bytes; + type Error = crate::Status; + + fn poll_frame( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll, Self::Error>>> { + match &mut self.kind { + Kind::Empty => Poll::Ready(None), + Kind::Wrap(body) => Pin::new(body).poll_frame(cx), + } + } + + fn size_hint(&self) -> http_body::SizeHint { + match &self.kind { + Kind::Empty => http_body::SizeHint::with_exact(0), + Kind::Wrap(body) => body.size_hint(), + } + } + + fn is_end_stream(&self) -> bool { + match &self.kind { + Kind::Empty => true, + Kind::Wrap(body) => body.is_end_stream(), + } + } } diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/client/grpc.rs rust-tonic-0.13.0+dfsg/tonic/src/client/grpc.rs --- rust-tonic-0.12.3+dfsg/tonic/src/client/grpc.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/client/grpc.rs 2025-03-25 18:49:19.000000000 +0000 @@ -2,7 +2,7 @@ use crate::codec::EncodeBody; use crate::metadata::GRPC_CONTENT_TYPE; use crate::{ - body::BoxBody, + body::Body, client::GrpcService, codec::{Codec, Decoder, Streaming}, request::SanitizeHeaders, @@ -12,7 +12,7 @@ header::{HeaderValue, CONTENT_TYPE, TE}, uri::{PathAndQuery, Uri}, }; -use http_body::Body; +use http_body::Body as HttpBody; use std::{fmt, future, pin::pin}; use tokio_stream::{Stream, StreamExt}; @@ -198,7 +198,7 @@ /// accept one more request. pub async fn ready(&mut self) -> Result<(), T::Error> where - T: GrpcService, + T: GrpcService, { future::poll_fn(|cx| self.inner.poll_ready(cx)).await } @@ -211,9 +211,9 @@ codec: C, ) -> Result, Status> where - T: GrpcService, - T::ResponseBody: Body + Send + 'static, - ::Error: Into, + T: GrpcService, + T::ResponseBody: HttpBody + Send + 'static, + ::Error: Into, C: Codec, M1: Send + Sync + 'static, M2: Send + Sync + 'static, @@ -230,9 +230,9 @@ codec: C, ) -> Result, Status> where - T: GrpcService, - T::ResponseBody: Body + Send + 'static, - ::Error: Into, + T: GrpcService, + T::ResponseBody: HttpBody + Send + 'static, + ::Error: Into, S: Stream + Send + 'static, C: Codec, M1: Send + Sync + 'static, @@ -267,9 +267,9 @@ codec: C, ) -> Result>, Status> where - T: GrpcService, - T::ResponseBody: Body + Send + 'static, - ::Error: Into, + T: GrpcService, + T::ResponseBody: HttpBody + Send + 'static, + ::Error: Into, C: Codec, M1: Send + Sync + 'static, M2: Send + Sync + 'static, @@ -286,9 +286,9 @@ mut codec: C, ) -> Result>, Status> where - T: GrpcService, - T::ResponseBody: Body + Send + 'static, - ::Error: Into, + T: GrpcService, + T::ResponseBody: HttpBody + Send + 'static, + ::Error: Into, S: Stream + Send + 'static, C: Codec, M1: Send + Sync + 'static, @@ -303,7 +303,7 @@ self.config.max_encoding_message_size, ) }) - .map(BoxBody::new); + .map(Body::new); let request = self.config.prepare_request(request, path); @@ -326,9 +326,9 @@ response: http::Response, ) -> Result>, Status> where - T: GrpcService, - T::ResponseBody: Body + Send + 'static, - ::Error: Into, + T: GrpcService, + T::ResponseBody: HttpBody + Send + 'static, + ::Error: Into, { let encoding = CompressionEncoding::from_encoding_header( response.headers(), @@ -369,11 +369,7 @@ } impl GrpcConfig { - fn prepare_request( - &self, - request: Request, - path: PathAndQuery, - ) -> http::Request { + fn prepare_request(&self, request: Request, path: PathAndQuery) -> http::Request { let mut parts = self.origin.clone().into_parts(); match &parts.path_and_query { @@ -408,7 +404,7 @@ .headers_mut() .insert(CONTENT_TYPE, GRPC_CONTENT_TYPE); - #[cfg(any(feature = "gzip", feature = "zstd"))] + #[cfg(any(feature = "gzip", feature = "deflate", feature = "zstd"))] if let Some(encoding) = self.send_compression_encodings { request.headers_mut().insert( crate::codec::compression::ENCODING_HEADER, @@ -447,32 +443,25 @@ impl fmt::Debug for Grpc { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut f = f.debug_struct("Grpc"); - - f.field("inner", &self.inner); - - f.field("origin", &self.config.origin); - - f.field( - "compression_encoding", - &self.config.send_compression_encodings, - ); - - f.field( - "accept_compression_encodings", - &self.config.accept_compression_encodings, - ); - - f.field( - "max_decoding_message_size", - &self.config.max_decoding_message_size, - ); - - f.field( - "max_encoding_message_size", - &self.config.max_encoding_message_size, - ); - - f.finish() + f.debug_struct("Grpc") + .field("inner", &self.inner) + .field("origin", &self.config.origin) + .field( + "compression_encoding", + &self.config.send_compression_encodings, + ) + .field( + "accept_compression_encodings", + &self.config.accept_compression_encodings, + ) + .field( + "max_decoding_message_size", + &self.config.max_decoding_message_size, + ) + .field( + "max_encoding_message_size", + &self.config.max_encoding_message_size, + ) + .finish() } } diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/client/service.rs rust-tonic-0.13.0+dfsg/tonic/src/client/service.rs --- rust-tonic-0.12.3+dfsg/tonic/src/client/service.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/client/service.rs 2025-03-25 18:49:19.000000000 +0000 @@ -14,7 +14,7 @@ /// Responses body given by the service. type ResponseBody: Body; /// Errors produced by the service. - type Error: Into; + type Error: Into; /// The future response value. type Future: Future, Self::Error>>; @@ -32,9 +32,9 @@ impl GrpcService for T where T: Service, Response = http::Response>, - T::Error: Into, + T::Error: Into, ResBody: Body, - ::Error: Into, + ::Error: Into, { type ResponseBody = ResBody; type Error = T::Error; diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/codec/compression.rs rust-tonic-0.13.0+dfsg/tonic/src/codec/compression.rs --- rust-tonic-0.12.3+dfsg/tonic/src/codec/compression.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/codec/compression.rs 2025-03-25 18:49:19.000000000 +0000 @@ -2,6 +2,8 @@ use bytes::{Buf, BufMut, BytesMut}; #[cfg(feature = "gzip")] use flate2::read::{GzDecoder, GzEncoder}; +#[cfg(feature = "deflate")] +use flate2::read::{ZlibDecoder, ZlibEncoder}; use std::fmt; #[cfg(feature = "zstd")] use zstd::stream::read::{Decoder, Encoder}; @@ -14,7 +16,7 @@ /// Represents an ordered list of compression encodings that are enabled. #[derive(Debug, Default, Clone, Copy)] pub struct EnabledCompressionEncodings { - inner: [Option; 2], + inner: [Option; 3], } impl EnabledCompressionEncodings { @@ -85,6 +87,9 @@ #[cfg(feature = "gzip")] Gzip, #[allow(missing_docs)] + #[cfg(feature = "deflate")] + Deflate, + #[allow(missing_docs)] #[cfg(feature = "zstd")] Zstd, } @@ -93,6 +98,8 @@ pub(crate) const ENCODINGS: &'static [CompressionEncoding] = &[ #[cfg(feature = "gzip")] CompressionEncoding::Gzip, + #[cfg(feature = "deflate")] + CompressionEncoding::Deflate, #[cfg(feature = "zstd")] CompressionEncoding::Zstd, ]; @@ -112,6 +119,8 @@ split_by_comma(header_value_str).find_map(|value| match value { #[cfg(feature = "gzip")] "gzip" => Some(CompressionEncoding::Gzip), + #[cfg(feature = "deflate")] + "deflate" => Some(CompressionEncoding::Deflate), #[cfg(feature = "zstd")] "zstd" => Some(CompressionEncoding::Zstd), _ => None, @@ -132,6 +141,10 @@ b"gzip" if enabled_encodings.is_enabled(CompressionEncoding::Gzip) => { Ok(Some(CompressionEncoding::Gzip)) } + #[cfg(feature = "deflate")] + b"deflate" if enabled_encodings.is_enabled(CompressionEncoding::Deflate) => { + Ok(Some(CompressionEncoding::Deflate)) + } #[cfg(feature = "zstd")] b"zstd" if enabled_encodings.is_enabled(CompressionEncoding::Zstd) => { Ok(Some(CompressionEncoding::Zstd)) @@ -170,12 +183,14 @@ match self { #[cfg(feature = "gzip")] CompressionEncoding::Gzip => "gzip", + #[cfg(feature = "deflate")] + CompressionEncoding::Deflate => "deflate", #[cfg(feature = "zstd")] CompressionEncoding::Zstd => "zstd", } } - #[cfg(any(feature = "gzip", feature = "zstd"))] + #[cfg(any(feature = "gzip", feature = "deflate", feature = "zstd"))] pub(crate) fn into_header_value(self) -> http::HeaderValue { http::HeaderValue::from_static(self.as_str()) } @@ -204,7 +219,7 @@ let capacity = ((len / buffer_growth_interval) + 1) * buffer_growth_interval; out_buf.reserve(capacity); - #[cfg(any(feature = "gzip", feature = "zstd"))] + #[cfg(any(feature = "gzip", feature = "deflate", feature = "zstd"))] let mut out_writer = out_buf.writer(); match settings.encoding { @@ -217,6 +232,15 @@ ); std::io::copy(&mut gzip_encoder, &mut out_writer)?; } + #[cfg(feature = "deflate")] + CompressionEncoding::Deflate => { + let mut deflate_encoder = ZlibEncoder::new( + &decompressed_buf[0..len], + // FIXME: support customizing the compression level + flate2::Compression::new(6), + ); + std::io::copy(&mut deflate_encoder, &mut out_writer)?; + } #[cfg(feature = "zstd")] CompressionEncoding::Zstd => { let mut zstd_encoder = Encoder::new( @@ -247,7 +271,7 @@ ((estimate_decompressed_len / buffer_growth_interval) + 1) * buffer_growth_interval; out_buf.reserve(capacity); - #[cfg(any(feature = "gzip", feature = "zstd"))] + #[cfg(any(feature = "gzip", feature = "deflate", feature = "zstd"))] let mut out_writer = out_buf.writer(); match settings.encoding { @@ -256,6 +280,11 @@ let mut gzip_decoder = GzDecoder::new(&compressed_buf[0..len]); std::io::copy(&mut gzip_decoder, &mut out_writer)?; } + #[cfg(feature = "deflate")] + CompressionEncoding::Deflate => { + let mut deflate_decoder = ZlibDecoder::new(&compressed_buf[0..len]); + std::io::copy(&mut deflate_decoder, &mut out_writer)?; + } #[cfg(feature = "zstd")] CompressionEncoding::Zstd => { let mut zstd_decoder = Decoder::new(&compressed_buf[0..len])?; @@ -282,6 +311,7 @@ #[cfg(test)] mod tests { + #[cfg(any(feature = "gzip", feature = "deflate", feature = "zstd"))] use http::HeaderValue; use super::*; @@ -299,13 +329,13 @@ const GZIP: HeaderValue = HeaderValue::from_static("gzip,identity"); let encodings = EnabledCompressionEncodings { - inner: [Some(CompressionEncoding::Gzip), None], + inner: [Some(CompressionEncoding::Gzip), None, None], }; assert_eq!(encodings.into_accept_encoding_header_value().unwrap(), GZIP); let encodings = EnabledCompressionEncodings { - inner: [None, Some(CompressionEncoding::Gzip)], + inner: [None, None, Some(CompressionEncoding::Gzip)], }; assert_eq!(encodings.into_accept_encoding_header_value().unwrap(), GZIP); @@ -317,43 +347,45 @@ const ZSTD: HeaderValue = HeaderValue::from_static("zstd,identity"); let encodings = EnabledCompressionEncodings { - inner: [Some(CompressionEncoding::Zstd), None], + inner: [Some(CompressionEncoding::Zstd), None, None], }; assert_eq!(encodings.into_accept_encoding_header_value().unwrap(), ZSTD); let encodings = EnabledCompressionEncodings { - inner: [None, Some(CompressionEncoding::Zstd)], + inner: [None, None, Some(CompressionEncoding::Zstd)], }; assert_eq!(encodings.into_accept_encoding_header_value().unwrap(), ZSTD); } #[test] - #[cfg(all(feature = "gzip", feature = "zstd"))] - fn convert_gzip_and_zstd_into_header_value() { + #[cfg(all(feature = "gzip", feature = "deflate", feature = "zstd"))] + fn convert_compression_encodings_into_header_value() { let encodings = EnabledCompressionEncodings { inner: [ Some(CompressionEncoding::Gzip), + Some(CompressionEncoding::Deflate), Some(CompressionEncoding::Zstd), ], }; assert_eq!( encodings.into_accept_encoding_header_value().unwrap(), - HeaderValue::from_static("gzip,zstd,identity"), + HeaderValue::from_static("gzip,deflate,zstd,identity"), ); let encodings = EnabledCompressionEncodings { inner: [ Some(CompressionEncoding::Zstd), + Some(CompressionEncoding::Deflate), Some(CompressionEncoding::Gzip), ], }; assert_eq!( encodings.into_accept_encoding_header_value().unwrap(), - HeaderValue::from_static("zstd,gzip,identity"), + HeaderValue::from_static("zstd,deflate,gzip,identity"), ); } } diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/codec/decode.rs rust-tonic-0.13.0+dfsg/tonic/src/codec/decode.rs --- rust-tonic-0.12.3+dfsg/tonic/src/codec/decode.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/codec/decode.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,9 +1,9 @@ use super::compression::{decompress, CompressionEncoding, CompressionSettings}; use super::{BufferSettings, DecodeBuf, Decoder, DEFAULT_MAX_RECV_MESSAGE_SIZE, HEADER_SIZE}; -use crate::{body::BoxBody, metadata::MetadataMap, Code, Status}; +use crate::{body::Body, metadata::MetadataMap, Code, Status}; use bytes::{Buf, BufMut, BytesMut}; use http::{HeaderMap, StatusCode}; -use http_body::Body; +use http_body::Body as HttpBody; use http_body_util::BodyExt; use std::{ fmt, future, @@ -24,7 +24,7 @@ } struct StreamingInner { - body: BoxBody, + body: Body, state: State, direction: Direction, buf: BytesMut, @@ -64,8 +64,8 @@ max_message_size: Option, ) -> Self where - B: Body + Send + 'static, - B::Error: Into, + B: HttpBody + Send + 'static, + B::Error: Into, D: Decoder + Send + 'static, { Self::new( @@ -80,8 +80,8 @@ /// Create empty response. For creating responses that have no content (headers + trailers only) pub fn new_empty(decoder: D, body: B) -> Self where - B: Body + Send + 'static, - B::Error: Into, + B: HttpBody + Send + 'static, + B::Error: Into, D: Decoder + Send + 'static, { Self::new(decoder, body, Direction::EmptyResponse, None, None) @@ -96,8 +96,8 @@ max_message_size: Option, ) -> Self where - B: Body + Send + 'static, - B::Error: Into, + B: HttpBody + Send + 'static, + B::Error: Into, D: Decoder + Send + 'static, { Self::new( @@ -117,18 +117,20 @@ max_message_size: Option, ) -> Self where - B: Body + Send + 'static, - B::Error: Into, + B: HttpBody + Send + 'static, + B::Error: Into, D: Decoder + Send + 'static, { let buffer_size = decoder.buffer_settings().buffer_size; Self { decoder: Box::new(decoder), inner: StreamingInner { - body: body - .map_frame(|frame| frame.map_data(|mut buf| buf.copy_to_bytes(buf.remaining()))) - .map_err(|err| Status::map_error(err.into())) - .boxed_unsync(), + body: Body::new( + body.map_frame(|frame| { + frame.map_data(|mut buf| buf.copy_to_bytes(buf.remaining())) + }) + .map_err(|err| Status::map_error(err.into())), + ), state: State::ReadHeader, direction, buf: BytesMut::with_capacity(buffer_size), @@ -244,8 +246,8 @@ // Returns Some(()) if data was found or None if the loop in `poll_next` should break fn poll_frame(&mut self, cx: &mut Context<'_>) -> Poll, Status>> { - let chunk = match ready!(Pin::new(&mut self.body).poll_frame(cx)) { - Some(Ok(d)) => Some(d), + let frame = match ready!(Pin::new(&mut self.body).poll_frame(cx)) { + Some(Ok(frame)) => frame, Some(Err(status)) => { if self.direction == Direction::Request && status.code() == Code::Cancelled { return Poll::Ready(Ok(None)); @@ -255,37 +257,30 @@ debug!("decoder inner stream error: {:?}", status); return Poll::Ready(Err(status)); } - None => None, - }; - - Poll::Ready(if let Some(frame) = chunk { - match frame { - frame if frame.is_data() => { - self.buf.put(frame.into_data().unwrap()); - Ok(Some(())) - } - frame if frame.is_trailers() => { - match &mut self.trailers { - Some(trailers) => { - trailers.extend(frame.into_trailers().unwrap()); - } - None => { - self.trailers = Some(frame.into_trailers().unwrap()); - } - } - + None => { + // FIXME: improve buf usage. + return Poll::Ready(if self.buf.has_remaining() { + trace!("unexpected EOF decoding stream, state: {:?}", self.state); + Err(Status::internal("Unexpected EOF decoding stream.")) + } else { Ok(None) - } - frame => panic!("unexpected frame: {:?}", frame), + }); } - } else { - // FIXME: improve buf usage. - if self.buf.has_remaining() { - trace!("unexpected EOF decoding stream, state: {:?}", self.state); - Err(Status::internal("Unexpected EOF decoding stream.")) + }; + + Poll::Ready(if frame.is_data() { + self.buf.put(frame.into_data().unwrap()); + Ok(Some(())) + } else if frame.is_trailers() { + if let Some(trailers) = &mut self.trailers { + trailers.extend(frame.into_trailers().unwrap()); } else { - Ok(None) + self.trailers = Some(frame.into_trailers().unwrap()); } + + Ok(None) + } else { + panic!("unexpected frame: {:?}", frame); }) } @@ -404,16 +399,13 @@ return Poll::Ready(Some(Ok(item))); } - match ready!(self.inner.poll_frame(cx))? { - Some(()) => (), - None => break, + if ready!(self.inner.poll_frame(cx))?.is_none() { + match self.inner.response() { + Ok(()) => return Poll::Ready(None), + Err(err) => self.inner.state = State::Error(Some(err)), + } } } - - Poll::Ready(match self.inner.response() { - Ok(()) => None, - Err(err) => Some(Err(err)), - }) } } diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/codegen.rs rust-tonic-0.13.0+dfsg/tonic/src/codegen.rs --- rust-tonic-0.12.3+dfsg/tonic/src/codegen.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/codegen.rs 2025-03-25 18:49:19.000000000 +0000 @@ -19,5 +19,3 @@ pub type BoxFuture = self::Pin> + Send + 'static>>; pub type BoxStream = self::Pin> + Send + 'static>>; - -pub use crate::body::empty_body; diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/lib.rs rust-tonic-0.13.0+dfsg/tonic/src/lib.rs --- rust-tonic-0.12.3+dfsg/tonic/src/lib.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/lib.rs 2025-03-25 18:49:19.000000000 +0000 @@ -24,9 +24,10 @@ //! - `router`: Enables the [`axum`] based service router. Enabled by default. //! - `codegen`: Enables all the required exports and optional dependencies required //! for [`tonic-build`]. Enabled by default. -//! - `tls`: Enables the [`rustls`] based TLS options for the `transport` feature. Not -//! enabled by default. -//! - `tls-roots`: Deprecated. An alias to `tls-native-roots` feature. +//! - `tls-ring`: Enables the [`rustls`] based TLS options for the `transport` feature using +//! the [`ring`] libcrypto provider. Not enabled by default. +//! - `tls-aws-lc`: Enables the [`rustls`] based TLS options for the `transport` feature using +//! the [`aws-lc-rs`] libcrypto provider. Not enabled by default. //! - `tls-native-roots`: Adds system trust roots to [`rustls`]-based gRPC clients using the //! [`rustls-native-certs`] crate. Not enabled by default. //! - `tls-webpki-roots`: Add the standard trust roots from the [`webpki-roots`] crate to @@ -34,6 +35,8 @@ //! - `prost`: Enables the [`prost`] based gRPC [`Codec`] implementation. Enabled by default. //! - `gzip`: Enables compressing requests, responses, and streams. Depends on [`flate2`]. //! Not enabled by default. +//! - `deflate`: Enables compressing requests, responses, and streams. Depends on [`flate2`]. +//! Not enabled by default. //! - `zstd`: Enables compressing requests, responses, and streams. Depends on [`zstd`]. //! Not enabled by default. //! @@ -71,6 +74,7 @@ //! [`hyper`]: https://docs.rs/hyper //! [`tower`]: https://docs.rs/tower //! [`tonic-build`]: https://docs.rs/tonic-build +//! [`ring`]: https://docs.rs/ring //! [`tonic-examples`]: https://github.com/hyperium/tonic/tree/master/examples //! [`Codec`]: codec/trait.Codec.html //! [`Channel`]: transport/struct.Channel.html @@ -84,17 +88,9 @@ //! [`zstd`]: https://docs.rs/zstd #![recursion_limit = "256"] -#![warn( - missing_debug_implementations, - missing_docs, - rust_2018_idioms, - unreachable_pub -)] -#![deny(rustdoc::broken_intra_doc_links)] #![doc( html_logo_url = "https://raw.githubusercontent.com/tokio-rs/website/master/public/img/icons/tonic.svg" )] -#![doc(html_root_url = "https://docs.rs/tonic/0.12.3")] #![doc(issue_tracker_base_url = "https://github.com/hyperium/tonic/issues/")] #![doc(test(no_crate_inject, attr(deny(rust_2018_idioms))))] #![cfg_attr(docsrs, feature(doc_auto_cfg))] @@ -128,7 +124,7 @@ pub use response::Response; pub use status::{Code, ConnectError, Status, TimeoutExpired}; -pub(crate) type Error = Box; +pub(crate) type BoxError = Box; #[doc(hidden)] #[cfg(feature = "codegen")] diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/metadata/key.rs rust-tonic-0.13.0+dfsg/tonic/src/metadata/key.rs --- rust-tonic-0.12.3+dfsg/tonic/src/metadata/key.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/metadata/key.rs 2025-03-25 18:49:19.000000000 +0000 @@ -204,7 +204,7 @@ } } -impl<'a, VE: ValueEncoding> PartialEq> for &'a MetadataKey { +impl PartialEq> for &MetadataKey { #[inline] fn eq(&self, other: &MetadataKey) -> bool { *other == *self @@ -260,7 +260,7 @@ } } -impl<'a, VE: ValueEncoding> PartialEq> for &'a str { +impl PartialEq> for &str { /// Performs a case-insensitive comparison of the string against the header /// name #[inline] diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/metadata/map.rs rust-tonic-0.13.0+dfsg/tonic/src/metadata/map.rs --- rust-tonic-0.12.3+dfsg/tonic/src/metadata/map.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/metadata/map.rs 2025-03-25 18:49:19.000000000 +0000 @@ -38,6 +38,18 @@ headers: http::HeaderMap, } +impl AsRef for MetadataMap { + fn as_ref(&self) -> &http::HeaderMap { + &self.headers + } +} + +impl AsMut for MetadataMap { + fn as_mut(&mut self) -> &mut http::HeaderMap { + &mut self.headers + } +} + /// `MetadataMap` entry iterator. /// /// Yields `KeyAndValueRef` values. The same header name may be yielded @@ -1274,7 +1286,7 @@ // ===== impl ValueDrain ===== -impl<'a, VE: ValueEncoding> Iterator for ValueDrain<'a, VE> { +impl Iterator for ValueDrain<'_, VE> { type Item = MetadataValue; fn next(&mut self) -> Option { @@ -1308,7 +1320,7 @@ } } -impl<'a> ExactSizeIterator for Keys<'a> {} +impl ExactSizeIterator for Keys<'_> {} // ===== impl Values ==== @@ -1967,7 +1979,7 @@ } } -impl<'a, VE: ValueEncoding> PartialEq for GetAll<'a, VE> { +impl PartialEq for GetAll<'_, VE> { fn eq(&self, other: &Self) -> bool { self.inner.iter().eq(other.inner.iter()) } @@ -2051,7 +2063,7 @@ impl IntoMetadataKey for MetadataKey {} - impl<'a, VE: ValueEncoding> Sealed for &'a MetadataKey { + impl Sealed for &MetadataKey { #[doc(hidden)] #[inline] fn insert( @@ -2070,7 +2082,7 @@ } } - impl<'a, VE: ValueEncoding> IntoMetadataKey for &'a MetadataKey {} + impl IntoMetadataKey for &MetadataKey {} impl Sealed for &'static str { #[doc(hidden)] @@ -2180,7 +2192,7 @@ impl AsMetadataKey for MetadataKey {} - impl<'a, VE: ValueEncoding> Sealed for &'a MetadataKey { + impl Sealed for &MetadataKey { #[doc(hidden)] #[inline] fn get(self, map: &MetadataMap) -> Option<&MetadataValue> { @@ -2221,9 +2233,9 @@ } } - impl<'a, VE: ValueEncoding> AsMetadataKey for &'a MetadataKey {} + impl AsMetadataKey for &MetadataKey {} - impl<'a, VE: ValueEncoding> Sealed for &'a str { + impl Sealed for &str { #[doc(hidden)] #[inline] fn get(self, map: &MetadataMap) -> Option<&MetadataValue> { @@ -2283,7 +2295,7 @@ } } - impl<'a, VE: ValueEncoding> AsMetadataKey for &'a str {} + impl AsMetadataKey for &str {} impl Sealed for String { #[doc(hidden)] @@ -2346,7 +2358,7 @@ impl AsMetadataKey for String {} - impl<'a, VE: ValueEncoding> Sealed for &'a String { + impl Sealed for &String { #[doc(hidden)] #[inline] fn get(self, map: &MetadataMap) -> Option<&MetadataValue> { @@ -2405,7 +2417,7 @@ } } - impl<'a, VE: ValueEncoding> AsMetadataKey for &'a String {} + impl AsMetadataKey for &String {} } mod as_encoding_agnostic_metadata_key { @@ -2442,7 +2454,7 @@ impl AsEncodingAgnosticMetadataKey for MetadataKey {} - impl<'a, VE: ValueEncoding> Sealed for &'a MetadataKey { + impl Sealed for &MetadataKey { #[doc(hidden)] #[inline] fn contains_key(&self, map: &MetadataMap) -> bool { @@ -2450,9 +2462,9 @@ } } - impl<'a, VE: ValueEncoding> AsEncodingAgnosticMetadataKey for &'a MetadataKey {} + impl AsEncodingAgnosticMetadataKey for &MetadataKey {} - impl<'a> Sealed for &'a str { + impl Sealed for &str { #[doc(hidden)] #[inline] fn contains_key(&self, map: &MetadataMap) -> bool { @@ -2460,7 +2472,7 @@ } } - impl<'a> AsEncodingAgnosticMetadataKey for &'a str {} + impl AsEncodingAgnosticMetadataKey for &str {} impl Sealed for String { #[doc(hidden)] @@ -2472,7 +2484,7 @@ impl AsEncodingAgnosticMetadataKey for String {} - impl<'a> Sealed for &'a String { + impl Sealed for &String { #[doc(hidden)] #[inline] fn contains_key(&self, map: &MetadataMap) -> bool { @@ -2480,7 +2492,7 @@ } } - impl<'a> AsEncodingAgnosticMetadataKey for &'a String {} + impl AsEncodingAgnosticMetadataKey for &String {} } #[cfg(test)] @@ -2500,7 +2512,7 @@ #[test] fn test_to_headers_encoding() { use crate::Status; - let special_char_message = "Beyond ascii \t\n\r🌶️💉💧🐮🍺"; + let special_char_message = "Beyond 100% ascii \t\n\r🌶️💉💧🐮🍺"; let s1 = Status::unknown(special_char_message); assert_eq!(s1.message(), special_char_message); @@ -2509,6 +2521,17 @@ let s2 = Status::from_header_map(&s1_map).unwrap(); assert_eq!(s1.message(), s2.message()); + + assert!( + s1_map + .get("grpc-message") + .unwrap() + .to_str() + .unwrap() + .starts_with("Beyond%20100%25%20ascii"), + "Percent sign or other character isn't encoded as desired: {:?}", + s1_map.get("grpc-message") + ); } #[test] diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/metadata/value.rs rust-tonic-0.13.0+dfsg/tonic/src/metadata/value.rs --- rust-tonic-0.12.3+dfsg/tonic/src/metadata/value.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/metadata/value.rs 2025-03-25 18:49:19.000000000 +0000 @@ -248,7 +248,7 @@ /// let val = AsciiMetadataValue::try_from(b"\n"); /// assert!(val.is_err()); /// ``` -impl<'a, VE: ValueEncoding> TryFrom<&'a [u8]> for MetadataValue { +impl TryFrom<&[u8]> for MetadataValue { type Error = InvalidMetadataValueBytes; #[inline] @@ -284,7 +284,7 @@ /// let val = AsciiMetadataValue::try_from(b"\n"); /// assert!(val.is_err()); /// ``` -impl<'a, VE: ValueEncoding, const N: usize> TryFrom<&'a [u8; N]> for MetadataValue { +impl TryFrom<&[u8; N]> for MetadataValue { type Error = InvalidMetadataValueBytes; #[inline] @@ -737,14 +737,14 @@ } } -impl<'a, VE: ValueEncoding> PartialEq> for &'a MetadataValue { +impl PartialEq> for &MetadataValue { #[inline] fn eq(&self, other: &MetadataValue) -> bool { **self == *other } } -impl<'a, VE: ValueEncoding> PartialOrd> for &'a MetadataValue { +impl PartialOrd> for &MetadataValue { #[inline] fn partial_cmp(&self, other: &MetadataValue) -> Option { (**self).partial_cmp(other) @@ -771,14 +771,14 @@ } } -impl<'a, VE: ValueEncoding> PartialEq> for &'a str { +impl PartialEq> for &str { #[inline] fn eq(&self, other: &MetadataValue) -> bool { *other == *self } } -impl<'a, VE: ValueEncoding> PartialOrd> for &'a str { +impl PartialOrd> for &str { #[inline] fn partial_cmp(&self, other: &MetadataValue) -> Option { self.as_bytes().partial_cmp(other.inner.as_bytes()) diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/request.rs rust-tonic-0.13.0+dfsg/tonic/src/request.rs --- rust-tonic-0.12.3+dfsg/tonic/src/request.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/request.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,15 +1,15 @@ use crate::metadata::{MetadataMap, MetadataValue}; #[cfg(feature = "server")] use crate::transport::server::TcpConnectInfo; -#[cfg(all(feature = "server", feature = "tls"))] +#[cfg(all(feature = "server", feature = "_tls-any"))] use crate::transport::server::TlsConnectInfo; use http::Extensions; #[cfg(feature = "server")] use std::net::SocketAddr; -#[cfg(all(feature = "server", feature = "tls"))] +#[cfg(all(feature = "server", feature = "_tls-any"))] use std::sync::Arc; use std::time::Duration; -#[cfg(all(feature = "server", feature = "tls"))] +#[cfg(all(feature = "server", feature = "_tls-any"))] use tokio_rustls::rustls::pki_types::CertificateDer; use tokio_stream::Stream; @@ -218,7 +218,7 @@ .get::() .and_then(|i| i.local_addr()); - #[cfg(feature = "tls")] + #[cfg(feature = "_tls-any")] let addr = addr.or_else(|| { self.extensions() .get::>() @@ -240,7 +240,7 @@ .get::() .and_then(|i| i.remote_addr()); - #[cfg(feature = "tls")] + #[cfg(feature = "_tls-any")] let addr = addr.or_else(|| { self.extensions() .get::>() @@ -256,7 +256,7 @@ /// and is mostly used for mTLS. This currently only returns /// `Some` on the server side of the `transport` server with /// TLS enabled connections. - #[cfg(all(feature = "server", feature = "tls"))] + #[cfg(all(feature = "server", feature = "_tls-any"))] pub fn peer_certs(&self) -> Option>>> { self.extensions() .get::>() @@ -308,20 +308,20 @@ /// Extensions can be set in interceptors: /// /// ```no_run - /// use tonic::{Request, service::interceptor}; + /// use tonic::{Request, Status}; /// /// #[derive(Clone)] // Extensions must be Clone /// struct MyExtension { /// some_piece_of_data: String, /// } /// - /// interceptor(|mut request: Request<()>| { + /// fn intercept(mut request: Request<()>) -> Result, Status> { /// request.extensions_mut().insert(MyExtension { /// some_piece_of_data: "foo".to_string(), /// }); /// /// Ok(request) - /// }); + /// } /// ``` /// /// And picked up by RPCs: diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/response.rs rust-tonic-0.13.0+dfsg/tonic/src/response.rs --- rust-tonic-0.12.3+dfsg/tonic/src/response.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/response.rs 2025-03-25 18:49:19.000000000 +0000 @@ -120,13 +120,19 @@ /// **Note**: This only has effect on responses to unary requests and responses to client to /// server streams. Response streams (server to client stream and bidirectional streams) will /// still be compressed according to the configuration of the server. - #[cfg(feature = "gzip")] + #[cfg(any(feature = "gzip", feature = "deflate", feature = "zstd"))] pub fn disable_compression(&mut self) { self.extensions_mut() .insert(crate::codec::compression::SingleMessageCompressionOverride::Disable); } } +impl From for Response { + fn from(inner: T) -> Self { + Response::new(inner) + } +} + #[cfg(test)] mod tests { use super::*; diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/server/grpc.rs rust-tonic-0.13.0+dfsg/tonic/src/server/grpc.rs --- rust-tonic-0.12.3+dfsg/tonic/src/server/grpc.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/server/grpc.rs 2025-03-25 18:49:19.000000000 +0000 @@ -4,12 +4,12 @@ use crate::codec::EncodeBody; use crate::metadata::GRPC_CONTENT_TYPE; use crate::{ - body::BoxBody, + body::Body, codec::{Codec, Streaming}, server::{ClientStreamingService, ServerStreamingService, StreamingService, UnaryService}, Request, Status, }; -use http_body::Body; +use http_body::Body as HttpBody; use std::{fmt, pin::pin}; use tokio_stream::{Stream, StreamExt}; @@ -183,40 +183,36 @@ #[doc(hidden)] pub fn apply_compression_config( - self, + mut self, accept_encodings: EnabledCompressionEncodings, send_encodings: EnabledCompressionEncodings, ) -> Self { - let mut this = self; - for &encoding in CompressionEncoding::ENCODINGS { if accept_encodings.is_enabled(encoding) { - this = this.accept_compressed(encoding); + self = self.accept_compressed(encoding); } if send_encodings.is_enabled(encoding) { - this = this.send_compressed(encoding); + self = self.send_compressed(encoding); } } - this + self } #[doc(hidden)] pub fn apply_max_message_size_config( - self, + mut self, max_decoding_message_size: Option, max_encoding_message_size: Option, ) -> Self { - let mut this = self; - if let Some(limit) = max_decoding_message_size { - this = this.max_decoding_message_size(limit); + self = self.max_decoding_message_size(limit); } if let Some(limit) = max_encoding_message_size { - this = this.max_encoding_message_size(limit); + self = self.max_encoding_message_size(limit); } - this + self } /// Handle a single unary gRPC request. @@ -224,11 +220,11 @@ &mut self, mut service: S, req: http::Request, - ) -> http::Response + ) -> http::Response where S: UnaryService, - B: Body + Send + 'static, - B::Error: Into + Send, + B: HttpBody + Send + 'static, + B::Error: Into + Send, { let accept_encoding = CompressionEncoding::from_accept_encoding_header( req.headers(), @@ -267,12 +263,12 @@ &mut self, mut service: S, req: http::Request, - ) -> http::Response + ) -> http::Response where S: ServerStreamingService, S::ResponseStream: Send + 'static, - B: Body + Send + 'static, - B::Error: Into + Send, + B: HttpBody + Send + 'static, + B::Error: Into + Send, { let accept_encoding = CompressionEncoding::from_accept_encoding_header( req.headers(), @@ -308,11 +304,11 @@ &mut self, mut service: S, req: http::Request, - ) -> http::Response + ) -> http::Response where S: ClientStreamingService, - B: Body + Send + 'static, - B::Error: Into + Send + 'static, + B: HttpBody + Send + 'static, + B::Error: Into + Send + 'static, { let accept_encoding = CompressionEncoding::from_accept_encoding_header( req.headers(), @@ -341,12 +337,12 @@ &mut self, mut service: S, req: http::Request, - ) -> http::Response + ) -> http::Response where S: StreamingService + Send, S::ResponseStream: Send + 'static, - B: Body + Send + 'static, - B::Error: Into + Send, + B: HttpBody + Send + 'static, + B::Error: Into + Send, { let accept_encoding = CompressionEncoding::from_accept_encoding_header( req.headers(), @@ -370,8 +366,8 @@ request: http::Request, ) -> Result, Status> where - B: Body + Send + 'static, - B::Error: Into + Send, + B: HttpBody + Send + 'static, + B::Error: Into + Send, { let request_compression_encoding = self.request_encoding_if_supported(&request)?; @@ -403,8 +399,8 @@ request: http::Request, ) -> Result>, Status> where - B: Body + Send + 'static, - B::Error: Into + Send, + B: HttpBody + Send + 'static, + B::Error: Into + Send, { let encoding = self.request_encoding_if_supported(&request)?; @@ -426,7 +422,7 @@ accept_encoding: Option, compression_override: SingleMessageCompressionOverride, max_message_size: Option, - ) -> http::Response + ) -> http::Response where B: Stream> + Send + 'static, { @@ -439,7 +435,7 @@ .headers .insert(http::header::CONTENT_TYPE, GRPC_CONTENT_TYPE); - #[cfg(any(feature = "gzip", feature = "zstd"))] + #[cfg(any(feature = "gzip", feature = "deflate", feature = "zstd"))] if let Some(encoding) = accept_encoding { // Set the content encoding parts.headers.insert( @@ -456,7 +452,7 @@ max_message_size, ); - http::Response::from_parts(parts, BoxBody::new(body)) + http::Response::from_parts(parts, Body::new(body)) } fn request_encoding_if_supported( @@ -472,21 +468,17 @@ impl fmt::Debug for Grpc { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut f = f.debug_struct("Grpc"); - - f.field("codec", &self.codec); - - f.field( - "accept_compression_encodings", - &self.accept_compression_encodings, - ); - - f.field( - "send_compression_encodings", - &self.send_compression_encodings, - ); - - f.finish() + f.debug_struct("Grpc") + .field("codec", &self.codec) + .field( + "accept_compression_encodings", + &self.accept_compression_encodings, + ) + .field( + "send_compression_encodings", + &self.send_compression_encodings, + ) + .finish() } } diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/service/interceptor.rs rust-tonic-0.13.0+dfsg/tonic/src/service/interceptor.rs --- rust-tonic-0.12.3+dfsg/tonic/src/service/interceptor.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/service/interceptor.rs 2025-03-25 18:49:19.000000000 +0000 @@ -2,12 +2,7 @@ //! //! See [`Interceptor`] for more details. -use crate::{ - body::{boxed, BoxBody}, - request::SanitizeHeaders, - Status, -}; -use bytes::Bytes; +use crate::{request::SanitizeHeaders, Status}; use pin_project::pin_project; use std::{ fmt, @@ -57,33 +52,31 @@ } } -/// Create a new interceptor layer. -/// -/// See [`Interceptor`] for more details. -pub fn interceptor(f: F) -> InterceptorLayer -where - F: Interceptor, -{ - InterceptorLayer { f } -} - /// A gRPC interceptor that can be used as a [`Layer`], -/// created by calling [`interceptor`]. /// /// See [`Interceptor`] for more details. #[derive(Debug, Clone, Copy)] -pub struct InterceptorLayer { - f: F, +pub struct InterceptorLayer { + interceptor: I, } -impl Layer for InterceptorLayer +impl InterceptorLayer { + /// Create a new interceptor layer. + /// + /// See [`Interceptor`] for more details. + pub fn new(interceptor: I) -> Self { + Self { interceptor } + } +} + +impl Layer for InterceptorLayer where - F: Interceptor + Clone, + I: Clone, { - type Service = InterceptedService; + type Service = InterceptedService; fn layer(&self, service: S) -> Self::Service { - InterceptedService::new(service, self.f.clone()) + InterceptedService::new(service, self.interceptor.clone()) } } @@ -91,44 +84,40 @@ /// /// See [`Interceptor`] for more details. #[derive(Clone, Copy)] -pub struct InterceptedService { +pub struct InterceptedService { inner: S, - f: F, + interceptor: I, } -impl InterceptedService { +impl InterceptedService { /// Create a new `InterceptedService` that wraps `S` and intercepts each request with the /// function `F`. - pub fn new(service: S, f: F) -> Self - where - F: Interceptor, - { - Self { inner: service, f } + pub fn new(service: S, interceptor: I) -> Self { + Self { + inner: service, + interceptor, + } } } -impl fmt::Debug for InterceptedService +impl fmt::Debug for InterceptedService where S: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("InterceptedService") .field("inner", &self.inner) - .field("f", &format_args!("{}", std::any::type_name::())) + .field("f", &format_args!("{}", std::any::type_name::())) .finish() } } -impl Service> for InterceptedService +impl Service> for InterceptedService where - ResBody: Default + http_body::Body + Send + 'static, - F: Interceptor, S: Service, Response = http::Response>, - S::Error: Into, - ResBody: http_body::Body + Send + 'static, - ResBody::Error: Into, + I: Interceptor, { - type Response = http::Response; + type Response = http::Response>; type Error = S::Error; type Future = ResponseFuture; @@ -150,7 +139,7 @@ let (metadata, extensions, msg) = req.into_parts(); match self - .f + .interceptor .call(crate::Request::from_parts(metadata, extensions, ())) { Ok(req) => { @@ -165,7 +154,7 @@ } // required to use `InterceptedService` with `Router` -impl crate::server::NamedService for InterceptedService +impl crate::server::NamedService for InterceptedService where S: crate::server::NamedService, { @@ -204,55 +193,87 @@ impl Future for ResponseFuture where F: Future, E>>, - E: Into, - B: Default + http_body::Body + Send + 'static, - B::Error: Into, { - type Output = Result, E>; + type Output = Result>, E>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match self.project().kind.project() { - KindProj::Future(future) => future - .poll(cx) - .map(|result| result.map(|res| res.map(boxed))), + KindProj::Future(future) => future.poll(cx).map_ok(|res| res.map(ResponseBody::wrap)), KindProj::Status(status) => { - let response = status - .take() - .unwrap() - .into_http() - .map(|_| B::default()) - .map(boxed); + let (parts, ()) = status.take().unwrap().into_http::<()>().into_parts(); + let response = http::Response::from_parts(parts, ResponseBody::::empty()); Poll::Ready(Ok(response)) } } } } -#[cfg(test)] -mod tests { - use super::*; - use http_body::Frame; - use http_body_util::Empty; - use tower::ServiceExt; +/// Response body for [`InterceptedService`]. +#[pin_project] +#[derive(Debug)] +pub struct ResponseBody { + #[pin] + kind: ResponseBodyKind, +} - #[derive(Debug, Default)] - struct TestBody; +#[pin_project(project = ResponseBodyKindProj)] +#[derive(Debug)] +enum ResponseBodyKind { + Empty, + Wrap(#[pin] B), +} - impl http_body::Body for TestBody { - type Data = Bytes; - type Error = Status; - - fn poll_frame( - self: Pin<&mut Self>, - _cx: &mut Context<'_>, - ) -> Poll, Self::Error>>> { - Poll::Ready(None) +impl ResponseBody { + fn new(kind: ResponseBodyKind) -> Self { + Self { kind } + } + + fn empty() -> Self { + Self::new(ResponseBodyKind::Empty) + } + + fn wrap(body: B) -> Self { + Self::new(ResponseBodyKind::Wrap(body)) + } +} + +impl http_body::Body for ResponseBody { + type Data = B::Data; + type Error = B::Error; + + fn poll_frame( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll, Self::Error>>> { + match self.project().kind.project() { + ResponseBodyKindProj::Empty => Poll::Ready(None), + ResponseBodyKindProj::Wrap(body) => body.poll_frame(cx), } } + fn size_hint(&self) -> http_body::SizeHint { + match &self.kind { + ResponseBodyKind::Empty => http_body::SizeHint::with_exact(0), + ResponseBodyKind::Wrap(body) => body.size_hint(), + } + } + + fn is_end_stream(&self) -> bool { + match &self.kind { + ResponseBodyKind::Empty => true, + ResponseBodyKind::Wrap(body) => body.is_end_stream(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use tower::ServiceExt; + #[tokio::test] async fn doesnt_remove_headers_from_requests() { - let svc = tower::service_fn(|request: http::Request| async move { + let svc = tower::service_fn(|request: http::Request<()>| async move { assert_eq!( request .headers() @@ -261,7 +282,7 @@ "test-tonic" ); - Ok::<_, Status>(http::Response::new(TestBody)) + Ok::<_, Status>(http::Response::new(())) }); let svc = InterceptedService::new(svc, |request: crate::Request<()>| { @@ -278,7 +299,7 @@ let request = http::Request::builder() .header("user-agent", "test-tonic") - .body(TestBody) + .body(()) .unwrap(); svc.oneshot(request).await.unwrap(); @@ -287,17 +308,17 @@ #[tokio::test] async fn handles_intercepted_status_as_response() { let message = "Blocked by the interceptor"; - let expected = Status::permission_denied(message).into_http(); + let expected = Status::permission_denied(message).into_http::<()>(); - let svc = tower::service_fn(|_: http::Request| async { - Ok::<_, Status>(http::Response::new(TestBody)) + let svc = tower::service_fn(|_: http::Request<()>| async { + Ok::<_, Status>(http::Response::new(())) }); let svc = InterceptedService::new(svc, |_: crate::Request<()>| { Err(Status::permission_denied(message)) }); - let request = http::Request::builder().body(TestBody).unwrap(); + let request = http::Request::builder().body(()).unwrap(); let response = svc.oneshot(request).await.unwrap(); assert_eq!(expected.status(), response.status()); @@ -307,17 +328,17 @@ #[tokio::test] async fn doesnt_change_http_method() { - let svc = tower::service_fn(|request: http::Request>| async move { + let svc = tower::service_fn(|request: http::Request<()>| async move { assert_eq!(request.method(), http::Method::OPTIONS); - Ok::<_, hyper::Error>(hyper::Response::new(Empty::new())) + Ok::<_, hyper::Error>(hyper::Response::new(())) }); let svc = InterceptedService::new(svc, Ok); let request = http::Request::builder() .method(http::Method::OPTIONS) - .body(Empty::new()) + .body(()) .unwrap(); svc.oneshot(request).await.unwrap(); diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/service/layered.rs rust-tonic-0.13.0+dfsg/tonic/src/service/layered.rs --- rust-tonic-0.12.3+dfsg/tonic/src/service/layered.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/service/layered.rs 2025-03-25 18:49:19.000000000 +0000 @@ -0,0 +1,93 @@ +use std::{ + marker::PhantomData, + task::{Context, Poll}, +}; + +use tower_layer::Layer; +use tower_service::Service; + +use crate::server::NamedService; + +/// A layered service to propagate [`NamedService`] implementation. +#[derive(Debug, Clone)] +pub struct Layered { + inner: S, + _ty: PhantomData, +} + +impl NamedService for Layered { + const NAME: &'static str = T::NAME; +} + +impl Service for Layered +where + S: Service, +{ + type Response = S::Response; + type Error = S::Error; + type Future = S::Future; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_ready(cx) + } + + fn call(&mut self, req: Req) -> Self::Future { + self.inner.call(req) + } +} + +/// Extension trait which adds utility methods to types which implement [`tower_layer::Layer`]. +pub trait LayerExt: sealed::Sealed { + /// Applies the layer to a service and wraps it in [`Layered`]. + fn named_layer(&self, service: S) -> Layered + where + L: Layer; +} + +impl LayerExt for L { + fn named_layer(&self, service: S) -> Layered<::Service, S> + where + L: Layer, + { + Layered { + inner: self.layer(service), + _ty: PhantomData, + } + } +} + +mod sealed { + pub trait Sealed {} + impl Sealed for T {} +} + +#[cfg(test)] +mod tests { + use super::*; + + #[derive(Debug, Default)] + struct TestService {} + + const TEST_SERVICE_NAME: &str = "test-service-name"; + + impl NamedService for TestService { + const NAME: &'static str = TEST_SERVICE_NAME; + } + + // Checks if the argument implements `NamedService` and returns the implemented `NAME`. + fn get_name_of_named_service(_s: &S) -> &'static str { + S::NAME + } + + #[test] + fn named_service_is_propagated_to_layered() { + use std::time::Duration; + use tower::{limit::ConcurrencyLimitLayer, timeout::TimeoutLayer}; + + let layered = TimeoutLayer::new(Duration::from_secs(5)).named_layer(TestService::default()); + assert_eq!(get_name_of_named_service(&layered), TEST_SERVICE_NAME); + + let layered = ConcurrencyLimitLayer::new(3).named_layer(layered); + assert_eq!(get_name_of_named_service(&layered), TEST_SERVICE_NAME); + } +} diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/service/mod.rs rust-tonic-0.13.0+dfsg/tonic/src/service/mod.rs --- rust-tonic-0.12.3+dfsg/tonic/src/service/mod.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/service/mod.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,13 +1,18 @@ //! Utilities for using Tower services with Tonic. pub mod interceptor; +pub(crate) mod layered; #[cfg(feature = "router")] pub(crate) mod router; #[doc(inline)] -pub use self::interceptor::{interceptor, Interceptor}; +pub use self::interceptor::{Interceptor, InterceptorLayer}; +pub use self::layered::{LayerExt, Layered}; #[doc(inline)] #[cfg(feature = "router")] pub use self::router::{Routes, RoutesBuilder}; #[cfg(feature = "router")] pub use axum::{body::Body as AxumBody, Router as AxumRouter}; + +pub mod recover_error; +pub use self::recover_error::{RecoverError, RecoverErrorLayer}; diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/service/recover_error.rs rust-tonic-0.13.0+dfsg/tonic/src/service/recover_error.rs --- rust-tonic-0.12.3+dfsg/tonic/src/service/recover_error.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/service/recover_error.rs 2025-03-25 18:49:19.000000000 +0000 @@ -0,0 +1,163 @@ +//! Middleware which recovers from error. + +use std::{ + fmt, + future::Future, + pin::Pin, + task::{ready, Context, Poll}, +}; + +use http::Response; +use pin_project::pin_project; +use tower_layer::Layer; +use tower_service::Service; + +use crate::Status; + +/// Layer which applies the [`RecoverError`] middleware. +#[derive(Debug, Default, Clone)] +pub struct RecoverErrorLayer { + _priv: (), +} + +impl RecoverErrorLayer { + /// Create a new `RecoverErrorLayer`. + pub fn new() -> Self { + Self { _priv: () } + } +} + +impl Layer for RecoverErrorLayer { + type Service = RecoverError; + + fn layer(&self, inner: S) -> Self::Service { + RecoverError::new(inner) + } +} + +/// Middleware that attempts to recover from service errors by turning them into a response built +/// from the `Status`. +#[derive(Debug, Clone)] +pub struct RecoverError { + inner: S, +} + +impl RecoverError { + /// Create a new `RecoverError` middleware. + pub fn new(inner: S) -> Self { + Self { inner } + } +} + +impl Service for RecoverError +where + S: Service>, + S::Error: Into, +{ + type Response = Response>; + type Error = crate::BoxError; + type Future = ResponseFuture; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_ready(cx).map_err(Into::into) + } + + fn call(&mut self, req: Req) -> Self::Future { + ResponseFuture { + inner: self.inner.call(req), + } + } +} + +/// Response future for [`RecoverError`]. +#[pin_project] +pub struct ResponseFuture { + #[pin] + inner: F, +} + +impl fmt::Debug for ResponseFuture { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ResponseFuture").finish() + } +} + +impl Future for ResponseFuture +where + F: Future, E>>, + E: Into, +{ + type Output = Result>, crate::BoxError>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match ready!(self.project().inner.poll(cx)) { + Ok(response) => { + let response = response.map(ResponseBody::full); + Poll::Ready(Ok(response)) + } + Err(err) => match Status::try_from_error(err.into()) { + Ok(status) => { + let (parts, ()) = status.into_http::<()>().into_parts(); + let res = Response::from_parts(parts, ResponseBody::empty()); + Poll::Ready(Ok(res)) + } + Err(err) => Poll::Ready(Err(err)), + }, + } + } +} + +/// Response body for [`RecoverError`]. +#[pin_project] +pub struct ResponseBody { + #[pin] + inner: Option, +} + +impl fmt::Debug for ResponseBody { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ResponseBody").finish() + } +} + +impl ResponseBody { + fn full(inner: B) -> Self { + Self { inner: Some(inner) } + } + + const fn empty() -> Self { + Self { inner: None } + } +} + +impl http_body::Body for ResponseBody +where + B: http_body::Body, +{ + type Data = B::Data; + type Error = B::Error; + + fn poll_frame( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll, Self::Error>>> { + match self.project().inner.as_pin_mut() { + Some(b) => b.poll_frame(cx), + None => Poll::Ready(None), + } + } + + fn is_end_stream(&self) -> bool { + match &self.inner { + Some(b) => b.is_end_stream(), + None => true, + } + } + + fn size_hint(&self) -> http_body::SizeHint { + match &self.inner { + Some(body) => body.size_hint(), + None => http_body::SizeHint::with_exact(0), + } + } +} diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/service/router.rs rust-tonic-0.13.0+dfsg/tonic/src/service/router.rs --- rust-tonic-0.12.3+dfsg/tonic/src/service/router.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/service/router.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,16 +1,11 @@ -use crate::{ - body::{boxed, BoxBody}, - metadata::GRPC_CONTENT_TYPE, - server::NamedService, - Status, -}; -use http::{HeaderValue, Request, Response}; +use crate::{body::Body, server::NamedService, Status}; +use http::{Request, Response}; use std::{ convert::Infallible, fmt, future::Future, pin::Pin, - task::{ready, Context, Poll}, + task::{Context, Poll}, }; use tower::{Service, ServiceExt}; @@ -30,13 +25,14 @@ /// Add a new service. pub fn add_service(&mut self, svc: S) -> &mut Self where - S: Service, Response = Response, Error = Infallible> + S: Service, Error = Infallible> + NamedService + Clone + Send + + Sync + 'static, + S::Response: axum::response::IntoResponse, S::Future: Send + 'static, - S::Error: Into + Send, { let routes = self.routes.take().unwrap_or_default(); self.routes.replace(routes.add_service(svc)); @@ -61,13 +57,14 @@ /// Create a new routes with `svc` already added to it. pub fn new(svc: S) -> Self where - S: Service, Response = Response, Error = Infallible> + S: Service, Error = Infallible> + NamedService + Clone + Send + + Sync + 'static, + S::Response: axum::response::IntoResponse, S::Future: Send + 'static, - S::Error: Into + Send, { Self::default().add_service(svc) } @@ -80,17 +77,18 @@ /// Add a new service. pub fn add_service(mut self, svc: S) -> Self where - S: Service, Response = Response, Error = Infallible> + S: Service, Error = Infallible> + NamedService + Clone + Send + + Sync + 'static, + S::Response: axum::response::IntoResponse, S::Future: Send + 'static, - S::Error: Into + Send, { self.router = self.router.route_service( - &format!("/{}/*rest", S::NAME), - svc.map_request(|req: Request| req.map(boxed)), + &format!("/{}/{{*rest}}", S::NAME), + svc.map_request(|req: Request| req.map(Body::new)), ); self } @@ -105,15 +103,30 @@ } /// Convert this `Routes` into an [`axum::Router`]. - #[deprecated(since = "0.12.2", note = "Use `Routes::into_axum_router` instead.")] - pub fn into_router(self) -> axum::Router { - self.into_axum_router() - } - - /// Convert this `Routes` into an [`axum::Router`]. pub fn into_axum_router(self) -> axum::Router { self.router } + + /// Get a mutable reference to the [`axum::Router`]. + pub fn axum_router_mut(&mut self) -> &mut axum::Router { + &mut self.router + } +} + +impl From for RoutesBuilder { + fn from(routes: Routes) -> Self { + Self { + routes: Some(routes), + } + } +} + +impl From for RoutesBuilder { + fn from(router: axum::Router) -> Self { + Self { + routes: Some(router.into()), + } + } } impl From for Routes { @@ -122,26 +135,26 @@ } } -async fn unimplemented() -> impl axum::response::IntoResponse { - let status = http::StatusCode::OK; - let headers = [ - (Status::GRPC_STATUS, HeaderValue::from_static("12")), - (http::header::CONTENT_TYPE, GRPC_CONTENT_TYPE), - ]; - (status, headers) +async fn unimplemented() -> Response { + let (parts, ()) = Status::unimplemented("").into_http::<()>().into_parts(); + Response::from_parts(parts, Body::empty()) } -impl Service> for Routes { - type Response = Response; - type Error = crate::Error; +impl Service> for Routes +where + B: http_body::Body + Send + 'static, + B::Error: Into, +{ + type Response = Response; + type Error = Infallible; type Future = RoutesFuture; #[inline] - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + Service::>::poll_ready(&mut self.router, cx).map_err(|e| match e {}) } - fn call(&mut self, req: Request) -> Self::Future { + fn call(&mut self, req: Request) -> Self::Future { RoutesFuture(self.router.call(req)) } } @@ -155,15 +168,11 @@ } impl Future for RoutesFuture { - type Output = Result, crate::Error>; + type Output = Result, Infallible>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match ready!(Pin::new(&mut self.as_mut().0).poll(cx)) { - Ok(res) => Ok(res.map(boxed)).into(), - // NOTE: This pattern is not needed from Rust 1.82. - // See https://github.com/rust-lang/rust/pull/122792. - #[allow(unreachable_patterns)] - Err(err) => match err {}, - } + Pin::new(&mut self.as_mut().0) + .poll(cx) + .map_ok(|res| res.map(Body::new)) } } diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/status.rs rust-tonic-0.13.0+dfsg/tonic/src/status.rs --- rust-tonic-0.12.3+dfsg/tonic/src/status.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/status.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,5 +1,5 @@ use crate::metadata::MetadataMap; -use crate::{body::BoxBody, metadata::GRPC_CONTENT_TYPE}; +use crate::metadata::GRPC_CONTENT_TYPE; use base64::Engine as _; use bytes::Bytes; use http::{ @@ -14,6 +14,7 @@ .add(b' ') .add(b'"') .add(b'#') + .add(b'%') .add(b'<') .add(b'>') .add(b'`') @@ -438,51 +439,46 @@ /// Extract a `Status` from a hyper `HeaderMap`. pub fn from_header_map(header_map: &HeaderMap) -> Option { - header_map.get(Self::GRPC_STATUS).map(|code| { - let code = Code::from_bytes(code.as_ref()); - let error_message = header_map - .get(Self::GRPC_MESSAGE) - .map(|header| { - percent_decode(header.as_bytes()) - .decode_utf8() - .map(|cow| cow.to_string()) - }) - .unwrap_or_else(|| Ok(String::new())); - - let details = header_map - .get(Self::GRPC_STATUS_DETAILS) - .map(|h| { - crate::util::base64::STANDARD - .decode(h.as_bytes()) - .expect("Invalid status header, expected base64 encoded value") - }) - .map(Bytes::from) - .unwrap_or_default(); - - let mut other_headers = header_map.clone(); - other_headers.remove(Self::GRPC_STATUS); - other_headers.remove(Self::GRPC_MESSAGE); - other_headers.remove(Self::GRPC_STATUS_DETAILS); - - match error_message { - Ok(message) => Status { - code, - message, - details, - metadata: MetadataMap::from_headers(other_headers), - source: None, - }, - Err(err) => { - warn!("Error deserializing status message header: {}", err); - Status { - code: Code::Unknown, - message: format!("Error deserializing status message header: {}", err), - details, - metadata: MetadataMap::from_headers(other_headers), - source: None, - } - } + let code = Code::from_bytes(header_map.get(Self::GRPC_STATUS)?.as_ref()); + + let error_message = match header_map.get(Self::GRPC_MESSAGE) { + Some(header) => percent_decode(header.as_bytes()) + .decode_utf8() + .map(|cow| cow.to_string()), + None => Ok(String::new()), + }; + + let details = match header_map.get(Self::GRPC_STATUS_DETAILS) { + Some(header) => crate::util::base64::STANDARD + .decode(header.as_bytes()) + .expect("Invalid status header, expected base64 encoded value") + .into(), + None => Bytes::new(), + }; + + let other_headers = { + let mut header_map = header_map.clone(); + header_map.remove(Self::GRPC_STATUS); + header_map.remove(Self::GRPC_MESSAGE); + header_map.remove(Self::GRPC_STATUS_DETAILS); + header_map + }; + + let (code, message) = match error_message { + Ok(message) => (code, message), + Err(e) => { + let error_message = format!("Error deserializing status message header: {e}"); + warn!(error_message); + (Code::Unknown, error_message) } + }; + + Some(Status { + code, + message, + details, + metadata: MetadataMap::from_headers(other_headers), + source: None, }) } @@ -579,8 +575,8 @@ } /// Build an `http::Response` from the given `Status`. - pub fn into_http(self) -> http::Response { - let mut response = http::Response::new(crate::body::empty_body()); + pub fn into_http(self) -> http::Response { + let mut response = http::Response::new(B::default()); response .headers_mut() .insert(http::header::CONTENT_TYPE, GRPC_CONTENT_TYPE); @@ -783,8 +779,28 @@ /// Get the `Code` that represents the integer, if known. /// /// If not known, returns `Code::Unknown` (surprise!). - pub fn from_i32(i: i32) -> Code { - Code::from(i) + pub const fn from_i32(i: i32) -> Code { + match i { + 0 => Code::Ok, + 1 => Code::Cancelled, + 2 => Code::Unknown, + 3 => Code::InvalidArgument, + 4 => Code::DeadlineExceeded, + 5 => Code::NotFound, + 6 => Code::AlreadyExists, + 7 => Code::PermissionDenied, + 8 => Code::ResourceExhausted, + 9 => Code::FailedPrecondition, + 10 => Code::Aborted, + 11 => Code::OutOfRange, + 12 => Code::Unimplemented, + 13 => Code::Internal, + 14 => Code::Unavailable, + 15 => Code::DataLoss, + 16 => Code::Unauthenticated, + + _ => Code::Unknown, + } } /// Convert the string representation of a `Code` (as stored, for example, in the `grpc-status` @@ -849,27 +865,7 @@ impl From for Code { fn from(i: i32) -> Self { - match i { - 0 => Code::Ok, - 1 => Code::Cancelled, - 2 => Code::Unknown, - 3 => Code::InvalidArgument, - 4 => Code::DeadlineExceeded, - 5 => Code::NotFound, - 6 => Code::AlreadyExists, - 7 => Code::PermissionDenied, - 8 => Code::ResourceExhausted, - 9 => Code::FailedPrecondition, - 10 => Code::Aborted, - 11 => Code::OutOfRange, - 12 => Code::Unimplemented, - 13 => Code::Internal, - 14 => Code::Unavailable, - 15 => Code::DataLoss, - 16 => Code::Unauthenticated, - - _ => Code::Unknown, - } + Code::from_i32(i) } } @@ -883,10 +879,10 @@ #[cfg(test)] mod tests { use super::*; - use crate::Error; + use crate::BoxError; #[derive(Debug)] - struct Nested(Error); + struct Nested(BoxError); impl fmt::Display for Nested { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -911,7 +907,7 @@ #[test] fn from_error_unknown() { - let orig: Error = "peek-a-boo".into(); + let orig: BoxError = "peek-a-boo".into(); let found = Status::from_error(orig); assert_eq!(found.code(), Code::Unknown); diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/channel/endpoint.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/channel/endpoint.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/channel/endpoint.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/channel/endpoint.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,29 +1,39 @@ -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] use super::service::TlsConnector; use super::service::{self, Executor, SharedExec}; +use super::uds_connector::UdsConnector; use super::Channel; -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] use super::ClientTlsConfig; +#[cfg(feature = "_tls-any")] +use crate::transport::error; use crate::transport::Error; use bytes::Bytes; use http::{uri::Uri, HeaderValue}; use hyper::rt; use hyper_util::client::legacy::connect::HttpConnector; -use std::{fmt, future::Future, pin::Pin, str::FromStr, time::Duration}; +use std::{fmt, future::Future, net::IpAddr, pin::Pin, str, str::FromStr, time::Duration}; use tower_service::Service; +#[derive(Clone, PartialEq, Eq, Hash)] +pub(crate) enum EndpointType { + Uri(Uri), + Uds(String), +} + /// Channel builder. /// /// This struct is used to build and configure HTTP/2 channels. #[derive(Clone)] pub struct Endpoint { - pub(crate) uri: Uri, + pub(crate) uri: EndpointType, + fallback_uri: Uri, pub(crate) origin: Option, pub(crate) user_agent: Option, pub(crate) timeout: Option, pub(crate) concurrency_limit: Option, pub(crate) rate_limit: Option<(u64, Duration)>, - #[cfg(feature = "tls")] + #[cfg(feature = "_tls-any")] pub(crate) tls: Option, pub(crate) buffer_size: Option, pub(crate) init_stream_window_size: Option, @@ -36,6 +46,7 @@ pub(crate) http2_max_header_list_size: Option, pub(crate) connect_timeout: Option, pub(crate) http2_adaptive_window: Option, + pub(crate) local_address: Option, pub(crate) executor: SharedExec, } @@ -46,17 +57,72 @@ pub fn new(dst: D) -> Result where D: TryInto, - D::Error: Into, + D::Error: Into, { let me = dst.try_into().map_err(|e| Error::from_source(e.into()))?; - #[cfg(feature = "tls")] - if me.uri.scheme() == Some(&http::uri::Scheme::HTTPS) { - return me.tls_config(ClientTlsConfig::new().with_enabled_roots()); + #[cfg(feature = "_tls-any")] + if let EndpointType::Uri(uri) = &me.uri { + if uri.scheme() == Some(&http::uri::Scheme::HTTPS) { + return me.tls_config(ClientTlsConfig::new().with_enabled_roots()); + } } - Ok(me) } + fn new_uri(uri: Uri) -> Self { + Self { + uri: EndpointType::Uri(uri.clone()), + fallback_uri: uri, + origin: None, + user_agent: None, + concurrency_limit: None, + rate_limit: None, + timeout: None, + #[cfg(feature = "_tls-any")] + tls: None, + buffer_size: None, + init_stream_window_size: None, + init_connection_window_size: None, + tcp_keepalive: None, + tcp_nodelay: true, + http2_keep_alive_interval: None, + http2_keep_alive_timeout: None, + http2_keep_alive_while_idle: None, + http2_max_header_list_size: None, + connect_timeout: None, + http2_adaptive_window: None, + executor: SharedExec::tokio(), + local_address: None, + } + } + + fn new_uds(uds_filepath: &str) -> Self { + Self { + uri: EndpointType::Uds(uds_filepath.to_string()), + fallback_uri: Uri::from_static("http://tonic"), + origin: None, + user_agent: None, + concurrency_limit: None, + rate_limit: None, + timeout: None, + #[cfg(feature = "_tls-any")] + tls: None, + buffer_size: None, + init_stream_window_size: None, + init_connection_window_size: None, + tcp_keepalive: None, + tcp_nodelay: true, + http2_keep_alive_interval: None, + http2_keep_alive_timeout: None, + http2_keep_alive_while_idle: None, + http2_max_header_list_size: None, + connect_timeout: None, + http2_adaptive_window: None, + executor: SharedExec::tokio(), + local_address: None, + } + } + /// Convert an `Endpoint` from a static string. /// /// # Panics @@ -68,8 +134,16 @@ /// Endpoint::from_static("https://example.com"); /// ``` pub fn from_static(s: &'static str) -> Self { - let uri = Uri::from_static(s); - Self::from(uri) + if s.starts_with("unix:") { + let uds_filepath = s + .strip_prefix("unix://") + .or_else(|| s.strip_prefix("unix:")) + .expect("Invalid unix domain socket URI"); + Self::new_uds(uds_filepath) + } else { + let uri = Uri::from_static(s); + Self::new_uri(uri) + } } /// Convert an `Endpoint` from shared bytes. @@ -79,8 +153,19 @@ /// Endpoint::from_shared("https://example.com".to_string()); /// ``` pub fn from_shared(s: impl Into) -> Result { - let uri = Uri::from_maybe_shared(s.into()).map_err(|e| Error::new_invalid_uri().with(e))?; - Ok(Self::from(uri)) + let s = str::from_utf8(&s.into()) + .map_err(|e| Error::new_invalid_uri().with(e))? + .to_string(); + if s.starts_with("unix:") { + let uds_filepath = s + .strip_prefix("unix://") + .or_else(|| s.strip_prefix("unix:")) + .ok_or(Error::new_invalid_uri())?; + Ok(Self::new_uds(uds_filepath)) + } else { + let uri = Uri::from_maybe_shared(s).map_err(|e| Error::new_invalid_uri().with(e))?; + Ok(Self::from(uri)) + } } /// Set a custom user-agent header. @@ -244,16 +329,19 @@ } /// Configures TLS for the endpoint. - #[cfg(feature = "tls")] + #[cfg(feature = "_tls-any")] pub fn tls_config(self, tls_config: ClientTlsConfig) -> Result { - Ok(Endpoint { - tls: Some( - tls_config - .into_tls_connector(&self.uri) - .map_err(Error::from_source)?, - ), - ..self - }) + match &self.uri { + EndpointType::Uri(uri) => Ok(Endpoint { + tls: Some( + tls_config + .into_tls_connector(uri) + .map_err(Error::from_source)?, + ), + ..self + }), + EndpointType::Uds(_) => Err(Error::new(error::Kind::InvalidTlsConfigForUds)), + } } /// Set the value of `TCP_NODELAY` option for accepted connections. Enabled by default. @@ -320,22 +408,43 @@ pub(crate) fn connector(&self, c: C) -> service::Connector { service::Connector::new( c, - #[cfg(feature = "tls")] + #[cfg(feature = "_tls-any")] self.tls.clone(), ) } - /// Create a channel from this config. - pub async fn connect(&self) -> Result { + /// Set the local address. + /// + /// This sets the IP address the client will use. By default we let hyper select the IP address. + pub fn local_address(self, addr: Option) -> Self { + Endpoint { + local_address: addr, + ..self + } + } + + pub(crate) fn http_connector(&self) -> service::Connector { let mut http = HttpConnector::new(); http.enforce_http(false); http.set_nodelay(self.tcp_nodelay); http.set_keepalive(self.tcp_keepalive); http.set_connect_timeout(self.connect_timeout); + http.set_local_address(self.local_address); + self.connector(http) + } - let connector = self.connector(http); + pub(crate) fn uds_connector(&self, uds_filepath: &str) -> service::Connector { + self.connector(UdsConnector::new(uds_filepath)) + } - Channel::connect(connector, self.clone()).await + /// Create a channel from this config. + pub async fn connect(&self) -> Result { + match &self.uri { + EndpointType::Uri(_) => Channel::connect(self.http_connector(), self.clone()).await, + EndpointType::Uds(uds_filepath) => { + Channel::connect(self.uds_connector(uds_filepath.as_str()), self.clone()).await + } + } } /// Create a channel from this config. @@ -343,15 +452,12 @@ /// The channel returned by this method does not attempt to connect to the endpoint until first /// use. pub fn connect_lazy(&self) -> Channel { - let mut http = HttpConnector::new(); - http.enforce_http(false); - http.set_nodelay(self.tcp_nodelay); - http.set_keepalive(self.tcp_keepalive); - http.set_connect_timeout(self.connect_timeout); - - let connector = self.connector(http); - - Channel::new(connector, self.clone()) + match &self.uri { + EndpointType::Uri(_) => Channel::new(self.http_connector(), self.clone()), + EndpointType::Uds(uds_filepath) => { + Channel::new(self.uds_connector(uds_filepath.as_str()), self.clone()) + } + } } /// Connect with a custom connector. @@ -366,7 +472,7 @@ C: Service + Send + 'static, C::Response: rt::Read + rt::Write + Send + Unpin, C::Future: Send, - crate::Error: From + Send, + crate::BoxError: From + Send, { let connector = self.connector(connector); @@ -391,7 +497,7 @@ C: Service + Send + 'static, C::Response: rt::Read + rt::Write + Send + Unpin, C::Future: Send, - crate::Error: From + Send, + crate::BoxError: From + Send, { let connector = self.connector(connector); if let Some(connect_timeout) = self.connect_timeout { @@ -413,34 +519,35 @@ /// assert_eq!(endpoint.uri(), &Uri::from_static("https://example.com")); /// ``` pub fn uri(&self) -> &Uri { - &self.uri + match &self.uri { + EndpointType::Uri(uri) => uri, + EndpointType::Uds(_) => &self.fallback_uri, + } + } + + /// Get the value of `TCP_NODELAY` option for accepted connections. + pub fn get_tcp_nodelay(&self) -> bool { + self.tcp_nodelay + } + + /// Get the connect timeout. + pub fn get_connect_timeout(&self) -> Option { + self.connect_timeout + } + + /// Get whether TCP keepalive messages are enabled on accepted connections. + /// + /// If `None` is specified, keepalive is disabled, otherwise the duration + /// specified will be the time to remain idle before sending TCP keepalive + /// probes. + pub fn get_tcp_keepalive(&self) -> Option { + self.tcp_keepalive } } impl From for Endpoint { fn from(uri: Uri) -> Self { - Self { - uri, - origin: None, - user_agent: None, - concurrency_limit: None, - rate_limit: None, - timeout: None, - #[cfg(feature = "tls")] - tls: None, - buffer_size: None, - init_stream_window_size: None, - init_connection_window_size: None, - tcp_keepalive: None, - tcp_nodelay: true, - http2_keep_alive_interval: None, - http2_keep_alive_timeout: None, - http2_keep_alive_while_idle: None, - http2_max_header_list_size: None, - connect_timeout: None, - http2_adaptive_window: None, - executor: SharedExec::tokio(), - } + Self::new_uri(uri) } } diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/channel/mod.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/channel/mod.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/channel/mod.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/channel/mod.rs 2025-03-25 18:49:19.000000000 +0000 @@ -2,21 +2,22 @@ mod endpoint; pub(crate) mod service; -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] mod tls; +mod uds_connector; +pub use self::service::Change; pub use endpoint::Endpoint; -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] pub use tls::ClientTlsConfig; use self::service::{Connection, DynamicServiceStream, Executor, SharedExec}; -use crate::body::BoxBody; +use crate::body::Body; use bytes::Bytes; use http::{ uri::{InvalidUri, Uri}, Request, Response, }; -use hyper_util::client::legacy::connect::Connection as HyperConnection; use std::{ fmt, future::Future, @@ -29,14 +30,13 @@ use hyper::rt; use tower::balance::p2c::Balance; use tower::{ - buffer::{self, Buffer}, - discover::{Change, Discover}, - util::{BoxService, Either}, + buffer::{future::ResponseFuture as BufferResponseFuture, Buffer}, + discover::Discover, + util::BoxService, Service, }; type BoxFuture<'a, T> = Pin + Send + 'a>>; -type Svc = Either, Response, crate::Error>>; const DEFAULT_BUFFER_SIZE: usize = 1024; @@ -65,14 +65,14 @@ /// cloning the `Channel` type is cheap and encouraged. #[derive(Clone)] pub struct Channel { - svc: Buffer>, + svc: Buffer, BoxFuture<'static, Result, crate::BoxError>>>, } /// A future that resolves to an HTTP response. /// /// This is returned by the `Service::call` on [`Channel`]. pub struct ResponseFuture { - inner: buffer::future::ResponseFuture<>>::Future>, + inner: BufferResponseFuture, crate::BoxError>>>, } impl Channel { @@ -145,29 +145,36 @@ (Self::balance(list, DEFAULT_BUFFER_SIZE, executor), tx) } - pub(crate) fn new(connector: C, endpoint: Endpoint) -> Self + /// Create a new [`Channel`] using a custom connector to the provided [Endpoint]. + /// + /// This is a lower level API, prefer to use [`Endpoint::connect_lazy`] if you are not using a custom connector. + pub fn new(connector: C, endpoint: Endpoint) -> Self where C: Service + Send + 'static, - C::Error: Into + Send, + C::Error: Into + Send, C::Future: Send, - C::Response: rt::Read + rt::Write + HyperConnection + Unpin + Send + 'static, + C::Response: rt::Read + rt::Write + Unpin + Send + 'static, { let buffer_size = endpoint.buffer_size.unwrap_or(DEFAULT_BUFFER_SIZE); let executor = endpoint.executor.clone(); let svc = Connection::lazy(connector, endpoint); - let (svc, worker) = Buffer::pair(Either::A(svc), buffer_size); + let (svc, worker) = Buffer::pair(svc, buffer_size); + executor.execute(worker); Channel { svc } } - pub(crate) async fn connect(connector: C, endpoint: Endpoint) -> Result + /// Connect to the provided [`Endpoint`] using the provided connector, and return a new [`Channel`]. + /// + /// This is a lower level API, prefer to use [`Endpoint::connect`] if you are not using a custom connector. + pub async fn connect(connector: C, endpoint: Endpoint) -> Result where C: Service + Send + 'static, - C::Error: Into + Send, + C::Error: Into + Send, C::Future: Unpin + Send, - C::Response: rt::Read + rt::Write + HyperConnection + Unpin + Send + 'static, + C::Response: rt::Read + rt::Write + Unpin + Send + 'static, { let buffer_size = endpoint.buffer_size.unwrap_or(DEFAULT_BUFFER_SIZE); let executor = endpoint.executor.clone(); @@ -175,7 +182,7 @@ let svc = Connection::connect(connector, endpoint) .await .map_err(super::Error::from_source)?; - let (svc, worker) = Buffer::pair(Either::A(svc), buffer_size); + let (svc, worker) = Buffer::pair(svc, buffer_size); executor.execute(worker); Ok(Channel { svc }) @@ -184,22 +191,22 @@ pub(crate) fn balance(discover: D, buffer_size: usize, executor: E) -> Self where D: Discover + Unpin + Send + 'static, - D::Error: Into, + D::Error: Into, D::Key: Hash + Send + Clone, E: Executor> + Send + Sync + 'static, { let svc = Balance::new(discover); let svc = BoxService::new(svc); - let (svc, worker) = Buffer::pair(Either::B(svc), buffer_size); + let (svc, worker) = Buffer::pair(svc, buffer_size); executor.execute(Box::pin(worker)); Channel { svc } } } -impl Service> for Channel { - type Response = http::Response; +impl Service> for Channel { + type Response = http::Response; type Error = super::Error; type Future = ResponseFuture; @@ -207,7 +214,7 @@ Service::poll_ready(&mut self.svc, cx).map_err(super::Error::from_source) } - fn call(&mut self, request: http::Request) -> Self::Future { + fn call(&mut self, request: http::Request) -> Self::Future { let inner = Service::call(&mut self.svc, request); ResponseFuture { inner } @@ -215,7 +222,7 @@ } impl Future for ResponseFuture { - type Output = Result, super::Error>; + type Output = Result, super::Error>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Pin::new(&mut self.inner) diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/channel/service/add_origin.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/channel/service/add_origin.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/channel/service/add_origin.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/channel/service/add_origin.rs 2025-03-25 18:49:19.000000000 +0000 @@ -30,10 +30,10 @@ where T: Service>, T::Future: Send + 'static, - T::Error: Into, + T::Error: Into, { type Response = T::Response; - type Error = crate::Error; + type Error = crate::BoxError; type Future = BoxFuture<'static, Result>; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { @@ -53,7 +53,7 @@ head.uri = { // Split the request URI into parts. let mut uri: http::uri::Parts = head.uri.into(); - // Update the URI parts, setting hte scheme and authority + // Update the URI parts, setting the scheme and authority uri.scheme = self.scheme.clone(); uri.authority = self.authority.clone(); diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/channel/service/connection.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/channel/service/connection.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/channel/service/connection.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/channel/service/connection.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,6 +1,6 @@ use super::{AddOrigin, Reconnect, SharedExec, UserAgent}; use crate::{ - body::{boxed, BoxBody}, + body::Body, transport::{channel::BoxFuture, service::GrpcTimeout, Endpoint}, }; use http::{Request, Response, Uri}; @@ -21,14 +21,14 @@ use tower_service::Service; pub(crate) struct Connection { - inner: BoxService, Response, crate::Error>, + inner: BoxService, Response, crate::BoxError>, } impl Connection { fn new(connector: C, endpoint: Endpoint, is_lazy: bool) -> Self where C: Service + Send + 'static, - C::Error: Into + Send, + C::Error: Into + Send, C::Future: Send, C::Response: rt::Read + rt::Write + Unpin + Send + 'static, { @@ -57,7 +57,7 @@ let stack = ServiceBuilder::new() .layer_fn(|s| { - let origin = endpoint.origin.as_ref().unwrap_or(&endpoint.uri).clone(); + let origin = endpoint.origin.as_ref().unwrap_or(endpoint.uri()).clone(); AddOrigin::new(s, origin) }) @@ -70,17 +70,20 @@ let make_service = MakeSendRequestService::new(connector, endpoint.executor.clone(), settings); - let conn = Reconnect::new(make_service, endpoint.uri.clone(), is_lazy); + let conn = Reconnect::new(make_service, endpoint.uri().clone(), is_lazy); Self { inner: BoxService::new(stack.layer(conn)), } } - pub(crate) async fn connect(connector: C, endpoint: Endpoint) -> Result + pub(crate) async fn connect( + connector: C, + endpoint: Endpoint, + ) -> Result where C: Service + Send + 'static, - C::Error: Into + Send, + C::Error: Into + Send, C::Future: Unpin + Send, C::Response: rt::Read + rt::Write + Unpin + Send + 'static, { @@ -90,7 +93,7 @@ pub(crate) fn lazy(connector: C, endpoint: Endpoint) -> Self where C: Service + Send + 'static, - C::Error: Into + Send, + C::Error: Into + Send, C::Future: Send, C::Response: rt::Read + rt::Write + Unpin + Send + 'static, { @@ -98,16 +101,16 @@ } } -impl Service> for Connection { - type Response = Response; - type Error = crate::Error; +impl Service> for Connection { + type Response = Response; + type Error = crate::BoxError; type Future = BoxFuture<'static, Result>; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { Service::poll_ready(&mut self.inner, cx).map_err(Into::into) } - fn call(&mut self, req: Request) -> Self::Future { + fn call(&mut self, req: Request) -> Self::Future { self.inner.call(req) } } @@ -127,28 +130,28 @@ } struct SendRequest { - inner: hyper::client::conn::http2::SendRequest, + inner: hyper::client::conn::http2::SendRequest, } -impl From> for SendRequest { - fn from(inner: hyper::client::conn::http2::SendRequest) -> Self { +impl From> for SendRequest { + fn from(inner: hyper::client::conn::http2::SendRequest) -> Self { Self { inner } } } -impl tower::Service> for SendRequest { - type Response = Response; - type Error = crate::Error; +impl tower::Service> for SendRequest { + type Response = Response; + type Error = crate::BoxError; type Future = BoxFuture<'static, Result>; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.inner.poll_ready(cx).map_err(Into::into) } - fn call(&mut self, req: Request) -> Self::Future { + fn call(&mut self, req: Request) -> Self::Future { let fut = self.inner.send_request(req); - Box::pin(async move { fut.await.map_err(Into::into).map(|res| res.map(boxed)) }) + Box::pin(async move { fut.await.map_err(Into::into).map(|res| res.map(Body::new)) }) } } @@ -171,12 +174,12 @@ impl tower::Service for MakeSendRequestService where C: Service + Send + 'static, - C::Error: Into + Send, + C::Error: Into + Send, C::Future: Send, C::Response: rt::Read + rt::Write + Unpin + Send, { type Response = SendRequest; - type Error = crate::Error; + type Error = crate::BoxError; type Future = BoxFuture<'static, Result>; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/channel/service/connector.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/channel/service/connector.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/channel/service/connector.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/channel/service/connector.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,30 +1,30 @@ use super::BoxedIo; -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] use super::TlsConnector; use crate::transport::channel::BoxFuture; use crate::ConnectError; use http::Uri; -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] use std::fmt; use std::task::{Context, Poll}; use hyper::rt; -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] use hyper_util::rt::TokioIo; use tower_service::Service; pub(crate) struct Connector { inner: C, - #[cfg(feature = "tls")] + #[cfg(feature = "_tls-any")] tls: Option, } impl Connector { - pub(crate) fn new(inner: C, #[cfg(feature = "tls")] tls: Option) -> Self { + pub(crate) fn new(inner: C, #[cfg(feature = "_tls-any")] tls: Option) -> Self { Self { inner, - #[cfg(feature = "tls")] + #[cfg(feature = "_tls-any")] tls, } } @@ -35,7 +35,7 @@ C: Service, C::Response: rt::Read + rt::Write + Unpin + Send + 'static, C::Future: Send + 'static, - crate::Error: From + Send + 'static, + crate::BoxError: From + Send + 'static, { type Response = BoxedIo; type Error = ConnectError; @@ -48,10 +48,10 @@ } fn call(&mut self, uri: Uri) -> Self::Future { - #[cfg(feature = "tls")] + #[cfg(feature = "_tls-any")] let tls = self.tls.clone(); - #[cfg(feature = "tls")] + #[cfg(feature = "_tls-any")] let is_https = uri.scheme_str() == Some("https"); let connect = self.inner.call(uri); @@ -59,7 +59,7 @@ async { let io = connect.await?; - #[cfg(feature = "tls")] + #[cfg(feature = "_tls-any")] if is_https { return if let Some(tls) = tls { let io = tls.connect(TokioIo::new(io)).await?; @@ -69,7 +69,7 @@ }; } - Ok::<_, crate::Error>(BoxedIo::new(io)) + Ok::<_, crate::BoxError>(BoxedIo::new(io)) } .await .map_err(ConnectError) @@ -78,11 +78,11 @@ } /// Error returned when trying to connect to an HTTPS endpoint without TLS enabled. -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] #[derive(Debug)] pub(crate) struct HttpsUriWithoutTlsSupport(()); -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] impl fmt::Display for HttpsUriWithoutTlsSupport { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Connecting to HTTPS without TLS enabled") @@ -90,5 +90,5 @@ } // std::error::Error only requires a type to impl Debug and Display -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] impl std::error::Error for HttpsUriWithoutTlsSupport {} diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/channel/service/discover.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/channel/service/discover.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/channel/service/discover.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/channel/service/discover.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,17 +1,22 @@ use super::super::{Connection, Endpoint}; -use hyper_util::client::legacy::connect::HttpConnector; use std::{ hash::Hash, pin::Pin, task::{Context, Poll}, }; use tokio::sync::mpsc::Receiver; - use tokio_stream::Stream; -use tower::discover::Change; +use tower::discover::Change as TowerChange; -type DiscoverResult = Result, E>; +/// A change in the service set. +#[derive(Debug, Clone)] +pub enum Change { + /// A new service identified by key `K` was identified. + Insert(K, V), + /// The service identified by key `K` disappeared. + Remove(K), +} pub(crate) struct DynamicServiceStream { changes: Receiver>, @@ -24,25 +29,17 @@ } impl Stream for DynamicServiceStream { - type Item = DiscoverResult; + type Item = Result, crate::BoxError>; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let c = &mut self.changes; - match Pin::new(&mut *c).poll_recv(cx) { + match Pin::new(&mut self.changes).poll_recv(cx) { Poll::Pending | Poll::Ready(None) => Poll::Pending, Poll::Ready(Some(change)) => match change { Change::Insert(k, endpoint) => { - let mut http = HttpConnector::new(); - http.set_nodelay(endpoint.tcp_nodelay); - http.set_keepalive(endpoint.tcp_keepalive); - http.set_connect_timeout(endpoint.connect_timeout); - http.enforce_http(false); - - let connection = Connection::lazy(endpoint.connector(http), endpoint); - let change = Ok(Change::Insert(k, connection)); - Poll::Ready(Some(change)) + let connection = Connection::lazy(endpoint.http_connector(), endpoint); + Poll::Ready(Some(Ok(TowerChange::Insert(k, connection)))) } - Change::Remove(k) => Poll::Ready(Some(Ok(Change::Remove(k)))), + Change::Remove(k) => Poll::Ready(Some(Ok(TowerChange::Remove(k)))), }, } } diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/channel/service/mod.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/channel/service/mod.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/channel/service/mod.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/channel/service/mod.rs 2025-03-25 18:49:19.000000000 +0000 @@ -11,6 +11,7 @@ pub(super) use self::connection::Connection; mod discover; +pub use self::discover::Change; pub(super) use self::discover::DynamicServiceStream; mod io; @@ -22,7 +23,7 @@ mod executor; pub(super) use self::executor::{Executor, SharedExec}; -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] mod tls; -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] pub(super) use self::tls::TlsConnector; diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/channel/service/reconnect.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/channel/service/reconnect.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/channel/service/reconnect.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/channel/service/reconnect.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,4 +1,3 @@ -use crate::Error; use pin_project::pin_project; use std::fmt; use std::{ @@ -13,12 +12,12 @@ pub(crate) struct Reconnect where M: Service, - M::Error: Into, + M::Error: Into, { mk_service: M, state: State, target: Target, - error: Option, + error: Option, has_been_connected: bool, is_lazy: bool, } @@ -33,7 +32,7 @@ impl Reconnect where M: Service, - M::Error: Into, + M::Error: Into, { pub(crate) fn new(mk_service: M, target: Target, is_lazy: bool) -> Self { Reconnect { @@ -52,12 +51,12 @@ M: Service, S: Service, M::Future: Unpin, - Error: From + From, + crate::BoxError: From + From, Target: Clone, - >::Error: Into, + >::Error: Into, { type Response = S::Response; - type Error = Error; + type Error = crate::BoxError; type Future = ResponseFuture; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { @@ -160,7 +159,7 @@ M::Future: fmt::Debug, M::Response: fmt::Debug, Target: fmt::Debug, - >::Error: Into, + >::Error: Into, { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.debug_struct("Reconnect") @@ -183,7 +182,7 @@ #[derive(Debug)] enum Inner { Future(#[pin] F), - Error(Option), + Error(Option), } impl ResponseFuture { @@ -193,7 +192,7 @@ } } - pub(crate) fn error(error: crate::Error) -> Self { + pub(crate) fn error(error: crate::BoxError) -> Self { ResponseFuture { inner: Inner::Error(Some(error)), } @@ -203,9 +202,9 @@ impl Future for ResponseFuture where F: Future>, - E: Into, + E: Into, { - type Output = Result; + type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { //self.project().inner.poll(cx).map_err(Into::into) diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/channel/service/tls.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/channel/service/tls.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/channel/service/tls.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/channel/service/tls.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,19 +1,21 @@ use std::fmt; -use std::io::Cursor; use std::sync::Arc; use hyper_util::rt::TokioIo; use tokio::io::{AsyncRead, AsyncWrite}; use tokio_rustls::{ rustls::{ + crypto, pki_types::{ServerName, TrustAnchor}, - ClientConfig, RootCertStore, + ClientConfig, ConfigBuilder, RootCertStore, WantsVerifier, }, TlsConnector as RustlsConnector, }; use super::io::BoxedIo; -use crate::transport::service::tls::{add_certs_from_pem, load_identity, TlsError, ALPN_H2}; +use crate::transport::service::tls::{ + convert_certificate_to_pki_types, convert_identity_to_pki_types, TlsError, ALPN_H2, +}; use crate::transport::tls::{Certificate, Identity}; #[derive(Clone)] @@ -24,16 +26,36 @@ } impl TlsConnector { + #[allow(clippy::too_many_arguments)] pub(crate) fn new( ca_certs: Vec, trust_anchors: Vec>, identity: Option, domain: &str, assume_http2: bool, + use_key_log: bool, #[cfg(feature = "tls-native-roots")] with_native_roots: bool, #[cfg(feature = "tls-webpki-roots")] with_webpki_roots: bool, - ) -> Result { - let builder = ClientConfig::builder(); + ) -> Result { + fn with_provider( + provider: Arc, + ) -> ConfigBuilder { + ClientConfig::builder_with_provider(provider) + .with_safe_default_protocol_versions() + .unwrap() + } + + #[allow(unreachable_patterns)] + let builder = match crypto::CryptoProvider::get_default() { + Some(provider) => with_provider(provider.clone()), + #[cfg(feature = "tls-ring")] + None => with_provider(Arc::new(crypto::ring::default_provider())), + #[cfg(feature = "tls-aws-lc")] + None => with_provider(Arc::new(crypto::aws_lc_rs::default_provider())), + // somehow tls is enabled, but neither of the crypto features are enabled. + _ => ClientConfig::builder(), + }; + let mut roots = RootCertStore::from_iter(trust_anchors); #[cfg(feature = "tls-native-roots")] @@ -41,7 +63,7 @@ let rustls_native_certs::CertificateResult { certs, errors, .. } = rustls_native_certs::load_native_certs(); if !errors.is_empty() { - tracing::debug!("errors occured when loading native certs: {errors:?}"); + tracing::debug!("errors occurred when loading native certs: {errors:?}"); } if certs.is_empty() { return Err(TlsError::NativeCertsNotFound.into()); @@ -55,18 +77,22 @@ } for cert in ca_certs { - add_certs_from_pem(&mut Cursor::new(cert), &mut roots)?; + roots.add_parsable_certificates(convert_certificate_to_pki_types(&cert)?); } let builder = builder.with_root_certificates(roots); let mut config = match identity { Some(identity) => { - let (client_cert, client_key) = load_identity(identity)?; + let (client_cert, client_key) = convert_identity_to_pki_types(&identity)?; builder.with_client_auth_cert(client_cert, client_key)? } None => builder.with_no_client_auth(), }; + if use_key_log { + config.key_log = Arc::new(tokio_rustls::rustls::KeyLogFile::new()); + } + config.alpn_protocols.push(ALPN_H2.into()); Ok(Self { config: Arc::new(config), @@ -75,7 +101,7 @@ }) } - pub(crate) async fn connect(&self, io: I) -> Result + pub(crate) async fn connect(&self, io: I) -> Result where I: AsyncRead + AsyncWrite + Send + Unpin + 'static, { diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/channel/tls.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/channel/tls.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/channel/tls.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/channel/tls.rs 2025-03-25 18:49:19.000000000 +0000 @@ -18,6 +18,7 @@ with_native_roots: bool, #[cfg(feature = "tls-webpki-roots")] with_webpki_roots: bool, + use_key_log: bool, } impl ClientTlsConfig { @@ -84,6 +85,14 @@ } } + /// Use key log as specified by the `SSLKEYLOGFILE` environment variable. + pub fn use_key_log(self) -> Self { + ClientTlsConfig { + use_key_log: true, + ..self + } + } + /// Enables the platform's trusted certs. #[cfg(feature = "tls-native-roots")] pub fn with_native_roots(self) -> Self { @@ -112,7 +121,7 @@ config } - pub(crate) fn into_tls_connector(self, uri: &Uri) -> Result { + pub(crate) fn into_tls_connector(self, uri: &Uri) -> Result { let domain = match &self.domain { Some(domain) => domain, None => uri.host().ok_or_else(Error::new_invalid_uri)?, @@ -123,6 +132,7 @@ self.identity, domain, self.assume_http2, + self.use_key_log, #[cfg(feature = "tls-native-roots")] self.with_native_roots, #[cfg(feature = "tls-webpki-roots")] diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/channel/uds_connector.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/channel/uds_connector.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/channel/uds_connector.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/channel/uds_connector.rs 2025-03-25 18:49:19.000000000 +0000 @@ -0,0 +1,80 @@ +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; + +use http::Uri; +use hyper_util::rt::TokioIo; + +use tower::Service; + +use crate::status::ConnectError; + +#[cfg(not(target_os = "windows"))] +use tokio::net::UnixStream; + +#[cfg(not(target_os = "windows"))] +async fn connect_uds(uds_path: String) -> Result { + UnixStream::connect(uds_path) + .await + .map_err(|err| ConnectError(From::from(err))) +} + +// Dummy type that will allow us to compile and match trait bounds +// but is never used. +#[cfg(target_os = "windows")] +#[allow(dead_code)] +type UnixStream = tokio::io::DuplexStream; + +#[cfg(target_os = "windows")] +async fn connect_uds(_uds_path: String) -> Result { + Err(ConnectError( + "uds connections are not allowed on windows".into(), + )) +} + +pub(crate) struct UdsConnector { + uds_filepath: String, +} + +impl UdsConnector { + pub(crate) fn new(uds_filepath: &str) -> Self { + UdsConnector { + uds_filepath: uds_filepath.to_string(), + } + } +} + +impl Service for UdsConnector { + type Response = TokioIo; + type Error = ConnectError; + type Future = UdsConnecting; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, _: Uri) -> Self::Future { + let uds_path = self.uds_filepath.clone(); + let fut = async move { + let stream = connect_uds(uds_path).await?; + Ok(TokioIo::new(stream)) + }; + UdsConnecting { + inner: Box::pin(fut), + } + } +} + +type ConnectResult = Result, ConnectError>; + +pub(crate) struct UdsConnecting { + inner: Pin + Send>>, +} + +impl Future for UdsConnecting { + type Output = ConnectResult; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.get_mut().inner.as_mut().poll(cx) + } +} diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/error.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/error.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/error.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/error.rs 2025-03-25 18:49:19.000000000 +0000 @@ -19,6 +19,8 @@ InvalidUri, #[cfg(feature = "channel")] InvalidUserAgent, + #[cfg(all(feature = "_tls-any", feature = "channel"))] + InvalidTlsConfigForUds, } impl Error { @@ -33,7 +35,7 @@ self } - pub(crate) fn from_source(source: impl Into) -> Self { + pub(crate) fn from_source(source: impl Into) -> Self { Error::new(Kind::Transport).with(source) } @@ -54,6 +56,8 @@ Kind::InvalidUri => "invalid URI", #[cfg(feature = "channel")] Kind::InvalidUserAgent => "user agent is not a valid header value", + #[cfg(all(feature = "_tls-any", feature = "channel"))] + Kind::InvalidTlsConfigForUds => "cannot apply TLS config for unix domain socket", } } } diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/mod.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/mod.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/mod.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/mod.rs 2025-03-25 18:49:19.000000000 +0000 @@ -21,7 +21,6 @@ //! # #[cfg(feature = "rustls")] //! # use tonic::transport::{Channel, Certificate, ClientTlsConfig}; //! # use std::time::Duration; -//! # use tonic::body::BoxBody; //! # use tonic::client::GrpcService;; //! # use http::Request; //! # #[cfg(feature = "rustls")] @@ -49,20 +48,20 @@ //! # use std::convert::Infallible; //! # #[cfg(feature = "rustls")] //! # use tonic::transport::{Server, Identity, ServerTlsConfig}; -//! # use tonic::body::BoxBody; +//! # use tonic::body::Body; //! # use tower::Service; //! # #[cfg(feature = "rustls")] //! # async fn do_thing() -> Result<(), Box> { //! # #[derive(Clone)] //! # pub struct Svc; -//! # impl Service> for Svc { -//! # type Response = hyper::Response; +//! # impl Service> for Svc { +//! # type Response = hyper::Response; //! # type Error = Infallible; //! # type Future = std::future::Ready>; //! # fn poll_ready(&mut self, _cx: &mut std::task::Context<'_>) -> std::task::Poll> { //! # Ok(()).into() //! # } -//! # fn call(&mut self, _req: hyper::Request) -> Self::Future { +//! # fn call(&mut self, _req: hyper::Request) -> Self::Future { //! # unimplemented!() //! # } //! # } @@ -96,7 +95,7 @@ mod error; mod service; -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] mod tls; #[doc(inline)] @@ -106,18 +105,16 @@ #[doc(inline)] #[cfg(feature = "server")] pub use self::server::Server; -/// Deprecated. Please use [`crate::status::TimeoutExpired`] instead. -pub use crate::status::TimeoutExpired; -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] pub use self::tls::Certificate; pub use hyper::{body::Body, Uri}; -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] pub use tokio_rustls::rustls::pki_types::CertificateDer; -#[cfg(all(feature = "channel", feature = "tls"))] +#[cfg(all(feature = "channel", feature = "_tls-any"))] pub use self::channel::ClientTlsConfig; -#[cfg(all(feature = "server", feature = "tls"))] +#[cfg(all(feature = "server", feature = "_tls-any"))] pub use self::server::ServerTlsConfig; -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] pub use self::tls::Identity; diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/server/conn.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/server/conn.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/server/conn.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/server/conn.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,11 +1,11 @@ use std::net::SocketAddr; use tokio::net::TcpStream; -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] use std::sync::Arc; -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] use tokio_rustls::rustls::pki_types::CertificateDer; -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] use tokio_rustls::server::TlsStream; /// Trait that connected IO resources implement and use to produce info about the connection. @@ -102,7 +102,7 @@ fn connect_info(&self) -> Self::ConnectInfo {} } -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] impl Connected for TlsStream where T: Connected, @@ -128,14 +128,14 @@ /// See [`Connected`] for more details. /// /// [ext]: crate::Request::extensions -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] #[derive(Debug, Clone)] pub struct TlsConnectInfo { inner: T, certs: Option>>>, } -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] impl TlsConnectInfo { /// Get a reference to the underlying connection info. pub fn get_ref(&self) -> &T { diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/server/incoming.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/server/incoming.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/server/incoming.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/server/incoming.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,156 +1,15 @@ use std::{ - io, net::{SocketAddr, TcpListener as StdTcpListener}, - ops::ControlFlow, - pin::{pin, Pin}, - task::{ready, Context, Poll}, + pin::Pin, + task::{Context, Poll}, time::Duration, }; -use tokio::{ - io::{AsyncRead, AsyncWrite}, - net::{TcpListener, TcpStream}, -}; -use tokio_stream::wrappers::TcpListenerStream; -use tokio_stream::{Stream, StreamExt}; +use socket2::TcpKeepalive; +use tokio::net::{TcpListener, TcpStream}; +use tokio_stream::{wrappers::TcpListenerStream, Stream}; use tracing::warn; -use super::service::ServerIo; -#[cfg(feature = "tls")] -use super::service::TlsAcceptor; - -#[cfg(not(feature = "tls"))] -pub(crate) fn tcp_incoming( - incoming: impl Stream>, -) -> impl Stream, crate::Error>> -where - IO: AsyncRead + AsyncWrite + Unpin + Send + 'static, - IE: Into, -{ - async_stream::try_stream! { - let mut incoming = pin!(incoming); - - while let Some(item) = incoming.next().await { - yield match item { - Ok(_) => item.map(ServerIo::new_io)?, - Err(e) => match handle_accept_error(e) { - ControlFlow::Continue(()) => continue, - ControlFlow::Break(e) => Err(e)?, - } - } - } - } -} - -#[cfg(feature = "tls")] -pub(crate) fn tcp_incoming( - incoming: impl Stream>, - tls: Option, -) -> impl Stream, crate::Error>> -where - IO: AsyncRead + AsyncWrite + Unpin + Send + 'static, - IE: Into, -{ - async_stream::try_stream! { - let mut incoming = pin!(incoming); - - let mut tasks = tokio::task::JoinSet::new(); - - loop { - match select(&mut incoming, &mut tasks).await { - SelectOutput::Incoming(stream) => { - if let Some(tls) = &tls { - let tls = tls.clone(); - tasks.spawn(async move { - let io = tls.accept(stream).await?; - Ok(ServerIo::new_tls_io(io)) - }); - } else { - yield ServerIo::new_io(stream); - } - } - - SelectOutput::Io(io) => { - yield io; - } - - SelectOutput::Err(e) => match handle_accept_error(e) { - ControlFlow::Continue(()) => continue, - ControlFlow::Break(e) => Err(e)?, - } - - SelectOutput::Done => { - break; - } - } - } - } -} - -fn handle_accept_error(e: impl Into) -> ControlFlow { - let e = e.into(); - tracing::debug!(error = %e, "accept loop error"); - if let Some(e) = e.downcast_ref::() { - if matches!( - e.kind(), - io::ErrorKind::ConnectionAborted - | io::ErrorKind::ConnectionReset - | io::ErrorKind::BrokenPipe - | io::ErrorKind::Interrupted - | io::ErrorKind::InvalidData // Raised if TLS handshake failed - | io::ErrorKind::UnexpectedEof // Raised if TLS handshake failed - | io::ErrorKind::WouldBlock - ) { - return ControlFlow::Continue(()); - } - } - - ControlFlow::Break(e) -} - -#[cfg(feature = "tls")] -async fn select( - incoming: &mut (impl Stream> + Unpin), - tasks: &mut tokio::task::JoinSet, crate::Error>>, -) -> SelectOutput -where - IE: Into, -{ - if tasks.is_empty() { - return match incoming.try_next().await { - Ok(Some(stream)) => SelectOutput::Incoming(stream), - Ok(None) => SelectOutput::Done, - Err(e) => SelectOutput::Err(e.into()), - }; - } - - tokio::select! { - stream = incoming.try_next() => { - match stream { - Ok(Some(stream)) => SelectOutput::Incoming(stream), - Ok(None) => SelectOutput::Done, - Err(e) => SelectOutput::Err(e.into()), - } - } - - accept = tasks.join_next() => { - match accept.expect("JoinSet should never end") { - Ok(Ok(io)) => SelectOutput::Io(io), - Ok(Err(e)) => SelectOutput::Err(e), - Err(e) => SelectOutput::Err(e.into()), - } - } - } -} - -#[cfg(feature = "tls")] -enum SelectOutput { - Incoming(A), - Io(ServerIo), - Err(crate::Error), - Done, -} - /// Binds a socket address for a [Router](super::Router) /// /// An incoming stream, usable with [Router::serve_with_incoming](super::Router::serve_with_incoming), @@ -158,33 +17,33 @@ #[derive(Debug)] pub struct TcpIncoming { inner: TcpListenerStream, - nodelay: bool, - keepalive: Option, + nodelay: Option, + keepalive: Option, } impl TcpIncoming { - /// Creates an instance by binding (opening) the specified socket address - /// to which the specified TCP 'nodelay' and 'keepalive' parameters are applied. + /// Creates an instance by binding (opening) the specified socket address. + /// /// Returns a TcpIncoming if the socket address was successfully bound. /// /// # Examples /// ```no_run /// # use tower_service::Service; /// # use http::{request::Request, response::Response}; - /// # use tonic::{body::BoxBody, server::NamedService, transport::{Server, server::TcpIncoming}}; + /// # use tonic::{body::Body, server::NamedService, transport::{Server, server::TcpIncoming}}; /// # use core::convert::Infallible; /// # use std::error::Error; /// # fn main() { } // Cannot have type parameters, hence instead define: /// # fn run(some_service: S) -> Result<(), Box> /// # where - /// # S: Service, Response = Response, Error = Infallible> + NamedService + Clone + Send + 'static, + /// # S: Service, Response = Response, Error = Infallible> + NamedService + Clone + Send + Sync + 'static, /// # S::Future: Send + 'static, /// # { /// // Find a free port /// let mut port = 1322; /// let tinc = loop { /// let addr = format!("127.0.0.1:{}", port).parse().unwrap(); - /// match TcpIncoming::new(addr, true, None) { + /// match TcpIncoming::bind(addr) { /// Ok(t) => break t, /// Err(_) => port += 1 /// } @@ -194,64 +53,65 @@ /// .serve_with_incoming(tinc); /// # Ok(()) /// # } - pub fn new( - addr: SocketAddr, - nodelay: bool, - keepalive: Option, - ) -> Result { + pub fn bind(addr: SocketAddr) -> std::io::Result { let std_listener = StdTcpListener::bind(addr)?; std_listener.set_nonblocking(true)?; - let inner = TcpListenerStream::new(TcpListener::from_std(std_listener)?); - Ok(Self { - inner, - nodelay, - keepalive, - }) + Ok(TcpListener::from_std(std_listener)?.into()) } - /// Creates a new `TcpIncoming` from an existing `tokio::net::TcpListener`. - pub fn from_listener( - listener: TcpListener, - nodelay: bool, - keepalive: Option, - ) -> Result { - Ok(Self { + /// Sets the `TCP_NODELAY` option on the accepted connection. + pub fn with_nodelay(self, nodelay: Option) -> Self { + Self { nodelay, ..self } + } + + /// Sets the `TCP_KEEPALIVE` option on the accepted connection. + pub fn with_keepalive(self, keepalive: Option) -> Self { + let keepalive = keepalive.map(|t| TcpKeepalive::new().with_time(t)); + Self { keepalive, ..self } + } +} + +impl From for TcpIncoming { + fn from(listener: TcpListener) -> Self { + Self { inner: TcpListenerStream::new(listener), - nodelay, - keepalive, - }) + nodelay: None, + keepalive: None, + } } } impl Stream for TcpIncoming { - type Item = Result; + type Item = std::io::Result; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match ready!(Pin::new(&mut self.inner).poll_next(cx)) { - Some(Ok(stream)) => { - set_accepted_socket_options(&stream, self.nodelay, self.keepalive); - Some(Ok(stream)).into() - } - other => Poll::Ready(other), + let polled = Pin::new(&mut self.inner).poll_next(cx); + + if let Poll::Ready(Some(Ok(stream))) = &polled { + set_accepted_socket_options(stream, self.nodelay, &self.keepalive); } + + polled } } // Consistent with hyper-0.14, this function does not return an error. -fn set_accepted_socket_options(stream: &TcpStream, nodelay: bool, keepalive: Option) { - if nodelay { - if let Err(e) = stream.set_nodelay(true) { - warn!("error trying to set TCP nodelay: {}", e); +fn set_accepted_socket_options( + stream: &TcpStream, + nodelay: Option, + keepalive: &Option, +) { + if let Some(nodelay) = nodelay { + if let Err(e) = stream.set_nodelay(nodelay) { + warn!("error trying to set TCP_NODELAY: {e}"); } } - if let Some(timeout) = keepalive { + if let Some(keepalive) = keepalive { let sock_ref = socket2::SockRef::from(&stream); - let sock_keepalive = socket2::TcpKeepalive::new().with_time(timeout); - - if let Err(e) = sock_ref.set_tcp_keepalive(&sock_keepalive) { - warn!("error trying to set TCP keepalive: {}", e); + if let Err(e) = sock_ref.set_tcp_keepalive(keepalive) { + warn!("error trying to set TCP_KEEPALIVE: {e}"); } } } @@ -263,9 +123,9 @@ async fn one_tcpincoming_at_a_time() { let addr = "127.0.0.1:1322".parse().unwrap(); { - let _t1 = TcpIncoming::new(addr, true, None).unwrap(); - let _t2 = TcpIncoming::new(addr, true, None).unwrap_err(); + let _t1 = TcpIncoming::bind(addr).unwrap(); + let _t2 = TcpIncoming::bind(addr).unwrap_err(); } - let _t3 = TcpIncoming::new(addr, true, None).unwrap(); + let _t3 = TcpIncoming::bind(addr).unwrap(); } } diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/server/io_stream.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/server/io_stream.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/server/io_stream.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/server/io_stream.rs 2025-03-25 18:49:19.000000000 +0000 @@ -0,0 +1,183 @@ +#[cfg(feature = "_tls-any")] +use std::future::Future; +use std::{ + io, + ops::ControlFlow, + pin::{pin, Pin}, + task::{ready, Context, Poll}, +}; + +use pin_project::pin_project; +use tokio::io::{AsyncRead, AsyncWrite}; +#[cfg(feature = "_tls-any")] +use tokio::task::JoinSet; +use tokio_stream::Stream; +#[cfg(feature = "_tls-any")] +use tokio_stream::StreamExt as _; + +use super::service::ServerIo; +#[cfg(feature = "_tls-any")] +use super::service::TlsAcceptor; + +#[cfg(feature = "_tls-any")] +struct State(TlsAcceptor, JoinSet, crate::BoxError>>); + +#[pin_project] +pub(crate) struct ServerIoStream +where + S: Stream>, +{ + #[pin] + inner: S, + #[cfg(feature = "_tls-any")] + state: Option>, +} + +impl ServerIoStream +where + S: Stream>, +{ + pub(crate) fn new(incoming: S, #[cfg(feature = "_tls-any")] tls: Option) -> Self { + Self { + inner: incoming, + #[cfg(feature = "_tls-any")] + state: tls.map(|tls| State(tls, JoinSet::new())), + } + } + + fn poll_next_without_tls( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll, crate::BoxError>>> + where + IE: Into, + { + match ready!(self.as_mut().project().inner.poll_next(cx)) { + Some(Ok(io)) => Poll::Ready(Some(Ok(ServerIo::new_io(io)))), + Some(Err(e)) => match handle_tcp_accept_error(e) { + ControlFlow::Continue(()) => { + cx.waker().wake_by_ref(); + Poll::Pending + } + ControlFlow::Break(e) => Poll::Ready(Some(Err(e))), + }, + None => Poll::Ready(None), + } + } +} + +impl Stream for ServerIoStream +where + S: Stream>, + IO: AsyncRead + AsyncWrite + Unpin + Send + 'static, + IE: Into, +{ + type Item = Result, crate::BoxError>; + + #[cfg(not(feature = "_tls-any"))] + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_next_without_tls(cx) + } + + #[cfg(feature = "_tls-any")] + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut projected = self.as_mut().project(); + + let Some(State(tls, tasks)) = projected.state else { + return self.poll_next_without_tls(cx); + }; + + let select_output = ready!(pin!(select(&mut projected.inner, tasks)).poll(cx)); + + match select_output { + SelectOutput::Incoming(stream) => { + let tls = tls.clone(); + tasks.spawn(async move { + let io = tls.accept(stream).await?; + Ok(ServerIo::new_tls_io(io)) + }); + cx.waker().wake_by_ref(); + Poll::Pending + } + + SelectOutput::Io(io) => Poll::Ready(Some(Ok(io))), + + SelectOutput::TcpErr(e) => match handle_tcp_accept_error(e) { + ControlFlow::Continue(()) => { + cx.waker().wake_by_ref(); + Poll::Pending + } + ControlFlow::Break(e) => Poll::Ready(Some(Err(e))), + }, + + SelectOutput::TlsErr(e) => { + tracing::debug!(error = %e, "tls accept error"); + cx.waker().wake_by_ref(); + Poll::Pending + } + + SelectOutput::Done => Poll::Ready(None), + } + } +} + +fn handle_tcp_accept_error(e: impl Into) -> ControlFlow { + let e = e.into(); + tracing::debug!(error = %e, "accept loop error"); + if let Some(e) = e.downcast_ref::() { + if matches!( + e.kind(), + io::ErrorKind::ConnectionAborted + | io::ErrorKind::ConnectionReset + | io::ErrorKind::BrokenPipe + | io::ErrorKind::Interrupted + | io::ErrorKind::WouldBlock + | io::ErrorKind::TimedOut + ) { + return ControlFlow::Continue(()); + } + } + + ControlFlow::Break(e) +} + +#[cfg(feature = "_tls-any")] +async fn select( + incoming: &mut (impl Stream> + Unpin), + tasks: &mut JoinSet, crate::BoxError>>, +) -> SelectOutput +where + IE: Into, +{ + let incoming_stream_future = async { + match incoming.try_next().await { + Ok(Some(stream)) => SelectOutput::Incoming(stream), + Ok(None) => SelectOutput::Done, + Err(e) => SelectOutput::TcpErr(e.into()), + } + }; + + if tasks.is_empty() { + return incoming_stream_future.await; + } + + tokio::select! { + stream = incoming_stream_future => stream, + accept = tasks.join_next() => { + match accept.expect("JoinSet should never end") { + Ok(Ok(io)) => SelectOutput::Io(io), + Ok(Err(e)) => SelectOutput::TlsErr(e), + Err(e) => SelectOutput::TlsErr(e.into()), + } + } + } +} + +#[cfg(feature = "_tls-any")] +enum SelectOutput { + Incoming(A), + Io(ServerIo), + TcpErr(crate::BoxError), + TlsErr(crate::BoxError), + Done, +} diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/server/mod.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/server/mod.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/server/mod.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/server/mod.rs 2025-03-25 18:49:19.000000000 +0000 @@ -2,8 +2,9 @@ mod conn; mod incoming; +mod io_stream; mod service; -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] mod tls; #[cfg(unix)] mod unix; @@ -11,7 +12,11 @@ use tokio_stream::StreamExt as _; use tracing::{debug, trace}; -use crate::service::Routes; +#[cfg(feature = "router")] +use crate::{server::NamedService, service::Routes}; + +#[cfg(feature = "router")] +use std::convert::Infallible; pub use conn::{Connected, TcpConnectInfo}; use hyper_util::{ @@ -19,13 +24,13 @@ server::conn::auto::{Builder as ConnectionBuilder, HttpServerConnExec}, service::TowerToHyperService, }; -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] pub use tls::ServerTlsConfig; -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] pub use conn::TlsConnectInfo; -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] use self::service::TlsAcceptor; #[cfg(unix)] @@ -33,21 +38,19 @@ pub use incoming::TcpIncoming; -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] use crate::transport::Error; -use self::service::{RecoverError, ServerIo}; +use self::service::{ConnectInfoLayer, ServerIo}; use super::service::GrpcTimeout; -use crate::body::{boxed, BoxBody}; -use crate::server::NamedService; +use crate::body::Body; +use crate::service::RecoverErrorLayer; use bytes::Bytes; use http::{Request, Response}; use http_body_util::BodyExt; use hyper::{body::Incoming, service::Service as HyperService}; use pin_project::pin_project; -use std::future::pending; use std::{ - convert::Infallible, fmt, future::{self, poll_fn, Future}, marker::PhantomData, @@ -58,20 +61,19 @@ time::Duration, }; use tokio::io::{AsyncRead, AsyncWrite}; -use tokio::time::sleep; use tokio_stream::Stream; use tower::{ layer::util::{Identity, Stack}, layer::Layer, limit::concurrency::ConcurrencyLimitLayer, - util::{BoxCloneService, Either}, + util::BoxCloneService, Service, ServiceBuilder, ServiceExt, }; -type BoxService = tower::util::BoxCloneService, Response, crate::Error>; +type BoxService = tower::util::BoxCloneService, Response, crate::BoxError>; type TraceInterceptor = Arc) -> tracing::Span + Send + Sync + 'static>; -const DEFAULT_HTTP2_KEEPALIVE_TIMEOUT_SECS: u64 = 20; +const DEFAULT_HTTP2_KEEPALIVE_TIMEOUT: Duration = Duration::from_secs(20); /// A default batteries included `transport` server. /// @@ -86,7 +88,7 @@ trace_interceptor: Option, concurrency_limit: Option, timeout: Option, - #[cfg(feature = "tls")] + #[cfg(feature = "_tls-any")] tls: Option, init_stream_window_size: Option, init_connection_window_size: Option, @@ -94,7 +96,7 @@ tcp_keepalive: Option, tcp_nodelay: bool, http2_keepalive_interval: Option, - http2_keepalive_timeout: Option, + http2_keepalive_timeout: Duration, http2_adaptive_window: Option, http2_max_pending_accept_reset_streams: Option, http2_max_header_list_size: Option, @@ -110,7 +112,7 @@ trace_interceptor: None, concurrency_limit: None, timeout: None, - #[cfg(feature = "tls")] + #[cfg(feature = "_tls-any")] tls: None, init_stream_window_size: None, init_connection_window_size: None, @@ -118,7 +120,7 @@ tcp_keepalive: None, tcp_nodelay: false, http2_keepalive_interval: None, - http2_keepalive_timeout: None, + http2_keepalive_timeout: DEFAULT_HTTP2_KEEPALIVE_TIMEOUT, http2_adaptive_window: None, http2_max_pending_accept_reset_streams: None, http2_max_header_list_size: None, @@ -131,16 +133,13 @@ } /// A stack based [`Service`] router. +#[cfg(feature = "router")] #[derive(Debug)] pub struct Router { server: Server, routes: Routes, } -impl NamedService for Either { - const NAME: &'static str = S::NAME; -} - impl Server { /// Create a new server builder that can configure a [`Server`]. pub fn builder() -> Self { @@ -154,7 +153,7 @@ impl Server { /// Configure TLS for this server. - #[cfg(feature = "tls")] + #[cfg(feature = "_tls-any")] pub fn tls_config(self, tls_config: ServerTlsConfig) -> Result { Ok(Server { tls: Some(tls_config.tls_acceptor().map_err(Error::from_source)?), @@ -284,11 +283,11 @@ /// Default is 20 seconds. /// #[must_use] - pub fn http2_keepalive_timeout(self, http2_keepalive_timeout: Option) -> Self { - Server { - http2_keepalive_timeout, - ..self + pub fn http2_keepalive_timeout(mut self, http2_keepalive_timeout: Option) -> Self { + if let Some(timeout) = http2_keepalive_timeout { + self.http2_keepalive_timeout = timeout; } + self } /// Sets whether to use an adaptive flow control. Defaults to false. @@ -396,13 +395,16 @@ /// /// This will clone the `Server` builder and create a router that will /// route around different services. + #[cfg(feature = "router")] pub fn add_service(&mut self, svc: S) -> Router where - S: Service, Response = Response, Error = Infallible> + S: Service, Error = Infallible> + NamedService + Clone + Send + + Sync + 'static, + S::Response: axum::response::IntoResponse, S::Future: Send + 'static, L: Clone, { @@ -417,13 +419,16 @@ /// # Note /// Even when the argument given is `None` this will capture *all* requests to this service name. /// As a result, one cannot use this to toggle between two identically named implementations. + #[cfg(feature = "router")] pub fn add_optional_service(&mut self, svc: Option) -> Router where - S: Service, Response = Response, Error = Infallible> + S: Service, Error = Infallible> + NamedService + Clone + Send + + Sync + 'static, + S::Response: axum::response::IntoResponse, S::Future: Send + 'static, L: Clone, { @@ -435,6 +440,7 @@ /// /// This will clone the `Server` builder and create a router that will /// route around different services that were already added to the provided `routes`. + #[cfg(feature = "router")] pub fn add_routes(&mut self, routes: Routes) -> Router where L: Clone, @@ -469,7 +475,7 @@ /// # use tower_service::Service; /// use tower::ServiceBuilder; /// use std::time::Duration; - /// use tonic::{Request, Status, service::interceptor}; + /// use tonic::{Request, Status, service::InterceptorLayer}; /// /// fn auth_interceptor(request: Request<()>) -> Result, Status> { /// if valid_credentials(&request) { @@ -491,8 +497,8 @@ /// let layer = ServiceBuilder::new() /// .load_shed() /// .timeout(Duration::from_secs(30)) - /// .layer(interceptor(auth_interceptor)) - /// .layer(interceptor(some_other_interceptor)) + /// .layer(InterceptorLayer::new(auth_interceptor)) + /// .layer(InterceptorLayer::new(some_other_interceptor)) /// .into_inner(); /// /// Server::builder().layer(layer); @@ -509,7 +515,7 @@ trace_interceptor: self.trace_interceptor, concurrency_limit: self.concurrency_limit, timeout: self.timeout, - #[cfg(feature = "tls")] + #[cfg(feature = "_tls-any")] tls: self.tls, init_stream_window_size: self.init_stream_window_size, init_connection_window_size: self.init_connection_window_size, @@ -527,7 +533,96 @@ } } - pub(crate) async fn serve_with_shutdown( + fn bind_incoming(&self, addr: SocketAddr) -> Result { + Ok(TcpIncoming::bind(addr) + .map_err(super::Error::from_source)? + .with_nodelay(Some(self.tcp_nodelay)) + .with_keepalive(self.tcp_keepalive)) + } + + /// Serve the service. + pub async fn serve(self, addr: SocketAddr, svc: S) -> Result<(), super::Error> + where + L: Layer, + L::Service: Service, Response = Response> + Clone + Send + 'static, + <>::Service as Service>>::Future: Send, + <>::Service as Service>>::Error: + Into + Send + 'static, + ResBody: http_body::Body + Send + 'static, + ResBody::Error: Into, + { + let incoming = self.bind_incoming(addr)?; + self.serve_with_incoming(svc, incoming).await + } + + /// Serve the service with the shutdown signal. + pub async fn serve_with_shutdown( + self, + addr: SocketAddr, + svc: S, + signal: F, + ) -> Result<(), super::Error> + where + L: Layer, + L::Service: Service, Response = Response> + Clone + Send + 'static, + <>::Service as Service>>::Future: Send, + <>::Service as Service>>::Error: + Into + Send + 'static, + F: Future, + ResBody: http_body::Body + Send + 'static, + ResBody::Error: Into, + { + let incoming = self.bind_incoming(addr)?; + self.serve_with_incoming_shutdown(svc, incoming, signal) + .await + } + + /// Serve the service on the provided incoming stream. + pub async fn serve_with_incoming( + self, + svc: S, + incoming: I, + ) -> Result<(), super::Error> + where + L: Layer, + L::Service: Service, Response = Response> + Clone + Send + 'static, + <>::Service as Service>>::Future: Send, + <>::Service as Service>>::Error: + Into + Send + 'static, + I: Stream>, + IO: AsyncRead + AsyncWrite + Connected + Unpin + Send + 'static, + IE: Into, + ResBody: http_body::Body + Send + 'static, + ResBody::Error: Into, + { + self.serve_internal(svc, incoming, Option::>::None) + .await + } + + /// Serve the service with the signal on the provided incoming stream. + pub async fn serve_with_incoming_shutdown( + self, + svc: S, + incoming: I, + signal: F, + ) -> Result<(), super::Error> + where + L: Layer, + L::Service: Service, Response = Response> + Clone + Send + 'static, + <>::Service as Service>>::Future: Send, + <>::Service as Service>>::Error: + Into + Send + 'static, + I: Stream>, + IO: AsyncRead + AsyncWrite + Connected + Unpin + Send + 'static, + IE: Into, + F: Future, + ResBody: http_body::Body + Send + 'static, + ResBody::Error: Into, + { + self.serve_internal(svc, incoming, Some(signal)).await + } + + async fn serve_internal( self, svc: S, incoming: I, @@ -535,18 +630,16 @@ ) -> Result<(), super::Error> where L: Layer, - L::Service: - Service, Response = Response> + Clone + Send + 'static, - <>::Service as Service>>::Future: Send + 'static, - <>::Service as Service>>::Error: - Into + Send + 'static, + L::Service: Service, Response = Response> + Clone + Send + 'static, + <>::Service as Service>>::Future: Send, + <>::Service as Service>>::Error: + Into + Send + 'static, I: Stream>, IO: AsyncRead + AsyncWrite + Connected + Unpin + Send + 'static, - IO::ConnectInfo: Clone + Send + Sync + 'static, - IE: Into, + IE: Into, F: Future, ResBody: http_body::Body + Send + 'static, - ResBody::Error: Into, + ResBody::Error: Into, { let trace_interceptor = self.trace_interceptor.clone(); let concurrency_limit = self.concurrency_limit; @@ -559,18 +652,16 @@ let http2_only = !self.accept_http1; let http2_keepalive_interval = self.http2_keepalive_interval; - let http2_keepalive_timeout = self - .http2_keepalive_timeout - .unwrap_or_else(|| Duration::new(DEFAULT_HTTP2_KEEPALIVE_TIMEOUT_SECS, 0)); + let http2_keepalive_timeout = self.http2_keepalive_timeout; let http2_adaptive_window = self.http2_adaptive_window; let http2_max_pending_accept_reset_streams = self.http2_max_pending_accept_reset_streams; let max_connection_age = self.max_connection_age; let svc = self.service_builder.service(svc); - let incoming = incoming::tcp_incoming( + let incoming = io_stream::ServerIoStream::new( incoming, - #[cfg(feature = "tls")] + #[cfg(feature = "_tls-any")] self.tls, ); let mut svc = MakeSvc { @@ -644,7 +735,7 @@ .map_err(super::Error::from_source)?; let hyper_io = TokioIo::new(io); - let hyper_svc = TowerToHyperService::new(req_svc.map_request(|req: Request| req.map(boxed))); + let hyper_svc = TowerToHyperService::new(req_svc.map_request(|req: Request| req.map(Body::new))); serve_connection(hyper_io, hyper_svc, server.clone(), graceful.then(|| signal_rx.clone()), max_connection_age); } @@ -693,8 +784,7 @@ let mut conn = pin!(builder.serve_connection(hyper_io, hyper_svc)); - let sleep = sleep_or_pending(max_connection_age); - tokio::pin!(sleep); + let mut sleep = pin!(sleep_or_pending(max_connection_age)); loop { tokio::select! { @@ -722,26 +812,30 @@ async fn sleep_or_pending(wait_for: Option) { match wait_for { - Some(wait) => sleep(wait).await, - None => pending().await, + Some(wait) => tokio::time::sleep(wait).await, + None => future::pending().await, }; } +#[cfg(feature = "router")] impl Router { pub(crate) fn new(server: Server, routes: Routes) -> Self { Self { server, routes } } } +#[cfg(feature = "router")] impl Router { /// Add a new service to this router. pub fn add_service(mut self, svc: S) -> Self where - S: Service, Response = Response, Error = Infallible> + S: Service, Error = Infallible> + NamedService + Clone + Send + + Sync + 'static, + S::Response: axum::response::IntoResponse, S::Future: Send + 'static, { self.routes = self.routes.add_service(svc); @@ -753,14 +847,15 @@ /// # Note /// Even when the argument given is `None` this will capture *all* requests to this service name. /// As a result, one cannot use this to toggle between two identically named implementations. - #[allow(clippy::type_complexity)] pub fn add_optional_service(mut self, svc: Option) -> Self where - S: Service, Response = Response, Error = Infallible> + S: Service, Error = Infallible> + NamedService + Clone + Send + + Sync + 'static, + S::Response: axum::response::IntoResponse, S::Future: Send + 'static, { if let Some(svc) = svc { @@ -769,12 +864,6 @@ self } - /// Convert this tonic `Router` into an axum `Router` consuming the tonic one. - #[deprecated(since = "0.12.2", note = "Use `Routes::into_axum_router` instead.")] - pub fn into_router(self) -> axum::Router { - self.routes.into_axum_router() - } - /// Consume this [`Server`] creating a future that will execute the server /// on [tokio]'s default executor. /// @@ -783,23 +872,14 @@ pub async fn serve(self, addr: SocketAddr) -> Result<(), super::Error> where L: Layer + Clone, - L::Service: - Service, Response = Response> + Clone + Send + 'static, - <>::Service as Service>>::Future: Send + 'static, - <>::Service as Service>>::Error: - Into + Send, + L::Service: Service, Response = Response> + Clone + Send + 'static, + <>::Service as Service>>::Future: Send, + <>::Service as Service>>::Error: + Into + Send, ResBody: http_body::Body + Send + 'static, - ResBody::Error: Into, + ResBody::Error: Into, { - let incoming = TcpIncoming::new(addr, self.server.tcp_nodelay, self.server.tcp_keepalive) - .map_err(super::Error::from_source)?; - self.server - .serve_with_shutdown::<_, _, future::Ready<()>, _, _, ResBody>( - self.routes.prepare(), - incoming, - None, - ) - .await + self.server.serve(addr, self.routes.prepare()).await } /// Consume this [`Server`] creating a future that will execute the server @@ -815,18 +895,15 @@ ) -> Result<(), super::Error> where L: Layer, - L::Service: - Service, Response = Response> + Clone + Send + 'static, - <>::Service as Service>>::Future: Send + 'static, - <>::Service as Service>>::Error: - Into + Send, + L::Service: Service, Response = Response> + Clone + Send + 'static, + <>::Service as Service>>::Future: Send, + <>::Service as Service>>::Error: + Into + Send, ResBody: http_body::Body + Send + 'static, - ResBody::Error: Into, + ResBody::Error: Into, { - let incoming = TcpIncoming::new(addr, self.server.tcp_nodelay, self.server.tcp_keepalive) - .map_err(super::Error::from_source)?; self.server - .serve_with_shutdown(self.routes.prepare(), incoming, Some(signal)) + .serve_with_shutdown(addr, self.routes.prepare(), signal) .await } @@ -843,23 +920,18 @@ where I: Stream>, IO: AsyncRead + AsyncWrite + Connected + Unpin + Send + 'static, - IO::ConnectInfo: Clone + Send + Sync + 'static, - IE: Into, + IE: Into, L: Layer, - L::Service: - Service, Response = Response> + Clone + Send + 'static, - <>::Service as Service>>::Future: Send + 'static, - <>::Service as Service>>::Error: - Into + Send, + + L::Service: Service, Response = Response> + Clone + Send + 'static, + <>::Service as Service>>::Future: Send, + <>::Service as Service>>::Error: + Into + Send, ResBody: http_body::Body + Send + 'static, - ResBody::Error: Into, + ResBody::Error: Into, { self.server - .serve_with_shutdown::<_, _, future::Ready<()>, _, _, ResBody>( - self.routes.prepare(), - incoming, - None, - ) + .serve_with_incoming(self.routes.prepare(), incoming) .await } @@ -879,37 +951,20 @@ where I: Stream>, IO: AsyncRead + AsyncWrite + Connected + Unpin + Send + 'static, - IO::ConnectInfo: Clone + Send + Sync + 'static, - IE: Into, + IE: Into, F: Future, L: Layer, - L::Service: - Service, Response = Response> + Clone + Send + 'static, - <>::Service as Service>>::Future: Send + 'static, - <>::Service as Service>>::Error: - Into + Send, + L::Service: Service, Response = Response> + Clone + Send + 'static, + <>::Service as Service>>::Future: Send, + <>::Service as Service>>::Error: + Into + Send, ResBody: http_body::Body + Send + 'static, - ResBody::Error: Into, + ResBody::Error: Into, { self.server - .serve_with_shutdown(self.routes.prepare(), incoming, Some(signal)) + .serve_with_incoming_shutdown(self.routes.prepare(), incoming, signal) .await } - - /// Create a tower service out of a router. - pub fn into_service(self) -> L::Service - where - L: Layer, - L::Service: - Service, Response = Response> + Clone + Send + 'static, - <>::Service as Service>>::Future: Send + 'static, - <>::Service as Service>>::Error: - Into + Send, - ResBody: http_body::Body + Send + 'static, - ResBody::Error: Into, - { - self.server.service_builder.service(self.routes.prepare()) - } } impl fmt::Debug for Server { @@ -924,22 +979,22 @@ trace_interceptor: Option, } -impl Service> for Svc +impl Service> for Svc where - S: Service, Response = Response>, - S::Error: Into, + S: Service, Response = Response>, + S::Error: Into, ResBody: http_body::Body + Send + 'static, - ResBody::Error: Into, + ResBody::Error: Into, { - type Response = Response; - type Error = crate::Error; + type Response = Response; + type Error = crate::BoxError; type Future = SvcFuture; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.inner.poll_ready(cx).map_err(Into::into) } - fn call(&mut self, mut req: Request) -> Self::Future { + fn call(&mut self, mut req: Request) -> Self::Future { let span = if let Some(trace_interceptor) = &self.trace_interceptor { let (parts, body) = req.into_parts(); let bodyless_request = Request::from_parts(parts, ()); @@ -971,18 +1026,18 @@ impl Future for SvcFuture where F: Future, E>>, - E: Into, + E: Into, ResBody: http_body::Body + Send + 'static, - ResBody::Error: Into, + ResBody::Error: Into, { - type Output = Result, crate::Error>; + type Output = Result, crate::BoxError>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); let _guard = this.span.enter(); let response: Response = ready!(this.inner.poll(cx)).map_err(Into::into)?; - let response = response.map(|body| boxed(body.map_err(Into::into))); + let response = response.map(|body| Body::new(body.map_err(Into::into))); Poll::Ready(Ok(response)) } } @@ -1004,15 +1059,15 @@ impl Service<&ServerIo> for MakeSvc where - IO: Connected, - S: Service, Response = Response> + Clone + Send + 'static, - S::Future: Send + 'static, - S::Error: Into + Send, + IO: Connected + 'static, + S: Service, Response = Response> + Clone + Send + 'static, + S::Future: Send, + S::Error: Into + Send, ResBody: http_body::Body + Send + 'static, - ResBody::Error: Into, + ResBody::Error: Into, { type Response = BoxService; - type Error = crate::Error; + type Error = crate::BoxError; type Future = future::Ready>; fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { @@ -1028,36 +1083,14 @@ let trace_interceptor = self.trace_interceptor.clone(); let svc = ServiceBuilder::new() - .layer_fn(RecoverError::new) + .layer(RecoverErrorLayer::new()) .option_layer(concurrency_limit.map(ConcurrencyLimitLayer::new)) .layer_fn(|s| GrpcTimeout::new(s, timeout)) .service(svc); let svc = ServiceBuilder::new() .layer(BoxCloneService::layer()) - .map_request(move |mut request: Request| { - match &conn_info { - tower::util::Either::A(inner) => { - request.extensions_mut().insert(inner.clone()); - } - tower::util::Either::B(inner) => { - #[cfg(feature = "tls")] - { - request.extensions_mut().insert(inner.clone()); - request.extensions_mut().insert(inner.get_ref().clone()); - } - - #[cfg(not(feature = "tls"))] - { - // just a type check to make sure we didn't forget to - // insert this into the extensions - let _: &() = inner; - } - } - } - - request - }) + .layer(ConnectInfoLayer::new(conn_info.clone())) .service(Svc { inner: svc, trace_interceptor, diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/server/service/io.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/server/service/io.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/server/service/io.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/server/service/io.rs 2025-03-25 18:49:19.000000000 +0000 @@ -4,30 +4,104 @@ use std::pin::Pin; use std::task::{Context, Poll}; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] use tokio_rustls::server::TlsStream; +use tower_layer::Layer; +use tower_service::Service; + +#[derive(Debug, Clone)] +pub(crate) struct ConnectInfoLayer { + connect_info: T, +} + +impl ConnectInfoLayer { + pub(crate) fn new(connect_info: T) -> Self { + Self { connect_info } + } +} + +impl Layer for ConnectInfoLayer +where + T: Clone, +{ + type Service = ConnectInfo; + + fn layer(&self, inner: S) -> Self::Service { + ConnectInfo::new(inner, self.connect_info.clone()) + } +} + +#[derive(Debug, Clone)] +pub(crate) struct ConnectInfo { + inner: S, + connect_info: T, +} + +impl ConnectInfo { + fn new(inner: S, connect_info: T) -> Self { + Self { + inner, + connect_info, + } + } +} + +impl Service> for ConnectInfo> +where + S: Service>, + IO: Connected, +{ + type Response = S::Response; + type Error = S::Error; + type Future = S::Future; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_ready(cx) + } + + fn call(&mut self, mut req: http::Request) -> Self::Future { + match self.connect_info.clone() { + ServerIoConnectInfo::Io(inner) => { + req.extensions_mut().insert(inner); + } + #[cfg(feature = "_tls-any")] + ServerIoConnectInfo::TlsIo(inner) => { + req.extensions_mut().insert(inner.get_ref().clone()); + req.extensions_mut().insert(inner); + } + } + self.inner.call(req) + } +} pub(crate) enum ServerIo { Io(IO), - #[cfg(feature = "tls")] + #[cfg(feature = "_tls-any")] TlsIo(Box>), } -use tower::util::Either; - -#[cfg(feature = "tls")] -type ServerIoConnectInfo = - Either<::ConnectInfo, as Connected>::ConnectInfo>; +pub(crate) enum ServerIoConnectInfo { + Io(::ConnectInfo), + #[cfg(feature = "_tls-any")] + TlsIo( as Connected>::ConnectInfo), +} -#[cfg(not(feature = "tls"))] -type ServerIoConnectInfo = Either<::ConnectInfo, ()>; +impl Clone for ServerIoConnectInfo { + fn clone(&self) -> Self { + match self { + Self::Io(io) => Self::Io(io.clone()), + #[cfg(feature = "_tls-any")] + Self::TlsIo(io) => Self::TlsIo(io.clone()), + } + } +} impl ServerIo { pub(in crate::transport) fn new_io(io: IO) -> Self { Self::Io(io) } - #[cfg(feature = "tls")] + #[cfg(feature = "_tls-any")] pub(in crate::transport) fn new_tls_io(io: TlsStream) -> Self { Self::TlsIo(Box::new(io)) } @@ -37,9 +111,9 @@ IO: Connected, { match self { - Self::Io(io) => Either::A(io.connect_info()), - #[cfg(feature = "tls")] - Self::TlsIo(io) => Either::B(io.connect_info()), + Self::Io(io) => ServerIoConnectInfo::Io(io.connect_info()), + #[cfg(feature = "_tls-any")] + Self::TlsIo(io) => ServerIoConnectInfo::TlsIo(io.connect_info()), } } } @@ -55,7 +129,7 @@ ) -> Poll> { match &mut *self { Self::Io(io) => Pin::new(io).poll_read(cx, buf), - #[cfg(feature = "tls")] + #[cfg(feature = "_tls-any")] Self::TlsIo(io) => Pin::new(io).poll_read(cx, buf), } } @@ -72,7 +146,7 @@ ) -> Poll> { match &mut *self { Self::Io(io) => Pin::new(io).poll_write(cx, buf), - #[cfg(feature = "tls")] + #[cfg(feature = "_tls-any")] Self::TlsIo(io) => Pin::new(io).poll_write(cx, buf), } } @@ -80,7 +154,7 @@ fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match &mut *self { Self::Io(io) => Pin::new(io).poll_flush(cx), - #[cfg(feature = "tls")] + #[cfg(feature = "_tls-any")] Self::TlsIo(io) => Pin::new(io).poll_flush(cx), } } @@ -88,7 +162,7 @@ fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match &mut *self { Self::Io(io) => Pin::new(io).poll_shutdown(cx), - #[cfg(feature = "tls")] + #[cfg(feature = "_tls-any")] Self::TlsIo(io) => Pin::new(io).poll_shutdown(cx), } } @@ -100,7 +174,7 @@ ) -> Poll> { match &mut *self { Self::Io(io) => Pin::new(io).poll_write_vectored(cx, bufs), - #[cfg(feature = "tls")] + #[cfg(feature = "_tls-any")] Self::TlsIo(io) => Pin::new(io).poll_write_vectored(cx, bufs), } } @@ -108,7 +182,7 @@ fn is_write_vectored(&self) -> bool { match self { Self::Io(io) => io.is_write_vectored(), - #[cfg(feature = "tls")] + #[cfg(feature = "_tls-any")] Self::TlsIo(io) => io.is_write_vectored(), } } diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/server/service/mod.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/server/service/mod.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/server/service/mod.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/server/service/mod.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,10 +1,7 @@ mod io; -pub(crate) use self::io::ServerIo; +pub(crate) use self::io::{ConnectInfoLayer, ServerIo}; -mod recover_error; -pub(crate) use self::recover_error::RecoverError; - -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] mod tls; -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] pub(crate) use self::tls::TlsAcceptor; diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/server/service/recover_error.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/server/service/recover_error.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/server/service/recover_error.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/server/service/recover_error.rs 1970-01-01 00:00:00.000000000 +0000 @@ -1,125 +0,0 @@ -use crate::Status; -use http::Response; -use http_body::Frame; -use pin_project::pin_project; -use std::{ - future::Future, - pin::Pin, - task::{ready, Context, Poll}, -}; -use tower::Service; - -/// Middleware that attempts to recover from service errors by turning them into a response built -/// from the `Status`. -#[derive(Debug, Clone)] -pub(crate) struct RecoverError { - inner: S, -} - -impl RecoverError { - pub(crate) fn new(inner: S) -> Self { - Self { inner } - } -} - -impl Service for RecoverError -where - S: Service>, - S::Error: Into, -{ - type Response = Response>; - type Error = crate::Error; - type Future = ResponseFuture; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - self.inner.poll_ready(cx).map_err(Into::into) - } - - fn call(&mut self, req: R) -> Self::Future { - ResponseFuture { - inner: self.inner.call(req), - } - } -} - -#[pin_project] -pub(crate) struct ResponseFuture { - #[pin] - inner: F, -} - -impl Future for ResponseFuture -where - F: Future, E>>, - E: Into, -{ - type Output = Result>, crate::Error>; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let result: Result, crate::Error> = - ready!(self.project().inner.poll(cx)).map_err(Into::into); - - match result { - Ok(response) => { - let response = response.map(MaybeEmptyBody::full); - Poll::Ready(Ok(response)) - } - Err(err) => match Status::try_from_error(err) { - Ok(status) => { - let mut res = Response::new(MaybeEmptyBody::empty()); - status.add_header(res.headers_mut()).unwrap(); - Poll::Ready(Ok(res)) - } - Err(err) => Poll::Ready(Err(err)), - }, - } - } -} - -#[pin_project] -pub(crate) struct MaybeEmptyBody { - #[pin] - inner: Option, -} - -impl MaybeEmptyBody { - fn full(inner: B) -> Self { - Self { inner: Some(inner) } - } - - fn empty() -> Self { - Self { inner: None } - } -} - -impl http_body::Body for MaybeEmptyBody -where - B: http_body::Body + Send, -{ - type Data = B::Data; - type Error = B::Error; - - fn poll_frame( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll, Self::Error>>> { - match self.project().inner.as_pin_mut() { - Some(b) => b.poll_frame(cx), - None => Poll::Ready(None), - } - } - - fn is_end_stream(&self) -> bool { - match &self.inner { - Some(b) => b.is_end_stream(), - None => true, - } - } - - fn size_hint(&self) -> http_body::SizeHint { - match &self.inner { - Some(body) => body.size_hint(), - None => http_body::SizeHint::with_exact(0), - } - } -} diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/server/service/tls.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/server/service/tls.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/server/service/tls.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/server/service/tls.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,4 +1,4 @@ -use std::{fmt, io::Cursor, sync::Arc}; +use std::{fmt, sync::Arc}; use tokio::io::{AsyncRead, AsyncWrite}; use tokio_rustls::{ @@ -8,7 +8,7 @@ }; use crate::transport::{ - service::tls::{add_certs_from_pem, load_identity, ALPN_H2}, + service::tls::{convert_certificate_to_pki_types, convert_identity_to_pki_types, ALPN_H2}, Certificate, Identity, }; @@ -19,17 +19,19 @@ impl TlsAcceptor { pub(crate) fn new( - identity: Identity, - client_ca_root: Option, + identity: &Identity, + client_ca_root: Option<&Certificate>, client_auth_optional: bool, - ) -> Result { + ignore_client_order: bool, + use_key_log: bool, + ) -> Result { let builder = ServerConfig::builder(); let builder = match client_ca_root { None => builder.with_no_client_auth(), Some(cert) => { let mut roots = RootCertStore::empty(); - add_certs_from_pem(&mut Cursor::new(cert), &mut roots)?; + roots.add_parsable_certificates(convert_certificate_to_pki_types(cert)?); let verifier = if client_auth_optional { WebPkiClientVerifier::builder(roots.into()).allow_unauthenticated() } else { @@ -40,8 +42,13 @@ } }; - let (cert, key) = load_identity(identity)?; + let (cert, key) = convert_identity_to_pki_types(identity)?; let mut config = builder.with_single_cert(cert, key)?; + config.ignore_client_order = ignore_client_order; + + if use_key_log { + config.key_log = Arc::new(tokio_rustls::rustls::KeyLogFile::new()); + } config.alpn_protocols.push(ALPN_H2.into()); Ok(Self { @@ -49,7 +56,7 @@ }) } - pub(crate) async fn accept(&self, io: IO) -> Result, crate::Error> + pub(crate) async fn accept(&self, io: IO) -> Result, crate::BoxError> where IO: AsyncRead + AsyncWrite + Unpin, { diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/server/tls.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/server/tls.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/server/tls.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/server/tls.rs 2025-03-25 18:49:19.000000000 +0000 @@ -9,6 +9,8 @@ identity: Option, client_ca_root: Option, client_auth_optional: bool, + ignore_client_order: bool, + use_key_log: bool, } impl fmt::Debug for ServerTlsConfig { @@ -20,11 +22,7 @@ impl ServerTlsConfig { /// Creates a new `ServerTlsConfig`. pub fn new() -> Self { - ServerTlsConfig { - identity: None, - client_ca_root: None, - client_auth_optional: false, - } + ServerTlsConfig::default() } /// Sets the [`Identity`] of the server. @@ -56,11 +54,32 @@ } } - pub(crate) fn tls_acceptor(&self) -> Result { + /// Sets whether the server's cipher preferences are followed instead of the client's. + /// + /// # Default + /// By default, this option is set to `false`. + pub fn ignore_client_order(self, ignore_client_order: bool) -> Self { + ServerTlsConfig { + ignore_client_order, + ..self + } + } + + /// Use key log as specified by the `SSLKEYLOGFILE` environment variable. + pub fn use_key_log(self) -> Self { + ServerTlsConfig { + use_key_log: true, + ..self + } + } + + pub(crate) fn tls_acceptor(&self) -> Result { TlsAcceptor::new( - self.identity.clone().unwrap(), - self.client_ca_root.clone(), + self.identity.as_ref().unwrap(), + self.client_ca_root.as_ref(), self.client_auth_optional, + self.ignore_client_order, + self.use_key_log, ) } } diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/server/unix.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/server/unix.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/server/unix.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/server/unix.rs 2025-03-25 18:49:19.000000000 +0000 @@ -9,7 +9,6 @@ /// See [Connected] for more details. /// /// [ext]: crate::Request::extensions -/// [Connected]: crate::transport::server::Connected #[derive(Clone, Debug)] pub struct UdsConnectInfo { /// Peer address. This will be "unnamed" for client unix sockets. diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/service/grpc_timeout.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/service/grpc_timeout.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/service/grpc_timeout.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/service/grpc_timeout.rs 2025-03-25 18:49:19.000000000 +0000 @@ -28,10 +28,10 @@ impl Service> for GrpcTimeout where S: Service>, - S::Error: Into, + S::Error: Into, { type Response = S::Response; - type Error = crate::Error; + type Error = crate::BoxError; type Future = ResponseFuture; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { @@ -73,15 +73,15 @@ impl Future for ResponseFuture where F: Future>, - E: Into, + E: Into, { - type Output = Result; + type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); - if let Poll::Ready(result) = this.inner.poll(cx) { - return Poll::Ready(result.map_err(Into::into)); + if let ready @ Poll::Ready(_) = this.inner.poll(cx) { + return ready.map_err(Into::into); } if let Some(sleep) = this.sleep.as_pin_mut() { @@ -103,47 +103,46 @@ fn try_parse_grpc_timeout( headers: &HeaderMap, ) -> Result, &HeaderValue> { - match headers.get(GRPC_TIMEOUT_HEADER) { - Some(val) => { - let (timeout_value, timeout_unit) = val - .to_str() - .map_err(|_| val) - .and_then(|s| if s.is_empty() { Err(val) } else { Ok(s) })? - // `HeaderValue::to_str` only returns `Ok` if the header contains ASCII so this - // `split_at` will never panic from trying to split in the middle of a character. - // See https://docs.rs/http/0.2.4/http/header/struct.HeaderValue.html#method.to_str - // - // `len - 1` also wont panic since we just checked `s.is_empty`. - .split_at(val.len() - 1); - - // gRPC spec specifies `TimeoutValue` will be at most 8 digits - // Caping this at 8 digits also prevents integer overflow from ever occurring - if timeout_value.len() > 8 { - return Err(val); - } + let Some(val) = headers.get(GRPC_TIMEOUT_HEADER) else { + return Ok(None); + }; + + let (timeout_value, timeout_unit) = val + .to_str() + .map_err(|_| val) + .and_then(|s| if s.is_empty() { Err(val) } else { Ok(s) })? + // `HeaderValue::to_str` only returns `Ok` if the header contains ASCII so this + // `split_at` will never panic from trying to split in the middle of a character. + // See https://docs.rs/http/1/http/header/struct.HeaderValue.html#method.to_str + // + // `len - 1` also wont panic since we just checked `s.is_empty`. + .split_at(val.len() - 1); + + // gRPC spec specifies `TimeoutValue` will be at most 8 digits + // Caping this at 8 digits also prevents integer overflow from ever occurring + if timeout_value.len() > 8 { + return Err(val); + } - let timeout_value: u64 = timeout_value.parse().map_err(|_| val)?; + let timeout_value: u64 = timeout_value.parse().map_err(|_| val)?; - let duration = match timeout_unit { - // Hours - "H" => Duration::from_secs(timeout_value * SECONDS_IN_HOUR), - // Minutes - "M" => Duration::from_secs(timeout_value * SECONDS_IN_MINUTE), - // Seconds - "S" => Duration::from_secs(timeout_value), - // Milliseconds - "m" => Duration::from_millis(timeout_value), - // Microseconds - "u" => Duration::from_micros(timeout_value), - // Nanoseconds - "n" => Duration::from_nanos(timeout_value), - _ => return Err(val), - }; + let duration = match timeout_unit { + // Hours + "H" => Duration::from_secs(timeout_value * SECONDS_IN_HOUR), + // Minutes + "M" => Duration::from_secs(timeout_value * SECONDS_IN_MINUTE), + // Seconds + "S" => Duration::from_secs(timeout_value), + // Milliseconds + "m" => Duration::from_millis(timeout_value), + // Microseconds + "u" => Duration::from_micros(timeout_value), + // Nanoseconds + "n" => Duration::from_nanos(timeout_value), + _ => return Err(val), + }; - Ok(Some(duration)) - } - None => Ok(None), - } + Ok(Some(duration)) } #[cfg(test)] diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/service/mod.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/service/mod.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/service/mod.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/service/mod.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,5 +1,5 @@ pub(crate) mod grpc_timeout; -#[cfg(feature = "tls")] +#[cfg(feature = "_tls-any")] pub(crate) mod tls; pub(crate) use self::grpc_timeout::GrpcTimeout; diff -Nru rust-tonic-0.12.3+dfsg/tonic/src/transport/service/tls.rs rust-tonic-0.13.0+dfsg/tonic/src/transport/service/tls.rs --- rust-tonic-0.12.3+dfsg/tonic/src/transport/service/tls.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic/src/transport/service/tls.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,11 +1,8 @@ use std::{fmt, io::Cursor}; -use tokio_rustls::rustls::{ - pki_types::{CertificateDer, PrivateKeyDer}, - RootCertStore, -}; +use tokio_rustls::rustls::pki_types::{pem::PemObject as _, CertificateDer, PrivateKeyDer}; -use crate::transport::Identity; +use crate::transport::{Certificate, Identity}; /// h2 alpn in plain format for rustls. pub(crate) const ALPN_H2: &[u8] = b"h2"; @@ -38,29 +35,19 @@ impl std::error::Error for TlsError {} -pub(crate) fn load_identity( - identity: Identity, -) -> Result<(Vec>, PrivateKeyDer<'static>), TlsError> { - let cert = rustls_pemfile::certs(&mut Cursor::new(identity.cert)) +pub(crate) fn convert_certificate_to_pki_types( + certificate: &Certificate, +) -> Result>, TlsError> { + CertificateDer::pem_reader_iter(&mut Cursor::new(certificate)) .collect::, _>>() - .map_err(|_| TlsError::CertificateParseError)?; - - let Ok(Some(key)) = rustls_pemfile::private_key(&mut Cursor::new(identity.key)) else { - return Err(TlsError::PrivateKeyParseError); - }; - - Ok((cert, key)) + .map_err(|_| TlsError::CertificateParseError) } -pub(crate) fn add_certs_from_pem( - mut certs: &mut dyn std::io::BufRead, - roots: &mut RootCertStore, -) -> Result<(), crate::Error> { - for cert in rustls_pemfile::certs(&mut certs).collect::, _>>()? { - roots - .add(cert) - .map_err(|_| TlsError::CertificateParseError)?; - } - - Ok(()) +pub(crate) fn convert_identity_to_pki_types( + identity: &Identity, +) -> Result<(Vec>, PrivateKeyDer<'static>), TlsError> { + let cert = convert_certificate_to_pki_types(&identity.cert)?; + let key = PrivateKeyDer::from_pem_reader(&mut Cursor::new(&identity.key)) + .map_err(|_| TlsError::PrivateKeyParseError)?; + Ok((cert, key)) } diff -Nru rust-tonic-0.12.3+dfsg/tonic-build/Cargo.toml rust-tonic-0.13.0+dfsg/tonic-build/Cargo.toml --- rust-tonic-0.12.3+dfsg/tonic-build/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-build/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -4,7 +4,6 @@ description = """ Codegen module of `tonic` gRPC implementation. """ -documentation = "https://docs.rs/tonic-build/0.12.3" edition = "2021" homepage = "https://github.com/hyperium/tonic" keywords = ["rpc", "grpc", "async", "codegen", "protobuf"] @@ -12,7 +11,8 @@ name = "tonic-build" readme = "README.md" repository = "https://github.com/hyperium/tonic" -version = "0.12.3" +version = "0.13.0" +rust-version = { workspace = true } [dependencies] prettyplease = { version = "0.2" } @@ -25,9 +25,12 @@ [features] default = ["transport", "prost"] prost = ["prost-build", "dep:prost-types"] -cleanup-markdown = ["prost", "prost-build/cleanup-markdown"] +cleanup-markdown = ["prost-build?/cleanup-markdown"] transport = [] +[lints] +workspace = true + [package.metadata.docs.rs] all-features = true diff -Nru rust-tonic-0.12.3+dfsg/tonic-build/src/client.rs rust-tonic-0.13.0+dfsg/tonic-build/src/client.rs --- rust-tonic-0.12.3+dfsg/tonic-build/src/client.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-build/src/client.rs 2025-03-25 18:49:19.000000000 +0000 @@ -67,7 +67,7 @@ impl #service_ident where - T: tonic::client::GrpcService, + T: tonic::client::GrpcService, T::Error: Into, T::ResponseBody: Body + std::marker::Send + 'static, ::Error: Into + std::marker::Send, @@ -87,10 +87,10 @@ F: tonic::service::Interceptor, T::ResponseBody: Default, T: tonic::codegen::Service< - http::Request, - Response = http::Response<>::ResponseBody> + http::Request, + Response = http::Response<>::ResponseBody> >, - >>::Error: Into + std::marker::Send + std::marker::Sync, + >>::Error: Into + std::marker::Send + std::marker::Sync, { #service_ident::new(InterceptedService::new(inner, interceptor)) } diff -Nru rust-tonic-0.12.3+dfsg/tonic-build/src/code_gen.rs rust-tonic-0.13.0+dfsg/tonic-build/src/code_gen.rs --- rust-tonic-0.12.3+dfsg/tonic-build/src/code_gen.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-build/src/code_gen.rs 2025-03-25 18:49:19.000000000 +0000 @@ -46,7 +46,7 @@ self } - /// Enable compiling well knonw types, this will force codegen to not + /// Enable compiling well known types, this will force codegen to not /// use the well known types from `prost-types`. pub fn compile_well_known_types(&mut self, enable: bool) -> &mut Self { self.compile_well_known_types = enable; diff -Nru rust-tonic-0.12.3+dfsg/tonic-build/src/lib.rs rust-tonic-0.13.0+dfsg/tonic-build/src/lib.rs --- rust-tonic-0.12.3+dfsg/tonic-build/src/lib.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-build/src/lib.rs 2025-03-25 18:49:19.000000000 +0000 @@ -4,7 +4,8 @@ //! # Feature flags //! //! - `cleanup-markdown`: Enables cleaning up documentation from the generated code. Useful -//! when documentation of the generated code fails `cargo test --doc` for example. +//! when documentation of the generated code fails `cargo test --doc` for example. The +//! `prost` feature must be enabled to use this feature. //! - `prost`: Enables usage of prost generator (enabled by default). //! - `transport`: Enables generation of `connect` method using `tonic::transport::Channel` //! (enabled by default). @@ -60,17 +61,9 @@ //! fails with `No such file or directory` error. #![recursion_limit = "256"] -#![warn( - missing_debug_implementations, - missing_docs, - rust_2018_idioms, - unreachable_pub -)] #![doc( html_logo_url = "https://raw.githubusercontent.com/tokio-rs/website/master/public/img/icons/tonic.svg" )] -#![deny(rustdoc::broken_intra_doc_links)] -#![doc(html_root_url = "https://docs.rs/tonic-build/0.12.3")] #![doc(issue_tracker_base_url = "https://github.com/hyperium/tonic/issues/")] #![doc(test(no_crate_inject, attr(deny(rust_2018_idioms))))] #![cfg_attr(docsrs, feature(doc_auto_cfg))] diff -Nru rust-tonic-0.12.3+dfsg/tonic-build/src/prost.rs rust-tonic-0.13.0+dfsg/tonic-build/src/prost.rs --- rust-tonic-0.12.3+dfsg/tonic-build/src/prost.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-build/src/prost.rs 2025-03-25 18:49:19.000000000 +0000 @@ -172,7 +172,7 @@ let convert_type = |proto_type: &str, rust_type: &str| -> TokenStream { if (is_google_type(proto_type) && !compile_well_known_types) || rust_type.starts_with("::") - || NON_PATH_TYPE_ALLOWLIST.iter().any(|ty| *ty == rust_type) + || NON_PATH_TYPE_ALLOWLIST.contains(&rust_type) { rust_type.parse::().unwrap() } else if rust_type.starts_with("crate::") { @@ -601,28 +601,6 @@ } /// Compile the .proto files and execute code generation. - #[deprecated(since = "0.12.3", note = "renamed to `compile_protos()`")] - pub fn compile( - self, - protos: &[impl AsRef], - includes: &[impl AsRef], - ) -> io::Result<()> { - self.compile_protos(protos, includes) - } - - /// Compile the .proto files and execute code generation using a custom - /// `prost_build::Config`. The provided config will be updated with this builder's config. - #[deprecated(since = "0.12.3", note = "renamed to `compile_protos_with_config()`")] - pub fn compile_with_config( - self, - config: Config, - protos: &[impl AsRef], - includes: &[impl AsRef], - ) -> io::Result<()> { - self.compile_protos_with_config(config, protos, includes) - } - - /// Compile the .proto files and execute code generation. pub fn compile_protos( self, protos: &[impl AsRef], diff -Nru rust-tonic-0.12.3+dfsg/tonic-build/src/server.rs rust-tonic-0.13.0+dfsg/tonic-build/src/server.rs --- rust-tonic-0.12.3+dfsg/tonic-build/src/server.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-build/src/server.rs 2025-03-25 18:49:19.000000000 +0000 @@ -152,7 +152,7 @@ B: Body + std::marker::Send + 'static, B::Error: Into + std::marker::Send + 'static, { - type Response = http::Response; + type Response = http::Response; type Error = std::convert::Infallible; type Future = BoxFuture; @@ -165,7 +165,7 @@ #methods _ => Box::pin(async move { - let mut response = http::Response::new(empty_body()); + let mut response = http::Response::new(tonic::body::Body::default()); let headers = response.headers_mut(); headers.insert(tonic::Status::GRPC_STATUS, (tonic::Code::Unimplemented as i32).into()); headers.insert(http::header::CONTENT_TYPE, tonic::metadata::GRPC_CONTENT_TYPE); diff -Nru rust-tonic-0.12.3+dfsg/tonic-health/Cargo.toml rust-tonic-0.13.0+dfsg/tonic-health/Cargo.toml --- rust-tonic-0.12.3+dfsg/tonic-health/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-health/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -4,7 +4,6 @@ description = """ Health Checking module of `tonic` gRPC implementation. """ -documentation = "https://docs.rs/tonic-health/0.12.3" edition = "2021" homepage = "https://github.com/hyperium/tonic" keywords = ["rpc", "grpc", "async", "healthcheck"] @@ -12,23 +11,21 @@ name = "tonic-health" readme = "README.md" repository = "https://github.com/hyperium/tonic" -version = "0.12.3" - -[features] -default = ["transport"] -transport = [] +version = "0.13.0" +rust-version = { workspace = true } [dependencies] -async-stream = "0.3" prost = "0.13" tokio = {version = "1.0", features = ["sync"]} -tokio-stream = "0.1" -tonic = { version = "0.12", path = "../tonic", default-features = false, features = ["codegen", "prost"] } +tokio-stream = {version = "0.1", default-features = false, features = ["sync"]} +tonic = { version = "0.13.0", path = "../tonic", default-features = false, features = ["codegen", "prost"] } [dev-dependencies] tokio = {version = "1.0", features = ["rt-multi-thread", "macros"]} -tokio-stream = "0.1" -prost-types = "0.13" +prost-types = "0.13.0" + +[lints] +workspace = true [package.metadata.cargo_check_external_types] allowed_external_types = [ @@ -43,6 +40,5 @@ "prost::*", "futures_core::stream::Stream", - "http_body_util::combinators::box_body::UnsyncBoxBody", "tower_service::Service", ] diff -Nru rust-tonic-0.12.3+dfsg/tonic-health/src/lib.rs rust-tonic-0.13.0+dfsg/tonic-health/src/lib.rs --- rust-tonic-0.12.3+dfsg/tonic-health/src/lib.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-health/src/lib.rs 2025-03-25 18:49:19.000000000 +0000 @@ -6,17 +6,9 @@ //! //! [here]: https://github.com/hyperium/tonic/blob/master/examples/src/health/server.rs -#![warn( - missing_debug_implementations, - missing_docs, - rust_2018_idioms, - unreachable_pub -)] #![doc( html_logo_url = "https://raw.githubusercontent.com/tokio-rs/website/master/public/img/icons/tonic.svg" )] -#![deny(rustdoc::broken_intra_doc_links)] -#![doc(html_root_url = "https://docs.rs/tonic-health/0.12.3")] #![doc(issue_tracker_base_url = "https://github.com/hyperium/tonic/issues/")] #![doc(test(no_crate_inject, attr(deny(rust_2018_idioms))))] #![cfg_attr(docsrs, feature(doc_auto_cfg))] @@ -28,9 +20,10 @@ #![allow(missing_docs)] #[rustfmt::skip] pub mod grpc_health_v1; + #[rustfmt::skip] + pub mod grpc_health_v1_fds; - /// Byte encoded FILE_DESCRIPTOR_SET. - pub const FILE_DESCRIPTOR_SET: &[u8] = include_bytes!("generated/grpc_health_v1.bin"); + pub use grpc_health_v1_fds::FILE_DESCRIPTOR_SET; #[cfg(test)] mod tests { diff -Nru rust-tonic-0.12.3+dfsg/tonic-health/src/server.rs rust-tonic-0.13.0+dfsg/tonic-health/src/server.rs --- rust-tonic-0.12.3+dfsg/tonic-health/src/server.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-health/src/server.rs 2025-03-25 18:49:19.000000000 +0000 @@ -4,13 +4,11 @@ use crate::pb::{HealthCheckRequest, HealthCheckResponse}; use crate::ServingStatus; use std::collections::HashMap; -use std::pin::Pin; +use std::fmt; use std::sync::Arc; use tokio::sync::{watch, RwLock}; use tokio_stream::Stream; -#[cfg(feature = "transport")] -use tonic::server::NamedService; -use tonic::{Request, Response, Status}; +use tonic::{server::NamedService, Request, Response, Status}; /// Creates a `HealthReporter` and a linked `HealthServer` pair. Together, /// these types can be used to serve the gRPC Health Checking service. @@ -50,8 +48,7 @@ /// Sets the status of the service implemented by `S` to `Serving`. This notifies any watchers /// if there is a change in status. - #[cfg(feature = "transport")] - pub async fn set_serving(&mut self) + pub async fn set_serving(&self) where S: NamedService, { @@ -62,8 +59,7 @@ /// Sets the status of the service implemented by `S` to `NotServing`. This notifies any watchers /// if there is a change in status. - #[cfg(feature = "transport")] - pub async fn set_not_serving(&mut self) + pub async fn set_not_serving(&self) where S: NamedService, { @@ -74,7 +70,7 @@ /// Sets the status of the service with `service_name` to `status`. This notifies any watchers /// if there is a change in status. - pub async fn set_service_status(&mut self, service_name: S, status: ServingStatus) + pub async fn set_service_status(&self, service_name: S, status: ServingStatus) where S: AsRef, { @@ -125,42 +121,64 @@ request: Request, ) -> Result, Status> { let service_name = request.get_ref().service.as_str(); - let status = self.service_health(service_name).await; + let Some(status) = self.service_health(service_name).await else { + return Err(Status::not_found("service not registered")); + }; - match status { - None => Err(Status::not_found("service not registered")), - Some(status) => Ok(Response::new(HealthCheckResponse { - status: crate::pb::health_check_response::ServingStatus::from(status) as i32, - })), - } + Ok(Response::new(HealthCheckResponse::new(status))) } - type WatchStream = - Pin> + Send + 'static>>; + type WatchStream = WatchStream; async fn watch( &self, request: Request, ) -> Result, Status> { let service_name = request.get_ref().service.as_str(); - let mut status_rx = match self.statuses.read().await.get(service_name) { + let status_rx = match self.statuses.read().await.get(service_name) { + Some((_tx, rx)) => rx.clone(), None => return Err(Status::not_found("service not registered")), - Some(pair) => pair.1.clone(), }; - let output = async_stream::try_stream! { - // yield the current value - let status = crate::pb::health_check_response::ServingStatus::from(*status_rx.borrow()) as i32; - yield HealthCheckResponse { status }; - - #[allow(clippy::redundant_pattern_matching)] - while let Ok(_) = status_rx.changed().await { - let status = crate::pb::health_check_response::ServingStatus::from(*status_rx.borrow()) as i32; - yield HealthCheckResponse { status }; - } - }; + Ok(Response::new(WatchStream::new(status_rx))) + } +} + +/// A watch stream for the health service. +pub struct WatchStream { + inner: tokio_stream::wrappers::WatchStream, +} + +impl WatchStream { + fn new(status_rx: watch::Receiver) -> Self { + let inner = tokio_stream::wrappers::WatchStream::new(status_rx); + Self { inner } + } +} + +impl Stream for WatchStream { + type Item = Result; + + fn poll_next( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + std::pin::Pin::new(&mut self.inner) + .poll_next(cx) + .map(|opt| opt.map(|status| Ok(HealthCheckResponse::new(status)))) + } +} + +impl fmt::Debug for WatchStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("WatchStream").finish() + } +} - Ok(Response::new(Box::pin(output) as Self::WatchStream)) +impl HealthCheckResponse { + fn new(status: ServingStatus) -> Self { + let status = crate::pb::health_check_response::ServingStatus::from(status) as i32; + Self { status } } } @@ -202,7 +220,7 @@ #[tokio::test] async fn test_service_check() { - let (mut reporter, service) = make_test_service().await; + let (reporter, service) = make_test_service().await; // Overall server health let resp = service diff -Nru rust-tonic-0.12.3+dfsg/tonic-reflection/Cargo.toml rust-tonic-0.13.0+dfsg/tonic-reflection/Cargo.toml --- rust-tonic-0.12.3+dfsg/tonic-reflection/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-reflection/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -9,31 +9,34 @@ """ edition = "2021" homepage = "https://github.com/hyperium/tonic" -documentation = "https://docs.rs/tonic-reflection/0.12.3" keywords = ["rpc", "grpc", "async", "reflection"] license = "MIT" name = "tonic-reflection" readme = "README.md" repository = "https://github.com/hyperium/tonic" -version = "0.12.3" +version = "0.13.0" +rust-version = { workspace = true } [package.metadata.docs.rs] all-features = true -rustdoc-args = ["--cfg", "docsrs"] [features] -server = ["prost-types", "dep:tokio", "dep:tokio-stream"] +server = ["dep:prost-types", "dep:tokio", "dep:tokio-stream"] default = ["server"] [dependencies] prost = "0.13" prost-types = {version = "0.13", optional = true} tokio = { version = "1.0", features = ["sync", "rt"], optional = true } -tokio-stream = {version = "0.1", features = ["net"], optional = true } -tonic = { version = "0.12", path = "../tonic", default-features = false, features = ["codegen", "prost"] } +tokio-stream = {version = "0.1", default-features = false, optional = true } +tonic = { version = "0.13.0", path = "../tonic", default-features = false, features = ["codegen", "prost"] } [dev-dependencies] -tonic = { version = "0.12", path = "../tonic", default-features = false, features = ["transport"] } +tokio-stream = {version = "0.1", default-features = false, features = ["net"]} +tonic = { version = "0.13.0", path = "../tonic", default-features = false, features = ["transport"] } + +[lints] +workspace = true [package.metadata.cargo_check_external_types] allowed_external_types = [ @@ -49,6 +52,5 @@ "prost_types::*", "futures_core::stream::Stream", - "http_body_util::combinators::box_body::UnsyncBoxBody", "tower_service::Service", ] diff -Nru rust-tonic-0.12.3+dfsg/tonic-reflection/src/lib.rs rust-tonic-0.13.0+dfsg/tonic-reflection/src/lib.rs --- rust-tonic-0.12.3+dfsg/tonic-reflection/src/lib.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-reflection/src/lib.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,16 +1,8 @@ //! A `tonic` based gRPC Server Reflection implementation. -#![warn( - missing_debug_implementations, - missing_docs, - rust_2018_idioms, - unreachable_pub -)] #![doc( html_logo_url = "https://github.com/hyperium/tonic/raw/master/.github/assets/tonic-docs.png" )] -#![deny(rustdoc::broken_intra_doc_links)] -#![doc(html_root_url = "https://docs.rs/tonic-reflection/0.12.3")] #![doc(issue_tracker_base_url = "https://github.com/hyperium/tonic/issues/")] #![doc(test(no_crate_inject, attr(deny(rust_2018_idioms))))] #![cfg_attr(docsrs, feature(doc_auto_cfg))] @@ -26,12 +18,14 @@ #[rustfmt::skip] pub mod grpc_reflection_v1; - /// Byte encoded FILE_DESCRIPTOR_SET. - pub const FILE_DESCRIPTOR_SET_V1ALPHA: &[u8] = - include_bytes!("generated/reflection_v1alpha1.bin"); + #[rustfmt::skip] + pub mod reflection_v1_fds; + + #[rustfmt::skip] + pub mod reflection_v1alpha1_fds; - /// Byte encoded FILE_DESCRIPTOR_SET. - pub const FILE_DESCRIPTOR_SET_V1: &[u8] = include_bytes!("generated/reflection_v1.bin"); + pub use reflection_v1_fds::FILE_DESCRIPTOR_SET as FILE_DESCRIPTOR_SET_V1; + pub use reflection_v1alpha1_fds::FILE_DESCRIPTOR_SET as FILE_DESCRIPTOR_SET_V1ALPHA; #[cfg(test)] mod tests { diff -Nru rust-tonic-0.12.3+dfsg/tonic-reflection/src/server/mod.rs rust-tonic-0.13.0+dfsg/tonic-reflection/src/server/mod.rs --- rust-tonic-0.12.3+dfsg/tonic-reflection/src/server/mod.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-reflection/src/server/mod.rs 2025-03-25 18:49:19.000000000 +0000 @@ -11,8 +11,6 @@ /// v1 interface for the gRPC Reflection Service server. pub mod v1; -/// Deprecated; access these via `v1` instead. -pub use v1::{ServerReflection, ServerReflectionServer}; // For backwards compatibility /// v1alpha interface for the gRPC Reflection Service server. pub mod v1alpha; @@ -76,12 +74,6 @@ } /// Build a v1 gRPC Reflection Service to be served via Tonic. - #[deprecated(since = "0.12.2", note = "use `build_v1()` instead")] - pub fn build(self) -> Result, Error> { - self.build_v1() - } - - /// Build a v1 gRPC Reflection Service to be served via Tonic. pub fn build_v1( mut self, ) -> Result, Error> { diff -Nru rust-tonic-0.12.3+dfsg/tonic-reflection/src/server/v1alpha.rs rust-tonic-0.13.0+dfsg/tonic-reflection/src/server/v1alpha.rs --- rust-tonic-0.12.3+dfsg/tonic-reflection/src/server/v1alpha.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-reflection/src/server/v1alpha.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,7 +1,7 @@ -use std::sync::Arc; +use std::{fmt, sync::Arc}; use tokio::sync::mpsc; -use tokio_stream::{wrappers::ReceiverStream, StreamExt}; +use tokio_stream::{Stream, StreamExt}; use tonic::{Request, Response, Status, Streaming}; use super::ReflectionServiceState; @@ -13,14 +13,15 @@ ServerReflectionResponse, ServiceResponse, }; +/// An implementation for `ServerReflection`. #[derive(Debug)] -pub(super) struct ReflectionService { +pub struct ReflectionService { state: Arc, } #[tonic::async_trait] impl ServerReflection for ReflectionService { - type ServerReflectionInfoStream = ReceiverStream>; + type ServerReflectionInfoStream = ServerReflectionInfoStream; async fn server_reflection_info( &self, @@ -91,7 +92,7 @@ } }); - Ok(Response::new(ReceiverStream::new(resp_rx))) + Ok(Response::new(ServerReflectionInfoStream::new(resp_rx))) } } @@ -102,3 +103,36 @@ } } } + +/// A response stream. +pub struct ServerReflectionInfoStream { + inner: tokio_stream::wrappers::ReceiverStream>, +} + +impl ServerReflectionInfoStream { + fn new(resp_rx: mpsc::Receiver>) -> Self { + let inner = tokio_stream::wrappers::ReceiverStream::new(resp_rx); + Self { inner } + } +} + +impl Stream for ServerReflectionInfoStream { + type Item = Result; + + fn poll_next( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + std::pin::Pin::new(&mut self.inner).poll_next(cx) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +impl fmt::Debug for ServerReflectionInfoStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("ServerReflectionInfoStream").finish() + } +} diff -Nru rust-tonic-0.12.3+dfsg/tonic-reflection/src/server/v1.rs rust-tonic-0.13.0+dfsg/tonic-reflection/src/server/v1.rs --- rust-tonic-0.12.3+dfsg/tonic-reflection/src/server/v1.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-reflection/src/server/v1.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,7 +1,7 @@ -use std::sync::Arc; +use std::{fmt, sync::Arc}; use tokio::sync::mpsc; -use tokio_stream::{wrappers::ReceiverStream, StreamExt}; +use tokio_stream::{Stream, StreamExt}; use tonic::{Request, Response, Status, Streaming}; use super::ReflectionServiceState; @@ -13,14 +13,15 @@ ServerReflectionResponse, ServiceResponse, }; +/// An implementation for `ServerReflection`. #[derive(Debug)] -pub(super) struct ReflectionService { +pub struct ReflectionService { state: Arc, } #[tonic::async_trait] impl ServerReflection for ReflectionService { - type ServerReflectionInfoStream = ReceiverStream>; + type ServerReflectionInfoStream = ServerReflectionInfoStream; async fn server_reflection_info( &self, @@ -91,7 +92,7 @@ } }); - Ok(Response::new(ReceiverStream::new(resp_rx))) + Ok(Response::new(ServerReflectionInfoStream::new(resp_rx))) } } @@ -102,3 +103,36 @@ } } } + +/// A response stream. +pub struct ServerReflectionInfoStream { + inner: tokio_stream::wrappers::ReceiverStream>, +} + +impl ServerReflectionInfoStream { + fn new(resp_rx: mpsc::Receiver>) -> Self { + let inner = tokio_stream::wrappers::ReceiverStream::new(resp_rx); + Self { inner } + } +} + +impl Stream for ServerReflectionInfoStream { + type Item = Result; + + fn poll_next( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + std::pin::Pin::new(&mut self.inner).poll_next(cx) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +impl fmt::Debug for ServerReflectionInfoStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("ServerReflectionInfoStream").finish() + } +} diff -Nru rust-tonic-0.12.3+dfsg/tonic-reflection/tests/server.rs rust-tonic-0.13.0+dfsg/tonic-reflection/tests/server.rs --- rust-tonic-0.12.3+dfsg/tonic-reflection/tests/server.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-reflection/tests/server.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,3 +1,5 @@ +#![allow(missing_docs)] + use prost::Message; use std::net::SocketAddr; use tokio::sync::oneshot; diff -Nru rust-tonic-0.12.3+dfsg/tonic-reflection/tests/versions.rs rust-tonic-0.13.0+dfsg/tonic-reflection/tests/versions.rs --- rust-tonic-0.12.3+dfsg/tonic-reflection/tests/versions.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-reflection/tests/versions.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,3 +1,5 @@ +#![allow(missing_docs)] + use std::net::SocketAddr; use tokio::sync::oneshot; diff -Nru rust-tonic-0.12.3+dfsg/tonic-types/Cargo.toml rust-tonic-0.13.0+dfsg/tonic-types/Cargo.toml --- rust-tonic-0.12.3+dfsg/tonic-types/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-types/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -7,7 +7,6 @@ description = """ A collection of useful protobuf types that can be used with `tonic`. """ -documentation = "https://docs.rs/tonic-types/0.12.3" edition = "2021" homepage = "https://github.com/hyperium/tonic" keywords = ["rpc", "grpc", "protobuf"] @@ -15,12 +14,16 @@ name = "tonic-types" readme = "README.md" repository = "https://github.com/hyperium/tonic" -version = "0.12.3" +version = "0.13.0" +rust-version = { workspace = true } [dependencies] prost = "0.13" prost-types = "0.13" -tonic = { version = "0.12", path = "../tonic", default-features = false } +tonic = { version = "0.13.0", path = "../tonic", default-features = false } + +[lints] +workspace = true [package.metadata.cargo_check_external_types] allowed_external_types = [ diff -Nru rust-tonic-0.12.3+dfsg/tonic-types/README.md rust-tonic-0.13.0+dfsg/tonic-types/README.md --- rust-tonic-0.12.3+dfsg/tonic-types/README.md 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-types/README.md 2025-03-25 18:49:19.000000000 +0000 @@ -17,17 +17,9 @@ [`tonic::Status`] that can be used by a tonic client to extract error details, and handle them with ease. -## Getting Started - -```toml -[dependencies] -tonic = -tonic-types = -``` - ## Examples -The examples bellow cover a basic use case of the [gRPC Richer Error Model]. +The examples below cover a basic use case of the [gRPC Richer Error Model]. More complete server and client implementations are provided in the **Richer Error example**, located in the main repo [examples] directory. diff -Nru rust-tonic-0.12.3+dfsg/tonic-types/src/lib.rs rust-tonic-0.13.0+dfsg/tonic-types/src/lib.rs --- rust-tonic-0.12.3+dfsg/tonic-types/src/lib.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-types/src/lib.rs 2025-03-25 18:49:19.000000000 +0000 @@ -25,7 +25,7 @@ //! //! # Examples //! -//! The examples bellow cover a basic use case of the [gRPC Richer Error Model]. +//! The examples below cover a basic use case of the [gRPC Richer Error Model]. //! More complete server and client implementations are provided in the //! **Richer Error example**, located in the main repo [examples] directory. //! @@ -140,17 +140,9 @@ //! [error_details.proto]: https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto //! [Richer Error example]: https://github.com/hyperium/tonic/tree/master/examples/src/richer-error -#![warn( - missing_debug_implementations, - missing_docs, - rust_2018_idioms, - unreachable_pub -)] #![doc( html_logo_url = "https://raw.githubusercontent.com/tokio-rs/website/master/public/img/icons/tonic.svg" )] -#![deny(rustdoc::broken_intra_doc_links)] -#![doc(html_root_url = "https://docs.rs/tonic-types/0.12.3")] #![doc(issue_tracker_base_url = "https://github.com/hyperium/tonic/issues/")] mod generated { @@ -158,9 +150,10 @@ #![allow(rustdoc::invalid_html_tags)] #[rustfmt::skip] pub mod google_rpc; + #[rustfmt::skip] + pub mod types_fds; - /// Byte encoded FILE_DESCRIPTOR_SET. - pub const FILE_DESCRIPTOR_SET: &[u8] = include_bytes!("generated/types.bin"); + pub use types_fds::FILE_DESCRIPTOR_SET; #[cfg(test)] mod tests { diff -Nru rust-tonic-0.12.3+dfsg/tonic-web/Cargo.toml rust-tonic-0.13.0+dfsg/tonic-web/Cargo.toml --- rust-tonic-0.12.3+dfsg/tonic-web/Cargo.toml 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-web/Cargo.toml 2025-03-25 18:49:19.000000000 +0000 @@ -4,7 +4,6 @@ description = """ grpc-web protocol translation for tonic services. """ -documentation = "https://docs.rs/tonic-web/0.12.3" edition = "2021" homepage = "https://github.com/hyperium/tonic" keywords = ["rpc", "grpc", "grpc-web"] @@ -12,24 +11,27 @@ name = "tonic-web" readme = "README.md" repository = "https://github.com/hyperium/tonic" -version = "0.12.3" +version = "0.13.0" +rust-version = { workspace = true } [dependencies] base64 = "0.22" bytes = "1" -tokio-stream = "0.1" +tokio-stream = { version = "0.1", default-features = false } http = "1" http-body = "1" -http-body-util = "0.1" pin-project = "1" -tonic = { version = "0.12", path = "../tonic", default-features = false } +tonic = { version = "0.13.0", path = "../tonic", default-features = false } tower-service = "0.3" tower-layer = "0.3" -tower-http = { version = "0.5", features = ["cors"] } tracing = "0.1" [dev-dependencies] tokio = { version = "1", features = ["macros", "rt"] } +tower-http = { version = "0.6", features = ["cors"] } + +[lints] +workspace = true [package.metadata.cargo_check_external_types] allowed_external_types = [ @@ -42,8 +44,6 @@ # not major released "futures_core::stream::Stream", - "http_body_util::combinators::box_body::UnsyncBoxBody", - "tower_http::cors::Cors", "tower_layer::Layer", "tower_service::Service", ] diff -Nru rust-tonic-0.12.3+dfsg/tonic-web/README.md rust-tonic-0.13.0+dfsg/tonic-web/README.md --- rust-tonic-0.12.3+dfsg/tonic-web/README.md 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-web/README.md 2025-03-25 18:49:19.000000000 +0000 @@ -3,13 +3,6 @@ Enables tonic servers to handle requests from `grpc-web` clients directly, without the need of an external proxy. -## Getting Started - -```toml -[dependencies] -tonic-web = "" -``` - ## Enabling tonic services The easiest way to get started, is to call the function with your tonic service diff -Nru rust-tonic-0.12.3+dfsg/tonic-web/src/call.rs rust-tonic-0.13.0+dfsg/tonic-web/src/call.rs --- rust-tonic-0.12.3+dfsg/tonic-web/src/call.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-web/src/call.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,4 +1,4 @@ -use std::error::Error; +use std::fmt; use std::pin::Pin; use std::task::{ready, Context, Poll}; @@ -160,8 +160,9 @@ impl GrpcWebCall where - B: Body, - B::Error: Error, + B: Body, + B::Data: Buf, + B::Error: fmt::Display, { // Poll body for data, decoding (e.g. via Base64 if necessary) and returning frames // to the caller. If the caller is a client, it should look for trailers before @@ -169,17 +170,19 @@ fn poll_decode( mut self: Pin<&mut Self>, cx: &mut Context<'_>, - ) -> Poll, Status>>> { + ) -> Poll, Status>>> { match self.encoding { Encoding::Base64 => loop { if let Some(bytes) = self.as_mut().decode_chunk()? { return Poll::Ready(Some(Ok(Frame::data(bytes)))); } - let mut this = self.as_mut().project(); + let this = self.as_mut().project(); - match ready!(this.inner.as_mut().poll_frame(cx)) { - Some(Ok(frame)) if frame.is_data() => this.buf.put(frame.into_data().unwrap()), + match ready!(this.inner.poll_frame(cx)) { + Some(Ok(frame)) if frame.is_data() => this + .buf + .put(frame.into_data().unwrap_or_else(|_| unreachable!())), Some(Ok(frame)) if frame.is_trailers() => { return Poll::Ready(Some(Err(internal_error( "malformed base64 request has unencoded trailers", @@ -201,19 +204,25 @@ } }, - Encoding::None => self.project().inner.poll_frame(cx).map_err(internal_error), + Encoding::None => self + .project() + .inner + .poll_frame(cx) + .map_ok(|f| f.map_data(|mut d| d.copy_to_bytes(d.remaining()))) + .map_err(internal_error), } } fn poll_encode( mut self: Pin<&mut Self>, cx: &mut Context<'_>, - ) -> Poll, Status>>> { - let mut this = self.as_mut().project(); + ) -> Poll, Status>>> { + let this = self.as_mut().project(); - match ready!(this.inner.as_mut().poll_frame(cx)) { + match ready!(this.inner.poll_frame(cx)) { Some(Ok(frame)) if frame.is_data() => { - let mut res = frame.into_data().unwrap(); + let mut data = frame.into_data().unwrap_or_else(|_| unreachable!()); + let mut res = data.copy_to_bytes(data.remaining()); if *this.encoding == Encoding::Base64 { res = crate::util::base64::STANDARD.encode(res).into(); @@ -222,12 +231,14 @@ Poll::Ready(Some(Ok(Frame::data(res)))) } Some(Ok(frame)) if frame.is_trailers() => { - let trailers = frame.into_trailers().expect("must be trailers"); - let mut frame = make_trailers_frame(trailers); + let trailers = frame.into_trailers().unwrap_or_else(|_| unreachable!()); + let mut res = make_trailers_frame(trailers); + if *this.encoding == Encoding::Base64 { - frame = crate::util::base64::STANDARD.encode(frame).into_bytes(); + res = crate::util::base64::STANDARD.encode(res).into(); } - Poll::Ready(Some(Ok(Frame::data(frame.into())))) + + Poll::Ready(Some(Ok(Frame::data(res)))) } Some(Ok(_)) => Poll::Ready(Some(Err(internal_error("unexpected frame type")))), Some(Err(e)) => Poll::Ready(Some(Err(internal_error(e)))), @@ -238,8 +249,8 @@ impl Body for GrpcWebCall where - B: Body, - B::Error: Error, + B: Body, + B::Error: fmt::Display, { type Data = Bytes; type Error = Status; @@ -327,8 +338,8 @@ impl Stream for GrpcWebCall where - B: Body, - B::Error: Error, + B: Body, + B::Error: fmt::Display, { type Item = Result, Status>; @@ -418,7 +429,9 @@ let value = value .split(|b| b == &b'\r') .next() - .ok_or_else(|| Status::internal("trailers was not escaped"))?; + .ok_or_else(|| Status::internal("trailers was not escaped"))? + .strip_prefix(b" ") + .unwrap_or(value); let header_key = HeaderName::try_from(key) .map_err(|e| Status::internal(format!("Unable to parse HeaderName: {}", e)))?; @@ -430,17 +443,17 @@ Ok(Some(map)) } -fn make_trailers_frame(trailers: HeaderMap) -> Vec { +fn make_trailers_frame(trailers: HeaderMap) -> Bytes { let trailers = encode_trailers(trailers); let len = trailers.len(); assert!(len <= u32::MAX as usize); - let mut frame = Vec::with_capacity(len + FRAME_HEADER_SIZE); - frame.push(GRPC_WEB_TRAILERS_BIT); + let mut frame = BytesMut::with_capacity(len + FRAME_HEADER_SIZE); + frame.put_u8(GRPC_WEB_TRAILERS_BIT); frame.put_u32(len as u32); - frame.extend(trailers); + frame.put_slice(&trailers); - frame + frame.freeze() } /// Search some buffer for grpc-web trailers headers and return @@ -530,9 +543,7 @@ let trailers = make_trailers_frame(headers.clone()); - let buf = Bytes::from(trailers); - - let map = decode_trailers_frame(buf).unwrap().unwrap(); + let map = decode_trailers_frame(trailers).unwrap().unwrap(); assert_eq!(headers, map); } @@ -634,4 +645,19 @@ assert_eq!(trailers, expected); } + + #[test] + fn decode_trailers_with_space_after_colon() { + let buf = b"\x80\0\0\0\x0fgrpc-status: 0\r\ngrpc-message: \r\n"; + + let trailers = decode_trailers_frame(Bytes::copy_from_slice(&buf[..])) + .unwrap() + .unwrap(); + + let mut expected = HeaderMap::new(); + expected.insert(Status::GRPC_STATUS, "0".parse().unwrap()); + expected.insert(Status::GRPC_MESSAGE, "".parse().unwrap()); + + assert_eq!(trailers, expected); + } } diff -Nru rust-tonic-0.12.3+dfsg/tonic-web/src/client.rs rust-tonic-0.13.0+dfsg/tonic-web/src/client.rs --- rust-tonic-0.12.3+dfsg/tonic-web/src/client.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-web/src/client.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,9 +1,7 @@ -use bytes::Bytes; use http::header::CONTENT_TYPE; use http::{Request, Response, Version}; -use http_body::Body; use pin_project::pin_project; -use std::error::Error; +use std::fmt; use std::future::Future; use std::pin::Pin; use std::task::{ready, Context, Poll}; @@ -53,9 +51,6 @@ impl Service> for GrpcWebClientService where S: Service>, Response = Response>, - B1: Body, - B2: Body, - B2::Error: Error, { type Response = Response>; type Error = S::Error; @@ -83,8 +78,7 @@ } } -/// Response future for the [`GrpcWebService`]. -#[allow(missing_debug_implementations)] +/// Response future for the [`GrpcWebService`](crate::GrpcWebService). #[pin_project] #[must_use = "futures do nothing unless polled"] pub struct ResponseFuture { @@ -94,7 +88,6 @@ impl Future for ResponseFuture where - B: Body, F: Future, E>>, { type Output = Result>, E>; @@ -105,3 +98,9 @@ Poll::Ready(res.map(|r| r.map(GrpcWebCall::client_response))) } } + +impl fmt::Debug for ResponseFuture { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ResponseFuture").finish() + } +} diff -Nru rust-tonic-0.12.3+dfsg/tonic-web/src/layer.rs rust-tonic-0.13.0+dfsg/tonic-web/src/layer.rs --- rust-tonic-0.12.3+dfsg/tonic-web/src/layer.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-web/src/layer.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,7 +1,6 @@ -use super::{BoxBody, BoxError, GrpcWebService}; +use super::GrpcWebService; use tower_layer::Layer; -use tower_service::Service; /// Layer implementing the grpc-web protocol. #[derive(Debug, Default, Clone)] @@ -16,13 +15,7 @@ } } -impl Layer for GrpcWebLayer -where - S: Service, Response = http::Response>, - S: Send + 'static, - S::Future: Send + 'static, - S::Error: Into + Send, -{ +impl Layer for GrpcWebLayer { type Service = GrpcWebService; fn layer(&self, inner: S) -> Self::Service { diff -Nru rust-tonic-0.12.3+dfsg/tonic-web/src/lib.rs rust-tonic-0.13.0+dfsg/tonic-web/src/lib.rs --- rust-tonic-0.12.3+dfsg/tonic-web/src/lib.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-web/src/lib.rs 2025-03-25 18:49:19.000000000 +0000 @@ -7,26 +7,6 @@ //! //! ## Enabling tonic services //! -//! The easiest way to get started, is to call the [`enable`] function with your tonic service -//! and allow the tonic server to accept HTTP/1.1 requests: -//! -//! ```ignore -//! #[tokio::main] -//! async fn main() -> Result<(), Box> { -//! let addr = "[::1]:50051".parse().unwrap(); -//! let greeter = GreeterServer::new(MyGreeter::default()); -//! -//! Server::builder() -//! .accept_http1(true) -//! .add_service(tonic_web::enable(greeter)) -//! .serve(addr) -//! .await?; -//! -//! Ok(()) -//! } -//! ``` -//! This will apply a default configuration that works well with grpc-web clients out of the box. -//! //! You can customize the CORS configuration composing the [`GrpcWebLayer`] with the cors layer of your choice. //! //! ```ignore @@ -63,7 +43,8 @@ //! // No need to enable HTTP/1 //! Server::builder() //! .tls_config(ServerTlsConfig::new().identity(identity))? -//! .add_service(tonic_web::enable(greeter)) +//! .layer(GrpcWebLayer::new()) +//! .add_service(greeter) //! .serve(addr) //! .await?; //! @@ -87,14 +68,6 @@ //! [`tonic_web`]: https://github.com/hyperium/tonic //! [grpc-web]: https://github.com/grpc/grpc-web //! [tower]: https://github.com/tower-rs/tower -//! [`enable`]: crate::enable() -#![warn( - missing_debug_implementations, - missing_docs, - rust_2018_idioms, - unreachable_pub -)] -#![doc(html_root_url = "https://docs.rs/tonic-web/0.12.3")] #![doc(issue_tracker_base_url = "https://github.com/hyperium/tonic/issues/")] pub use call::GrpcWebCall; @@ -107,86 +80,8 @@ mod layer; mod service; -use http::header::HeaderName; -use std::time::Duration; -use tonic::{body::BoxBody, server::NamedService, Status}; -use tower_http::cors::{AllowOrigin, CorsLayer}; -use tower_layer::Layer; -use tower_service::Service; - -const DEFAULT_MAX_AGE: Duration = Duration::from_secs(24 * 60 * 60); -const DEFAULT_EXPOSED_HEADERS: [HeaderName; 3] = [ - Status::GRPC_STATUS, - Status::GRPC_MESSAGE, - Status::GRPC_STATUS_DETAILS, -]; -const DEFAULT_ALLOW_HEADERS: [&str; 4] = - ["x-grpc-web", "content-type", "x-user-agent", "grpc-timeout"]; - type BoxError = Box; -/// Enable a tonic service to handle grpc-web requests with the default configuration. -/// -/// You can customize the CORS configuration composing the [`GrpcWebLayer`] with the cors layer of your choice. -pub fn enable(service: S) -> CorsGrpcWeb -where - S: Service, Response = http::Response>, - S: Clone + Send + 'static, - S::Future: Send + 'static, - S::Error: Into + Send, -{ - let cors = CorsLayer::new() - .allow_origin(AllowOrigin::mirror_request()) - .allow_credentials(true) - .max_age(DEFAULT_MAX_AGE) - .expose_headers(DEFAULT_EXPOSED_HEADERS.iter().cloned().collect::>()) - .allow_headers( - DEFAULT_ALLOW_HEADERS - .iter() - .cloned() - .map(HeaderName::from_static) - .collect::>(), - ); - - tower_layer::layer_fn(|s| CorsGrpcWeb(cors.layer(s))).layer(GrpcWebService::new(service)) -} - -/// A newtype wrapper around [`GrpcWebLayer`] and [`tower_http::cors::CorsLayer`] to allow -/// `tonic_web::enable` to implement the [`NamedService`] trait. -#[derive(Debug, Clone)] -pub struct CorsGrpcWeb(tower_http::cors::Cors>); - -impl Service> for CorsGrpcWeb -where - S: Service, Response = http::Response>, - S: Clone + Send + 'static, - S::Future: Send + 'static, - S::Error: Into + Send, -{ - type Response = S::Response; - type Error = S::Error; - type Future = - > as Service>>::Future; - - fn poll_ready( - &mut self, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - self.0.poll_ready(cx) - } - - fn call(&mut self, req: http::Request) -> Self::Future { - self.0.call(req) - } -} - -impl NamedService for CorsGrpcWeb -where - S: NamedService, -{ - const NAME: &'static str = S::NAME; -} - pub(crate) mod util { pub(crate) mod base64 { use base64::{ diff -Nru rust-tonic-0.12.3+dfsg/tonic-web/src/service.rs rust-tonic-0.13.0+dfsg/tonic-web/src/service.rs --- rust-tonic-0.12.3+dfsg/tonic-web/src/service.rs 2024-09-26 16:08:03.000000000 +0000 +++ rust-tonic-0.13.0+dfsg/tonic-web/src/service.rs 2025-03-25 18:49:19.000000000 +0000 @@ -1,21 +1,17 @@ +use core::fmt; use std::future::Future; use std::pin::Pin; use std::task::{ready, Context, Poll}; use http::{header, HeaderMap, HeaderValue, Method, Request, Response, StatusCode, Version}; -use http_body_util::BodyExt; use pin_project::pin_project; use tonic::metadata::GRPC_CONTENT_TYPE; -use tonic::{ - body::{empty_body, BoxBody}, - server::NamedService, -}; +use tonic::{body::Body, server::NamedService}; use tower_service::Service; use tracing::{debug, trace}; use crate::call::content_types::is_grpc_web; use crate::call::{Encoding, GrpcWebCall}; -use crate::BoxError; /// Service implementing the grpc-web protocol. #[derive(Debug, Clone)] @@ -47,29 +43,11 @@ } } -impl GrpcWebService +impl Service> for GrpcWebService where - S: Service, Response = Response> + Send + 'static, -{ - fn response(&self, status: StatusCode) -> ResponseFuture { - ResponseFuture { - case: Case::ImmediateResponse { - res: Some( - Response::builder() - .status(status) - .body(empty_body()) - .unwrap(), - ), - }, - } - } -} - -impl Service> for GrpcWebService -where - S: Service, Response = Response> + Send + 'static, - S::Future: Send + 'static, - S::Error: Into + Send, + S: Service, Response = Response>, + B: http_body::Body + Send + 'static, + B::Error: Into + fmt::Display, { type Response = S::Response; type Error = S::Error; @@ -79,7 +57,7 @@ self.inner.poll_ready(cx) } - fn call(&mut self, req: Request) -> Self::Future { + fn call(&mut self, req: Request) -> Self::Future { match RequestKind::new(req.headers(), req.method(), req.version()) { // A valid grpc-web request, regardless of HTTP version. // @@ -110,7 +88,10 @@ // This is not a valid grpc-web request, return HTTP 405. RequestKind::GrpcWeb { .. } => { debug!(kind = "simple", error="method not allowed", method = ?req.method()); - self.response(StatusCode::METHOD_NOT_ALLOWED) + + ResponseFuture { + case: Case::immediate(StatusCode::METHOD_NOT_ALLOWED), + } } // All http/2 requests that are not grpc-web are passed through to the inner service, @@ -119,7 +100,7 @@ debug!(kind = "other h2", content_type = ?req.headers().get(header::CONTENT_TYPE)); ResponseFuture { case: Case::Other { - future: self.inner.call(req), + future: self.inner.call(req.map(Body::new)), }, } } @@ -127,14 +108,16 @@ // Return HTTP 400 for all other requests. RequestKind::Other(_) => { debug!(kind = "other h1", content_type = ?req.headers().get(header::CONTENT_TYPE)); - self.response(StatusCode::BAD_REQUEST) + + ResponseFuture { + case: Case::immediate(StatusCode::BAD_REQUEST), + } } } } } /// Response future for the [`GrpcWebService`]. -#[allow(missing_debug_implementations)] #[pin_project] #[must_use = "futures do nothing unless polled"] pub struct ResponseFuture { @@ -154,28 +137,41 @@ future: F, }, ImmediateResponse { - res: Option>, + res: Option, }, } +impl Case { + fn immediate(status: StatusCode) -> Self { + let (res, ()) = Response::builder() + .status(status) + .body(()) + .unwrap() + .into_parts(); + Self::ImmediateResponse { res: Some(res) } + } +} + impl Future for ResponseFuture where - F: Future, E>> + Send + 'static, - E: Into + Send, + F: Future, E>>, { - type Output = Result, E>; + type Output = Result, E>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let mut this = self.project(); + let this = self.project(); - match this.case.as_mut().project() { + match this.case.project() { CaseProj::GrpcWeb { future, accept } => { let res = ready!(future.poll(cx))?; Poll::Ready(Ok(coerce_response(res, *accept))) } CaseProj::Other { future } => future.poll(cx), - CaseProj::ImmediateResponse { res } => Poll::Ready(Ok(res.take().unwrap())), + CaseProj::ImmediateResponse { res } => { + let res = Response::from_parts(res.take().unwrap(), Body::empty()); + Poll::Ready(Ok(res)) + } } } } @@ -184,6 +180,12 @@ const NAME: &'static str = S::NAME; } +impl fmt::Debug for ResponseFuture { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ResponseFuture").finish() + } +} + impl<'a> RequestKind<'a> { fn new(headers: &'a HeaderMap, method: &'a Method, version: Version) -> Self { if is_grpc_web(headers) { @@ -201,7 +203,11 @@ // Mutating request headers to conform to a gRPC request is not really // necessary for us at this point. We could remove most of these except // maybe for inserting `header::TE`, which tonic should check? -fn coerce_request(mut req: Request, encoding: Encoding) -> Request { +fn coerce_request(mut req: Request, encoding: Encoding) -> Request +where + B: http_body::Body + Send + 'static, + B::Error: Into + fmt::Display, +{ req.headers_mut().remove(header::CONTENT_LENGTH); req.headers_mut() @@ -215,13 +221,17 @@ HeaderValue::from_static("identity,deflate,gzip"), ); - req.map(|b| GrpcWebCall::request(b, encoding).boxed_unsync()) + req.map(|b| Body::new(GrpcWebCall::request(b, encoding))) } -fn coerce_response(res: Response, encoding: Encoding) -> Response { +fn coerce_response(res: Response, encoding: Encoding) -> Response +where + B: http_body::Body + Send + 'static, + B::Error: Into + fmt::Display, +{ let mut res = res .map(|b| GrpcWebCall::response(b, encoding)) - .map(BoxBody::new); + .map(Body::new); res.headers_mut().insert( header::CONTENT_TYPE, @@ -238,14 +248,15 @@ use http::header::{ ACCESS_CONTROL_REQUEST_HEADERS, ACCESS_CONTROL_REQUEST_METHOD, CONTENT_TYPE, ORIGIN, }; + use tower_layer::Layer as _; type BoxFuture = Pin> + Send>>; #[derive(Debug, Clone)] struct Svc; - impl tower_service::Service> for Svc { - type Response = Response; + impl tower_service::Service> for Svc { + type Response = Response; type Error = String; type Future = BoxFuture; @@ -253,8 +264,8 @@ Poll::Ready(Ok(())) } - fn call(&mut self, _: Request) -> Self::Future { - Box::pin(async { Ok(Response::new(empty_body())) }) + fn call(&mut self, _: Request) -> Self::Future { + Box::pin(async { Ok(Response::new(Body::default())) }) } } @@ -262,22 +273,33 @@ const NAME: &'static str = "test"; } + fn enable(service: S) -> tower_http::cors::Cors> + where + S: Service, Response = http::Response>, + { + tower_layer::Stack::new( + crate::GrpcWebLayer::new(), + tower_http::cors::CorsLayer::new(), + ) + .layer(service) + } + mod grpc_web { use super::*; use tower_layer::Layer; - fn request() -> Request { + fn request() -> Request { Request::builder() .method(Method::POST) .header(CONTENT_TYPE, GRPC_WEB) .header(ORIGIN, "http://example.com") - .body(empty_body()) + .body(Body::default()) .unwrap() } #[tokio::test] async fn default_cors_config() { - let mut svc = crate::enable(Svc); + let mut svc = enable(Svc); let res = svc.call(request()).await.unwrap(); assert_eq!(res.status(), StatusCode::OK); @@ -293,7 +315,7 @@ #[tokio::test] async fn without_origin() { - let mut svc = crate::enable(Svc); + let mut svc = enable(Svc); let mut req = request(); req.headers_mut().remove(ORIGIN); @@ -305,7 +327,7 @@ #[tokio::test] async fn only_post_and_options_allowed() { - let mut svc = crate::enable(Svc); + let mut svc = enable(Svc); for method in &[ Method::GET, @@ -330,7 +352,7 @@ #[tokio::test] async fn grpc_web_content_types() { - let mut svc = crate::enable(Svc); + let mut svc = enable(Svc); for ct in &[GRPC_WEB_TEXT, GRPC_WEB_PROTO, GRPC_WEB_TEXT_PROTO, GRPC_WEB] { let mut req = request(); @@ -347,19 +369,19 @@ mod options { use super::*; - fn request() -> Request { + fn request() -> Request { Request::builder() .method(Method::OPTIONS) .header(ORIGIN, "http://example.com") .header(ACCESS_CONTROL_REQUEST_HEADERS, "x-grpc-web") .header(ACCESS_CONTROL_REQUEST_METHOD, "POST") - .body(empty_body()) + .body(Body::default()) .unwrap() } #[tokio::test] async fn valid_grpc_web_preflight() { - let mut svc = crate::enable(Svc); + let mut svc = enable(Svc); let res = svc.call(request()).await.unwrap(); assert_eq!(res.status(), StatusCode::OK); @@ -369,17 +391,17 @@ mod grpc { use super::*; - fn request() -> Request { + fn request() -> Request { Request::builder() .version(Version::HTTP_2) .header(CONTENT_TYPE, GRPC_CONTENT_TYPE) - .body(empty_body()) + .body(Body::default()) .unwrap() } #[tokio::test] async fn h2_is_ok() { - let mut svc = crate::enable(Svc); + let mut svc = enable(Svc); let req = request(); let res = svc.call(req).await.unwrap(); @@ -389,11 +411,11 @@ #[tokio::test] async fn h1_is_err() { - let mut svc = crate::enable(Svc); + let mut svc = enable(Svc); let req = Request::builder() .header(CONTENT_TYPE, GRPC_CONTENT_TYPE) - .body(empty_body()) + .body(Body::default()) .unwrap(); let res = svc.call(req).await.unwrap(); @@ -402,7 +424,7 @@ #[tokio::test] async fn content_type_variants() { - let mut svc = crate::enable(Svc); + let mut svc = enable(Svc); for variant in &["grpc", "grpc+proto", "grpc+thrift", "grpc+foo"] { let mut req = request(); @@ -421,16 +443,16 @@ mod other { use super::*; - fn request() -> Request { + fn request() -> Request { Request::builder() .header(CONTENT_TYPE, "application/text") - .body(empty_body()) + .body(Body::default()) .unwrap() } #[tokio::test] async fn h1_is_err() { - let mut svc = crate::enable(Svc); + let mut svc = enable(Svc); let res = svc.call(request()).await.unwrap(); assert_eq!(res.status(), StatusCode::BAD_REQUEST) @@ -438,7 +460,7 @@ #[tokio::test] async fn h2_is_ok() { - let mut svc = crate::enable(Svc); + let mut svc = enable(Svc); let mut req = request(); *req.version_mut() = Version::HTTP_2;