├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── assets │ ├── tonic-banner.svg │ └── tonic-docs.png └── workflows │ └── CI.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE ├── README.md ├── SECURITY.md ├── codegen ├── Cargo.toml └── src │ └── main.rs ├── examples ├── Cargo.toml ├── README.md ├── build.rs ├── data │ ├── gcp │ │ └── roots.pem │ ├── route_guide_db.json │ └── tls │ │ ├── ca.pem │ │ ├── client1.key │ │ ├── client1.pem │ │ ├── client2.key │ │ ├── client2.pem │ │ ├── client_ca.pem │ │ ├── server.key │ │ └── server.pem ├── helloworld-tutorial.md ├── proto │ ├── attrs │ │ └── attrs.proto │ ├── echo │ │ └── echo.proto │ ├── googleapis │ │ └── google │ │ │ ├── api │ │ │ ├── annotations.proto │ │ │ ├── client.proto │ │ │ ├── field_behavior.proto │ │ │ ├── http.proto │ │ │ └── resource.proto │ │ │ └── pubsub │ │ │ └── v1 │ │ │ ├── pubsub.proto │ │ │ └── schema.proto │ ├── helloworld │ │ └── helloworld.proto │ ├── routeguide │ │ └── route_guide.proto │ └── unaryecho │ │ └── echo.proto ├── routeguide-tutorial.md └── src │ ├── authentication │ ├── client.rs │ └── server.rs │ ├── autoreload │ └── server.rs │ ├── blocking │ ├── client.rs │ └── server.rs │ ├── cancellation │ ├── client.rs │ └── server.rs │ ├── codec_buffers │ ├── client.rs │ ├── common.rs │ └── server.rs │ ├── compression │ ├── client.rs │ └── server.rs │ ├── dynamic │ └── server.rs │ ├── dynamic_load_balance │ ├── client.rs │ └── server.rs │ ├── gcp │ ├── README.md │ └── client.rs │ ├── grpc-web │ ├── client.rs │ └── server.rs │ ├── h2c │ ├── client.rs │ └── server.rs │ ├── health │ ├── README.md │ └── server.rs │ ├── helloworld │ ├── client.rs │ └── server.rs │ ├── interceptor │ ├── client.rs │ └── server.rs │ ├── json-codec │ ├── client.rs │ ├── common.rs │ └── server.rs │ ├── load_balance │ ├── client.rs │ └── server.rs │ ├── mock │ └── mock.rs │ ├── multiplex │ ├── client.rs │ └── server.rs │ ├── reflection │ └── server.rs │ ├── richer-error │ ├── client.rs │ ├── client_vec.rs │ ├── server.rs │ └── server_vec.rs │ ├── routeguide │ ├── client.rs │ ├── data.rs │ └── server.rs │ ├── streaming │ ├── client.rs │ └── server.rs │ ├── tls │ ├── client.rs │ └── server.rs │ ├── tls_client_auth │ ├── client.rs │ └── server.rs │ ├── tls_rustls │ ├── client.rs │ └── server.rs │ ├── tower │ ├── client.rs │ └── server.rs │ ├── tracing │ ├── client.rs │ └── server.rs │ └── uds │ ├── client_standard.rs │ ├── client_with_connector.rs │ └── server.rs ├── flake.lock ├── flake.nix ├── grpc ├── Cargo.toml ├── LICENSE ├── NOTICE.txt └── src │ ├── attributes.rs │ ├── client │ ├── load_balancing │ │ ├── child_manager.rs │ │ └── mod.rs │ ├── mod.rs │ ├── name_resolution │ │ └── mod.rs │ ├── service.rs │ └── service_config.rs │ └── lib.rs ├── interop ├── Cargo.toml ├── bin │ ├── client_darwin_amd64 │ ├── client_linux_amd64 │ ├── client_windows_amd64.exe │ ├── server_darwin_amd64 │ ├── server_linux_amd64 │ └── server_windows_amd64.exe ├── build.rs ├── data │ ├── README.md │ ├── ca.pem │ ├── cert-generator │ │ ├── .gitignore │ │ ├── ca.tf │ │ └── server_certs.tf │ ├── server1.key │ └── server1.pem ├── proto │ └── grpc │ │ └── testing │ │ ├── empty.proto │ │ ├── messages.proto │ │ └── test.proto ├── src │ ├── bin │ │ ├── client.rs │ │ └── server.rs │ ├── client.rs │ ├── lib.rs │ └── server.rs ├── test.sh └── update_binaries.sh ├── prepare-release.sh ├── publish-release.sh ├── tests ├── ambiguous_methods │ ├── Cargo.toml │ ├── build.rs │ ├── proto │ │ └── ambiguous_methods.proto │ └── src │ │ └── lib.rs ├── compression │ ├── Cargo.toml │ ├── build.rs │ ├── proto │ │ └── test.proto │ └── src │ │ ├── bidirectional_stream.rs │ │ ├── client_stream.rs │ │ ├── compressing_request.rs │ │ ├── compressing_response.rs │ │ ├── lib.rs │ │ ├── server_stream.rs │ │ └── util.rs ├── default_stubs │ ├── Cargo.toml │ ├── build.rs │ ├── proto │ │ ├── test.proto │ │ └── test_default.proto │ └── src │ │ ├── lib.rs │ │ └── test_defaults.rs ├── deprecated_methods │ ├── Cargo.toml │ ├── build.rs │ ├── proto │ │ └── test.proto │ ├── src │ │ └── lib.rs │ └── tests │ │ └── deprecated_methods.rs ├── disable_comments │ ├── Cargo.toml │ ├── build.rs │ ├── proto │ │ └── test.proto │ ├── src │ │ └── lib.rs │ └── tests │ │ └── disable_comments.rs ├── extern_path │ ├── my_application │ │ ├── Cargo.toml │ │ ├── build.rs │ │ └── src │ │ │ └── main.rs │ ├── proto │ │ ├── my_application │ │ │ └── service.proto │ │ └── uuid │ │ │ └── uuid.proto │ └── uuid │ │ ├── Cargo.toml │ │ ├── build.rs │ │ └── src │ │ └── lib.rs ├── included_service │ ├── Cargo.toml │ ├── build.rs │ ├── proto │ │ ├── includee.proto │ │ └── includer.proto │ └── src │ │ └── lib.rs ├── integration_tests │ ├── Cargo.toml │ ├── build.rs │ ├── proto │ │ ├── stream.proto │ │ └── test.proto │ ├── src │ │ └── lib.rs │ └── tests │ │ ├── client_layer.rs │ │ ├── complex_tower_middleware.rs │ │ ├── connect_info.rs │ │ ├── connection.rs │ │ ├── extensions.rs │ │ ├── http2_keep_alive.rs │ │ ├── http2_max_header_list_size.rs │ │ ├── interceptor.rs │ │ ├── max_message_size.rs │ │ ├── origin.rs │ │ ├── routes_builder.rs │ │ ├── status.rs │ │ ├── streams.rs │ │ ├── timeout.rs │ │ └── user_agent.rs ├── root-crate-path │ ├── Cargo.toml │ ├── build.rs │ ├── foo.proto │ └── src │ │ └── lib.rs ├── same_name │ ├── Cargo.toml │ ├── build.rs │ ├── proto │ │ └── foo.proto │ └── src │ │ └── lib.rs ├── service_named_result │ ├── Cargo.toml │ ├── build.rs │ ├── proto │ │ └── result.proto │ └── src │ │ └── lib.rs ├── service_named_service │ ├── Cargo.toml │ ├── build.rs │ ├── proto │ │ └── foo.proto │ └── src │ │ └── lib.rs ├── skip_debug │ ├── Cargo.toml │ ├── build.rs │ ├── proto │ │ └── test.proto │ ├── src │ │ └── lib.rs │ └── tests │ │ └── skip_debug.rs ├── stream_conflict │ ├── Cargo.toml │ ├── build.rs │ ├── proto │ │ └── stream_conflict.proto │ └── src │ │ └── lib.rs ├── use_arc_self │ ├── Cargo.toml │ ├── build.rs │ ├── proto │ │ └── test.proto │ └── src │ │ └── lib.rs ├── web │ ├── Cargo.toml │ ├── build.rs │ ├── proto │ │ └── test.proto │ ├── src │ │ └── lib.rs │ └── tests │ │ ├── grpc.rs │ │ └── grpc_web.rs ├── wellknown-compiled │ ├── Cargo.toml │ ├── build.rs │ ├── proto │ │ ├── google.proto │ │ └── test.proto │ └── src │ │ └── lib.rs └── wellknown │ ├── Cargo.toml │ ├── build.rs │ ├── proto │ └── wellknown.proto │ └── src │ └── lib.rs ├── tonic-build ├── Cargo.toml ├── LICENSE ├── README.md └── src │ ├── client.rs │ ├── code_gen.rs │ ├── compile_settings.rs │ ├── lib.rs │ ├── manual.rs │ ├── prost.rs │ └── server.rs ├── tonic-health ├── Cargo.toml ├── LICENSE ├── README.md ├── proto │ └── health.proto └── src │ ├── generated │ ├── grpc_health_v1.rs │ └── grpc_health_v1_fds.rs │ ├── lib.rs │ └── server.rs ├── tonic-reflection ├── Cargo.toml ├── LICENSE ├── README.md ├── proto │ ├── reflection_v1.proto │ └── reflection_v1alpha.proto ├── src │ ├── generated │ │ ├── grpc_reflection_v1.rs │ │ ├── grpc_reflection_v1alpha.rs │ │ ├── reflection_v1_fds.rs │ │ └── reflection_v1alpha1_fds.rs │ ├── lib.rs │ └── server │ │ ├── mod.rs │ │ ├── v1.rs │ │ └── v1alpha.rs └── tests │ ├── server.rs │ └── versions.rs ├── tonic-types ├── Cargo.toml ├── LICENSE ├── README.md ├── proto │ ├── error_details.proto │ └── status.proto └── src │ ├── generated │ ├── google_rpc.rs │ └── types_fds.rs │ ├── lib.rs │ └── richer_error │ ├── error_details │ ├── mod.rs │ └── vec.rs │ ├── mod.rs │ └── std_messages │ ├── bad_request.rs │ ├── debug_info.rs │ ├── error_info.rs │ ├── help.rs │ ├── loc_message.rs │ ├── mod.rs │ ├── prec_failure.rs │ ├── quota_failure.rs │ ├── request_info.rs │ ├── resource_info.rs │ └── retry_info.rs ├── tonic-web ├── Cargo.toml ├── LICENSE ├── README.md ├── src │ ├── call.rs │ ├── client.rs │ ├── layer.rs │ ├── lib.rs │ └── service.rs └── tests │ └── incomplete-buf-bug.bin └── tonic ├── Cargo.toml ├── LICENSE ├── benches-disabled ├── README.md ├── bench_main.rs ├── benchmarks │ ├── compiled_protos │ │ ├── diverse_types.rs │ │ ├── helloworld.rs │ │ └── mod.rs │ ├── mod.rs │ ├── request_response.rs │ ├── request_response_diverse_types.rs │ └── utils.rs └── proto │ ├── diverse_types │ └── diverse_types.proto │ └── helloworld │ └── helloworld.proto ├── benches └── decode.rs └── src ├── body.rs ├── client ├── grpc.rs ├── mod.rs └── service.rs ├── codec ├── buffer.rs ├── compression.rs ├── decode.rs ├── encode.rs ├── mod.rs └── prost.rs ├── codegen.rs ├── extensions.rs ├── lib.rs ├── macros.rs ├── metadata ├── encoding.rs ├── key.rs ├── map.rs ├── mod.rs └── value.rs ├── request.rs ├── response.rs ├── server ├── grpc.rs ├── mod.rs └── service.rs ├── service ├── interceptor.rs ├── layered.rs ├── mod.rs ├── recover_error.rs └── router.rs ├── status.rs ├── transport ├── channel │ ├── endpoint.rs │ ├── mod.rs │ ├── service │ │ ├── add_origin.rs │ │ ├── connection.rs │ │ ├── connector.rs │ │ ├── discover.rs │ │ ├── executor.rs │ │ ├── io.rs │ │ ├── mod.rs │ │ ├── reconnect.rs │ │ ├── tls.rs │ │ └── user_agent.rs │ ├── tls.rs │ └── uds_connector.rs ├── error.rs ├── mod.rs ├── server │ ├── conn.rs │ ├── incoming.rs │ ├── io_stream.rs │ ├── mod.rs │ ├── service │ │ ├── io.rs │ │ ├── mod.rs │ │ └── tls.rs │ ├── tls.rs │ └── unix.rs ├── service │ ├── grpc_timeout.rs │ ├── mod.rs │ └── tls.rs └── tls.rs └── util.rs /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug Report 3 | about: If something isn't working as expected 🤔. 4 | 5 | --- 6 | 7 | ## Bug Report 8 | 13 | 14 | ### Version 15 | 16 | 27 | 28 | ### Platform 29 | 30 | 33 | 34 | ### Crates 35 | 36 | 40 | 41 | ### Description 42 | 43 | 59 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 💡 Feature Request 3 | about: I have a suggestion (and may want to implement it 🙂)! 4 | 5 | --- 6 | 7 | ## Feature Request 8 | 9 | ### Crates 10 | 11 | 15 | 16 | ### Motivation 17 | 18 | 21 | 22 | ### Proposal 23 | 24 | 28 | 29 | ### Alternatives 30 | 31 | 36 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 9 | 10 | ## Motivation 11 | 12 | 17 | 18 | ## Solution 19 | 20 | 24 | -------------------------------------------------------------------------------- /.github/assets/tonic-banner.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 11 | 12 | 13 | 14 | 18 | 19 | 20 | 23 | 26 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /.github/assets/tonic-docs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperium/tonic/689a86dbad107fa721714bb02e7f8e39264a377d/.github/assets/tonic-docs.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | **/*.rs.bk 3 | Cargo.lock 4 | tags 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "tonic", 4 | "tonic-build", 5 | "tonic-health", 6 | "tonic-types", 7 | "tonic-reflection", 8 | "tonic-web", # Non-published crates 9 | "examples", 10 | "codegen", 11 | "grpc", 12 | "interop", # Tests 13 | "tests/disable_comments", 14 | "tests/included_service", 15 | "tests/same_name", 16 | "tests/service_named_service", 17 | "tests/wellknown", 18 | "tests/wellknown-compiled", 19 | "tests/extern_path/uuid", 20 | "tests/ambiguous_methods", 21 | "tests/extern_path/my_application", 22 | "tests/integration_tests", 23 | "tests/stream_conflict", 24 | "tests/root-crate-path", 25 | "tests/compression", 26 | "tests/web", 27 | "tests/service_named_result", 28 | "tests/use_arc_self", 29 | "tests/default_stubs", 30 | "tests/deprecated_methods", 31 | "tests/skip_debug", 32 | ] 33 | resolver = "2" 34 | 35 | [workspace.package] 36 | rust-version = "1.75" 37 | 38 | [workspace.lints.rust] 39 | missing_debug_implementations = "warn" 40 | missing_docs = "warn" 41 | rust_2018_idioms = "warn" 42 | unreachable_pub = "warn" 43 | 44 | [workspace.lints.clippy] 45 | uninlined_format_args = "deny" 46 | 47 | [workspace.lints.rustdoc] 48 | broken_intra_doc_links = "deny" 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2025 Lucio Franco 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | tonic (and related projects in hyperium) uses the same security policy as the [Tokio project][tokio-security]. 4 | 5 | ## Report a security issue 6 | 7 | The process for reporting an issue is the same as the [Tokio project][tokio-security]. This includes private reporting via security@tokio.rs. 8 | 9 | [tokio-security]: https://github.com/tokio-rs/tokio/security/policy 10 | -------------------------------------------------------------------------------- /codegen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "codegen" 3 | authors = ["Lucio Franco "] 4 | license = "MIT" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | protox = "0.8" 9 | prettyplease = "0.2" 10 | quote = "1" 11 | syn = "2" 12 | tempfile = "3.8.0" 13 | tonic-build = {path = "../tonic-build", default-features = false, features = ["prost", "cleanup-markdown"]} 14 | -------------------------------------------------------------------------------- /examples/data/tls/ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIE3DCCA0SgAwIBAgIRAObeYbJFiVQSGR8yk44dsOYwDQYJKoZIhvcNAQELBQAw 3 | gYUxHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTEtMCsGA1UECwwkbHVj 4 | aW9ATHVjaW9zLVdvcmstTUJQIChMdWNpbyBGcmFuY28pMTQwMgYDVQQDDCtta2Nl 5 | cnQgbHVjaW9ATHVjaW9zLVdvcmstTUJQIChMdWNpbyBGcmFuY28pMB4XDTE5MDky 6 | OTIzMzUzM1oXDTI5MDkyOTIzMzUzM1owgYUxHjAcBgNVBAoTFW1rY2VydCBkZXZl 7 | bG9wbWVudCBDQTEtMCsGA1UECwwkbHVjaW9ATHVjaW9zLVdvcmstTUJQIChMdWNp 8 | byBGcmFuY28pMTQwMgYDVQQDDCtta2NlcnQgbHVjaW9ATHVjaW9zLVdvcmstTUJQ 9 | IChMdWNpbyBGcmFuY28pMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA 10 | y/vE61ItbN/1qMYt13LMf+le1svwfkCCOPsygk7nWeRXmomgUpymqn1LnWiuB0+e 11 | 4IdVH2f5E9DknWEpPhKIDMRTCbz4jTwQfHrxCb8EGj3I8oO73pJO5S/xCedM9OrZ 12 | qWcYWwN0GQ8cO/ogazaoZf1uTrRNHyzRyQsKyb412kDBTNEeldJZ2ljKgXXvh4HO 13 | 2ZIk9K/ZAaAf6VN8K/89rlJ9/KPgRVNsyAapE+Pb8XXKtpzeFiEcUfuXVYWtkoW+ 14 | xyn/Zu8A1L2CXMQ1sARh7P/42BTMKr5pfraYgcBGxKXLrxoySpxCO9KqeVveKy1q 15 | fPm5FCwFsXDr0koFLrCiR58mcIO/04Q9DKKTV4Z2a+LoqDJRY37KfBSc8sDMPhw5 16 | k7g3WPoa6QwXRjZTCA5fHWVgLOtcwLsnju5tBE4LDxwF6s+1wPF8NI5yUfufcEjJ 17 | Z6JBwgoWYosVj27Lx7KBNLU/57PX9ryee691zmtswt0tP0WVBAgalhYWg99RXoa3 18 | AgMBAAGjRTBDMA4GA1UdDwEB/wQEAwICBDASBgNVHRMBAf8ECDAGAQH/AgEAMB0G 19 | A1UdDgQWBBQdvlE4Bdcsjc9oaxjDCRu5FiuZkzANBgkqhkiG9w0BAQsFAAOCAYEA 20 | BP/6o1kPINksMJZSSXgNCPZskDLyGw7auUZBnQ0ocDT3W6gXQvT/27LM1Hxoj9Eh 21 | qU1TYdEt7ppecLQSGvzQ02MExG7H75art75oLiB+A5agDira937YbK4MCjqW481d 22 | bDhw6ixJnY1jIvwjEZxyH6g94YyL927aSPch51fys0kSnjkFzC2RmuzDADScc4XH 23 | 5P1+/3dnIm3M5yfpeUzoaOrTXNmhn8p0RDIGrZ5kA5eISIGGD3Mm8FDssUNKndtO 24 | g4ojHUsxb14icnAYGeye1NOhGiqN6TEFcgr6MPd0XdFNZ5c0HUaBCfN6bc+JxDV5 25 | MKZVJdNeJsYYwilgJNHAyZgCi30JC20xeYVtTF7CEEsMrFDGJ70Kz7o/FnRiFsA1 26 | ZSwVVWhhkHG2VkT4vlo0O3fYeZpenYicvy+wZNTbGK83gzHWqxxNC1z3Etg5+HRJ 27 | F9qeMWPyfA3IHYXygiMcviyLcyNGG/SJ0EhUpYBN/Gg7wI5yFkcsxUDPPzd23O0M 28 | -----END CERTIFICATE----- 29 | -------------------------------------------------------------------------------- /examples/data/tls/client1.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCiiWrmzpENsI+c 3 | Cz4aBpG+Pl8WOsrByfZx/ZnJdCZHO3MTYE6sCLhYssf0ygAEEGxvmkd4cxmfCfgf 4 | xuT8u+D7Y5zQSoymkbWdU6/9jbNY6Ovtc+a96I1LGXOKROQw6KR3PuqLpUqEOJiB 5 | l03qK+HMU0g56G1n31Od7HkJsDRvtePqy3I3LgpdcRps23sk46tCzZzhyfqIQ7Qf 6 | J5qZx93tA+pfy+Xtb9XIUTIWKIp1/uyfh8Fp8HA0c9zJCSZzJOX2j3GH1TYqkVgP 7 | egI2lhmdXhP5Q8vdhwy0UJaL28RJXA6UAg0tPZeWJe6pux9JiA81sI6My+Krrw8D 8 | yibkGTTbAgMBAAECggEANCQhRym9HsclSsnQgkjZOE6J8nep08EWbjsMurOoE/He 9 | WLjshAPIH6w6uSyUFLmwD51OkDVcYsiv8IG9s9YRtpOeGrPPqx/TQ0U1kAGFJ2CR 10 | Tvt/aizQJudjSVgQXCBFontsgp/j58bAJdKEDDtHlGSjJvCJKGlcSa0ypwj/yVXt 11 | frjROJNYzw9gMM7fN/IKF/cysdXSeLl/Q9RnHVIfC3jOFJutsILCK8+PC51dM8Fl 12 | IOjmPmiZ080yV8RBcMRECwl53vLOE3OOpR3ZijfNCY1KU8zWi1oELJ1o6f4+cBye 13 | 7WPgFEoBew5XHXZ+ke8rh8cc0wth7ZTcC+xC/456AQKBgQDQr2EzBwXxYLF8qsN1 14 | R4zlzXILLdZN8a4bKfrS507/Gi1gDBHzfvbE7HfljeqrAkbKMdKNkbz3iS85SguH 15 | jsM047xUGJg0PAcwBLHUedlSn1xDDcDHW6X8ginpA2Zz1+WAlhNz6XurA1wnjZmS 16 | VcPxopH7QsuFCclqtt14MbBQ6QKBgQDHY3jcAVfQF+yhQ0YyM6GPLN342aTplgyJ 17 | yz4uWVMeXacU4QzqGbf2L2hc9M2L28Xb37RWC3Q/by0vUefiC6qxRt+GJdRsOuQj 18 | 2F1uUibeWtAWp249fcfvxjLib276J+Eit18LI0s0mNR3ekK4GcjSe4NwSq5IrU8e 19 | pBreet3dIwKBgQCxVuil4WkGd+I8jC0v5A7zVsR8hYZhlGkdgm45fgHevdMjlP5I 20 | S3PPYxh8hj6O9o9L0k0Yq2nHfdgYujjUCNkQgBuR55iogv6kqsioRKgPE4fnH6/c 21 | eqCy1bZh4tbUyPqqbF65mQfUCzXsEuQXvDSYiku+F0Q2mVuGCUJpmug3yQKBgEd3 22 | LeCdUp4xlQ0QEd74hpXM3RrO178pmwDgqj7uoU4m/zYKnBhkc3137I406F+SvE5c 23 | 1kRpApeh/64QS27IA7xazM9GS+cnDJKUgJiENY5JOoCELo03wiv8/EwQ6NQc6yMI 24 | WrahRdlqVe0lEzjtdP+MacYb3nAKPmubIk5P96nFAoGAFAyrKpFTyXbNYBTw9Rab 25 | TG6q7qkn+YTHN3+k4mo9NGGwZ3pXvmrKMYCIRhLMbqzsmTbFqCPPIxKsrmf8QYLh 26 | xHYQjrCkbZ0wZdcdeV6yFSDsF218nF/12ZPE7CBOQMfZTCKFNWGL97uIVcmR6K5G 27 | ojTkOvaUnwQtSFhNuzyr23I= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /examples/data/tls/client1.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDCTCCAfGgAwIBAgIQYbE9d1Rft5h4ku7FSAvWdzANBgkqhkiG9w0BAQsFADAn 3 | MSUwIwYDVQQDExxUb25pYyBFeGFtcGxlIENsaWVudCBSb290IENBMB4XDTE5MTAx 4 | NDEyMzkzNloXDTI0MTAxMjEyMzkzNlowEjEQMA4GA1UEAxMHY2xpZW50MTCCASIw 5 | DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKKJaubOkQ2wj5wLPhoGkb4+XxY6 6 | ysHJ9nH9mcl0Jkc7cxNgTqwIuFiyx/TKAAQQbG+aR3hzGZ8J+B/G5Py74PtjnNBK 7 | jKaRtZ1Tr/2Ns1jo6+1z5r3ojUsZc4pE5DDopHc+6oulSoQ4mIGXTeor4cxTSDno 8 | bWffU53seQmwNG+14+rLcjcuCl1xGmzbeyTjq0LNnOHJ+ohDtB8nmpnH3e0D6l/L 9 | 5e1v1chRMhYoinX+7J+HwWnwcDRz3MkJJnMk5faPcYfVNiqRWA96AjaWGZ1eE/lD 10 | y92HDLRQlovbxElcDpQCDS09l5Yl7qm7H0mIDzWwjozL4quvDwPKJuQZNNsCAwEA 11 | AaNGMEQwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAfBgNVHSME 12 | GDAWgBQV1YOR+Jpl1fbujvWLSBEoRvsDhTANBgkqhkiG9w0BAQsFAAOCAQEAfTPu 13 | KeHXmyVTSCUrYQ1X5Mu7VzfZlRbhoytHOw7bYGgwaFwQj+ZhlPt8nFC22/bEk4IV 14 | AoCOli0WyPIB7Lx52dZ+v9JmYOK6ca2Aa/Dkw8Q+M3XA024FQWq3nZ6qANKC32/9 15 | Nk+xOcb1Qd/11stpTkRf2Oj7F7K4GnlFbY6iMyNW+RFXGKEbL5QAJDTDPIT8vw1x 16 | oYeNPwmC042uEboCZPNXmuctiK9Wt1TAxjZT/cwdIBGGJ+xrW72abfJGs7bUcJfc 17 | O4r9V0xVv+X0iKWTW0fwd9qjNfiEP1tFCcZb2XsNQPe/DlQZ+h98P073tZEsWI/G 18 | KJrFspGX8vOuSdIeqw== 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /examples/data/tls/client2.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCvpRVRx7joJVf3 3 | dIcDih5lY0Z+Y2MoDX83iwNF4QTfMibULyXffOnC5XcvayS/W8ToxaLweNE2bIwa 4 | krl3K+R/4tdI9ZguSgHFBMRCNp33ZDGxD01XHduETXXdHXxKufxLNjCFtAWwM+GF 5 | KbYzEdeyMmV9yYc3ihW3mg+7rzaUwU+hK7LjhTi/YW7TJF53UGIkEQuPKYUfBCha 6 | 01CWAJTJDI7hrH7asfIG690Wm3uWNj2wsFVk2ZtyRIis2aQOE4tknG3yTKjnLGBH 7 | L+DQ+2UljuVyHi1pwCVN+zvoG/0RiiHWs6kW/zAompoxWhWPucKgssjC/JsWVKKp 8 | TpfOfEy7AgMBAAECggEAFclTQKaWT+054Q7KJG1AYfETcF/hj7lE213Z1RQZJ5ov 9 | 6MfEWdlDoZIW24HduAKpBPpmwI8r3CVQp4cljBucpyQ68ejMrIkveQGjWlct9t6t 10 | rzmnrTOd4+Y7xWZ/4UD6g1XAZQU0Y2u8AGlxGRqQd6D6p8SUihYNpY1tgCk2ivLO 11 | znGZau0Gdt2f6Pv9C0cy5R2832a+3V1X9oCeeXVP8kM1dBKbWQTVxhYaxJxMZifn 12 | qfJi3sjEJZZ05NEFKwKpYx7gUEtC1d/zvvL/VSjihcyCYk728jr40uoeBpBUiUsR 13 | G0N1/xGNGHuRAGFjQSyrACtz1hRSPFmTyRhr6vrRGQKBgQDOFWKl6HjVB/WZ6ZC+ 14 | BXMhUvCKz99lrOO1TbDAI8Tn26D+OSXhywXWPf++HhTZuND460I+79rPH+tJiFw4 15 | Y5z9JFce0MYPNcd/K7JBiV7zBBYof40UbBw8Eff7mWLaOXtI9uwItRXRBs+fIPRM 16 | eCgVUid7pe//oAQ8npcmQEVxjwKBgQDaMEmrTo0+RpT3sS1m9pCQc2LjiKmhWP1L 17 | N/IxZn5Ey9Ao+ibVIQzb69HMUBJ1EZGNZZkEcCguWICIov8rO10xIrD3m9rWgAVg 18 | HlVFlcSTrg0/3cVd26msJKW4pjKA/GmYVhP8o027Zy5KtZ/2jJCh2UjSLsY+acEq 19 | +Qr972BEFQKBgHOwAZ7NL/e27iKmwUBK4uSUIMBsDSaQtYtzv4M9ES5vVqMgBaoJ 20 | RI+OYmChlmban0T9HEUkdJrNelHfIJXvJZPdsKJ15JlpQUKcjwbHTOvzIVU+tT3/ 21 | qqH2HFW7N4j1t8WwB7Sjo0miHy9fWoUK9sVxRwTclCvV8krtZEBu2Az1AoGAGZoU 22 | 6t76v9X0YOQPWceQywJfFifRD7ercQoNhzJpmpT3xfckW1nXcm7HXVv/7nCzTY4g 23 | WF74uAd2fZHysxXyJ3PUpBlLomO/PboRc2rReCqyL05MfGjsDeD2+SW3Q19a3J8t 24 | FTXsRxMiYW3SaVGxHuyqGM+YP3aVTf+PBKD0AMkCgYEAyKtuxwBjPjO1pyOn195e 25 | UY2oaZq4PfaK04Eu5uCmOD2vBcgJWZ8VJNXPlRL5vlCx9LrLB9vJDyXgSurqJAH6 26 | M8/7tR/xlyMix+Jq47209kfOXMMdSGf55/xERmN6vPuhLtPwtj0ZduJzq6gwnlvs 27 | gG29TwA5xp9S4A1amFSrJBo= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /examples/data/tls/client2.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDCjCCAfKgAwIBAgIRAKD72aj29NawAKPAQkcm7QcwDQYJKoZIhvcNAQELBQAw 3 | JzElMCMGA1UEAxMcVG9uaWMgRXhhbXBsZSBDbGllbnQgUm9vdCBDQTAeFw0xOTEw 4 | MTQxMjM5MzZaFw0yNDEwMTIxMjM5MzZaMBIxEDAOBgNVBAMTB2NsaWVudDIwggEi 5 | MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvpRVRx7joJVf3dIcDih5lY0Z+ 6 | Y2MoDX83iwNF4QTfMibULyXffOnC5XcvayS/W8ToxaLweNE2bIwakrl3K+R/4tdI 7 | 9ZguSgHFBMRCNp33ZDGxD01XHduETXXdHXxKufxLNjCFtAWwM+GFKbYzEdeyMmV9 8 | yYc3ihW3mg+7rzaUwU+hK7LjhTi/YW7TJF53UGIkEQuPKYUfBCha01CWAJTJDI7h 9 | rH7asfIG690Wm3uWNj2wsFVk2ZtyRIis2aQOE4tknG3yTKjnLGBHL+DQ+2UljuVy 10 | Hi1pwCVN+zvoG/0RiiHWs6kW/zAompoxWhWPucKgssjC/JsWVKKpTpfOfEy7AgMB 11 | AAGjRjBEMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHwYDVR0j 12 | BBgwFoAUFdWDkfiaZdX27o71i0gRKEb7A4UwDQYJKoZIhvcNAQELBQADggEBAF5F 13 | 2A3V2GGZo7AdztxWaPd6Nu/8VbQWGEeZeWFpQEloNiur96KDcndXXrS6GAyL31d9 14 | vfV2IA2yrB/2przFVRjfNnTj7+xNEtp23iMW8qhMIeHQs1IYu+HqHWDPEJfphTPd 15 | 4Tu5M+ciE7KSZgPsV15piPgst2dqDvghBqxE6t3UnR1wQ5b3wWCt1O9NO+obeV9a 16 | 1lzlYw2NjdHxmrqXLS9I5eYqEo3JtRQrOu3LUd7LqdlEO0jHMaxblxTgwJDoj/9C 17 | CnXp3sNewqsNHW17dsKavMJ2LFXukqLjonvz8NjxeetD09OzMEsg8Tx/1nOQPUBw 18 | rtVdBkQ/TxiNTlTtdZc= 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /examples/data/tls/client_ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDGzCCAgOgAwIBAgIRAMNWpWRu6Q1txEYUyrkyXKEwDQYJKoZIhvcNAQELBQAw 3 | JzElMCMGA1UEAxMcVG9uaWMgRXhhbXBsZSBDbGllbnQgUm9vdCBDQTAeFw0xOTEw 4 | MTQxMjM5MzZaFw0yOTEwMTExMjM5MzZaMCcxJTAjBgNVBAMTHFRvbmljIEV4YW1w 5 | bGUgQ2xpZW50IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB 6 | AQCv8Nj4XJbMI0wWUvLbmCf7IEvJFnomodGnDurh8Y5AGMPJ8cGdZC1yo2Lgah+D 7 | IhXdsd72Wp7MhdntJAyPrMCDBfDrFiuj6YHDgt3OhPQSYl7EWG7QjFK3B2sp1K5D 8 | h16G5zfwUKDj9Jp3xuPGuqNFQHL02nwbhtDilqHvaTfOJKVjsFCoU8Z77mfwXSwn 9 | sPXpPB7oOO4mWfAtcwU11rTMiHFSGFlFhgbHULU/y90DcpfRQEpEiBoiK13gkyoP 10 | zHT9WAg3Pelwb6K7c7kJ7mp4axhbf7MkwFhDQIjbBWqus2Eu3b0mf86ALfDbAaNC 11 | wBi8xbNH2vWaDjiwLDY5uMZDAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwICBDAPBgNV 12 | HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQV1YOR+Jpl1fbujvWLSBEoRvsDhTANBgkq 13 | hkiG9w0BAQsFAAOCAQEAaXmM29TYkFUzZUsV7TSonAK560BjxDmbg0GJSUgLEFUJ 14 | wpKqa9UKOSapG45LEeR2wwAmVWDJomJplkuvTD/KOabAbZKyPEfp+VMCaBUnILQF 15 | Cxv5m7kQ3wmPS/rEL8FD809UGowW9cYqnZzUy5i/r263rx0k3OPjkkZN66Mh6+3H 16 | ibNdaxf7ITO0JVb/Ohq9vLC9qf7ujiB1atMdJwkOWsZrLJXLygpx/D0/UhBT4fFH 17 | OlyVOmuR27qaMbPgOs2l8DznkJY/QUfnET8iOQhFgb0Dt/Os4PYFhSDRIrgl5dJ7 18 | L/zZVQfZYpdxlBHJlDC1/NzVQl/1MgDnSgPGStZKPQ== 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /examples/data/tls/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDyptbMyYWztgta 3 | t1MXLMzIkaQdeeVbs1Y/qCpAdwZe/Y5ZpbzjGIjCxbB6vNRSnEbYKpytKHPzYfM7 4 | 8d8K8bPvpnqXIiTXFT0JQlw1OHLC1fr4e598GJumAmpMYFrtqv0fbmUFTuQGbHxe 5 | OH2vji0bvr3NKZubMfkEZP3X4sNXXoXIuW2LaS8OMGKoJaeCBvdbszEiSGj/v9Bj 6 | pM0yLTH89NNMX1T+FtTKnuXag5g7pr6lzJj83+MzAGy4nOjseSuUimuiyG90/C5t 7 | A5wC0Qh5RbDnkFYhC44Kxof/i6+jnfateIPNiIIwQV+2f6G/aK1hgjekT10m/eoR 8 | YDTf+e5ZAgMBAAECggEACODt7yRYjhDVLYaTtb9f5t7dYG67Y7WWLFIc6arxQryI 9 | XuNfm/ej2WyeXn9WTYeGWBaHERbv1zH4UnMxNBdP/C7dQXZwXqZaS2JwOUpNeK+X 10 | tUvgtAu6dkKUXSMRcKzXAjVp4N3YHhwOGOx8PNY49FDwZPdmyDD16aFAYIvdle6/ 11 | PSMrj38rB1sbQQdmRob2FjJBSDZ44nsr+/nilrcOFNfNnWv7tQIWYVXNcLfdK/WJ 12 | ZCDFhA8lr/Yon6MEq6ApTj2ZYRRGXPd6UeASJkmTZEUIUbeDcje/MO8cHkREpuRH 13 | wm3pCjR7OdO4vc+/d/QmEvu5ns6wbTauelYnL616YQKBgQD414gJtpCHauNEUlFB 14 | v/R3DzPI5NGp9PAqovOD8nCbI49Mw61gP/ExTIPKiR5uUX/5EL04uspaNkuohXk+ 15 | ys0G5At0NfV7W39lzhvALEaSfleybvYxppbBrc20/q8Gvi/i30NY+1LM3RdtMiEw 16 | hKHjU0SnFhJq0InFg3AO/iCeTQKBgQD5obkbzpOidSsa55aNsUlO2qjiUY9leq9b 17 | irAohIZ8YnuuixYvkOeSeSz1eIrA4tECeAFSgTZxYe1Iz+USru2Xg/0xNte11dJD 18 | rBoH/yMn2gDvBK7xQ6uFMPTeYtKG0vfvpXZYSWZzGntyrHTwFk6UV+xdrt9MBdd1 19 | XdSn7bwOPQKBgC9VQAko8uDvUf+C8PXiv2uONrl13PPJJY3WpR9qFEVOREnDxszS 20 | HNzVwxPZdTJiykbkCjoqPadfQJDzopZxGQLAifU29lTamKcSx3CMe3gOFDxaovXa 21 | zD5XAxP0hfJwZsdu1G6uj5dsTrJ0oJ+L+wc0pZBqwGIU/L/XOo9/g1DZAoGAUebL 22 | kuH98ik7EUK2VJq8EJERI9/ailLsQb6I+WIxtZGiPqwHhWencpkrNQZtj8dbB9JT 23 | rLwUHrMgZOlAoRafgTyez4zMzS3wJJ/Mkp8U67hM4h7JPwMSvUpIrMYDiJSjIA9L 24 | er/qSw1/Pypx22uWMHmAZWRAgvLPtAQrB0Wqk4kCgYEAr2H1PvfbwZwkSvlMt5o8 25 | WLnBbxcM3AKglLRbkShxxgiZYdEP71/uOtRMiL26du5XX8evItITN0DsvmXL/kcd 26 | h29LK7LM5uLw7efz0Qxs03G6kEyIHVkacowHi5I5Ul1qI61SoV3yMB1TjIU+bXZt 27 | 0ZjC07totO0fqPOLQxonjQg= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /examples/data/tls/server.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEmDCCAwCgAwIBAgIQVEJFCgU/CZk9JEwTucWPpzANBgkqhkiG9w0BAQsFADCB 3 | hTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMS0wKwYDVQQLDCRsdWNp 4 | b0BMdWNpb3MtV29yay1NQlAgKEx1Y2lvIEZyYW5jbykxNDAyBgNVBAMMK21rY2Vy 5 | dCBsdWNpb0BMdWNpb3MtV29yay1NQlAgKEx1Y2lvIEZyYW5jbykwHhcNMTkwNjAx 6 | MDAwMDAwWhcNMjkwOTI5MjMzNTM0WjBYMScwJQYDVQQKEx5ta2NlcnQgZGV2ZWxv 7 | cG1lbnQgY2VydGlmaWNhdGUxLTArBgNVBAsMJGx1Y2lvQEx1Y2lvcy1Xb3JrLU1C 8 | UCAoTHVjaW8gRnJhbmNvKTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB 9 | APKm1szJhbO2C1q3UxcszMiRpB155VuzVj+oKkB3Bl79jlmlvOMYiMLFsHq81FKc 10 | RtgqnK0oc/Nh8zvx3wrxs++mepciJNcVPQlCXDU4csLV+vh7n3wYm6YCakxgWu2q 11 | /R9uZQVO5AZsfF44fa+OLRu+vc0pm5sx+QRk/dfiw1dehci5bYtpLw4wYqglp4IG 12 | 91uzMSJIaP+/0GOkzTItMfz000xfVP4W1Mqe5dqDmDumvqXMmPzf4zMAbLic6Ox5 13 | K5SKa6LIb3T8Lm0DnALRCHlFsOeQViELjgrGh/+Lr6Od9q14g82IgjBBX7Z/ob9o 14 | rWGCN6RPXSb96hFgNN/57lkCAwEAAaOBrzCBrDAOBgNVHQ8BAf8EBAMCBaAwEwYD 15 | VR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBQdvlE4 16 | Bdcsjc9oaxjDCRu5FiuZkzBWBgNVHREETzBNggtleGFtcGxlLmNvbYINKi5leGFt 17 | cGxlLmNvbYIMZXhhbXBsZS50ZXN0gglsb2NhbGhvc3SHBH8AAAGHEAAAAAAAAAAA 18 | AAAAAAAAAAEwDQYJKoZIhvcNAQELBQADggGBAKb2TJ8l+e1eraNwZWizLw5fccAf 19 | y59J1JAWdLxZyAI/bkiTlVO3DQoPZpw7XwLhefCvILkwKAL4TtIGGVC9yTb5Q5eg 20 | rqGO3FC0yg1fn65Kf1VpVxxUVyoiM5PQ4pFJb4AicAv88rCOLD9FFuE0PKOKU/dm 21 | Tw0WgPStoh9wsJ1RXUuTJYZs1nd1kMBlfv9NbLilnL+cR2sLktS54X5XagsBYVlf 22 | oapRb0JtABOoQhX3U8QMq8UF8yzceRHNTN9yfLOUrW26s9nKtlWVniNhw1uPxZw9 23 | RHM7w9/4+a9LXtEDYg4IP/1mm0ywBoUqy1O6hA73uId+Yi/kFBks/GyYaGjKgYcO 24 | 23B75tkPGYEdGuGZYLzZNHbXg4V0UxFQG3KA1pUiSnD3bN2Rxs+CMpzORnOeK3xi 25 | EooKgAPYsehItoQOMPpccI2xHdSAMWtwUgOKrefUQujkx2Op+KFlspF0+WJ6AZEe 26 | 2D4hyWaEZsvvILXapwqHDCuN3/jSUlTIqUoE1w== 27 | -----END CERTIFICATE----- 28 | -------------------------------------------------------------------------------- /examples/proto/attrs/attrs.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package attrs; 4 | 5 | // EchoRequest is the request for echo. 6 | message EchoRequest { 7 | string message = 1; 8 | } 9 | 10 | // EchoResponse is the response for echo. 11 | message EchoResponse { 12 | string message = 1; 13 | } 14 | 15 | // Echo is the echo service. 16 | service Echo { 17 | // UnaryEcho is unary echo. 18 | rpc UnaryEcho(EchoRequest) returns (EchoResponse) {} 19 | } -------------------------------------------------------------------------------- /examples/proto/echo/echo.proto: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2018 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | syntax = "proto3"; 20 | 21 | package grpc.examples.echo; 22 | 23 | // EchoRequest is the request for echo. 24 | message EchoRequest { 25 | string message = 1; 26 | } 27 | 28 | // EchoResponse is the response for echo. 29 | message EchoResponse { 30 | string message = 1; 31 | } 32 | 33 | // Echo is the echo service. 34 | service Echo { 35 | // UnaryEcho is unary echo. 36 | rpc UnaryEcho(EchoRequest) returns (EchoResponse) {} 37 | // ServerStreamingEcho is server side streaming. 38 | rpc ServerStreamingEcho(EchoRequest) returns (stream EchoResponse) {} 39 | // ClientStreamingEcho is client side streaming. 40 | rpc ClientStreamingEcho(stream EchoRequest) returns (EchoResponse) {} 41 | // BidirectionalStreamingEcho is bidi streaming. 42 | rpc BidirectionalStreamingEcho(stream EchoRequest) returns (stream EchoResponse) {} 43 | } -------------------------------------------------------------------------------- /examples/proto/googleapis/google/api/annotations.proto: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package google.api; 18 | 19 | import "google/api/http.proto"; 20 | import "google/protobuf/descriptor.proto"; 21 | 22 | option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; 23 | option java_multiple_files = true; 24 | option java_outer_classname = "AnnotationsProto"; 25 | option java_package = "com.google.api"; 26 | option objc_class_prefix = "GAPI"; 27 | 28 | extend google.protobuf.MethodOptions { 29 | // See `HttpRule`. 30 | HttpRule http = 72295728; 31 | } 32 | -------------------------------------------------------------------------------- /examples/proto/helloworld/helloworld.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2015 gRPC authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | option java_multiple_files = true; 18 | option java_package = "io.grpc.examples.helloworld"; 19 | option java_outer_classname = "HelloWorldProto"; 20 | 21 | package helloworld; 22 | 23 | // The greeting service definition. 24 | service Greeter { 25 | // Sends a greeting 26 | rpc SayHello (HelloRequest) returns (HelloReply) {} 27 | } 28 | 29 | // The request message containing the user's name. 30 | message HelloRequest { 31 | string name = 1; 32 | } 33 | 34 | // The response message containing the greetings 35 | message HelloReply { 36 | string message = 1; 37 | } -------------------------------------------------------------------------------- /examples/proto/unaryecho/echo.proto: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2018 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | syntax = "proto3"; 20 | 21 | package grpc.examples.unaryecho; 22 | 23 | // EchoRequest is the request for echo. 24 | message EchoRequest { 25 | string message = 1; 26 | } 27 | 28 | // EchoResponse is the response for echo. 29 | message EchoResponse { 30 | string message = 1; 31 | } 32 | 33 | // Echo is the echo service. 34 | service Echo { 35 | // UnaryEcho is unary echo. 36 | rpc UnaryEcho(EchoRequest) returns (EchoResponse) {} 37 | } 38 | -------------------------------------------------------------------------------- /examples/src/authentication/client.rs: -------------------------------------------------------------------------------- 1 | pub mod pb { 2 | tonic::include_proto!("grpc.examples.unaryecho"); 3 | } 4 | 5 | use pb::{echo_client::EchoClient, EchoRequest}; 6 | use tonic::{metadata::MetadataValue, transport::Channel, Request}; 7 | 8 | #[tokio::main] 9 | async fn main() -> Result<(), Box> { 10 | let channel = Channel::from_static("http://[::1]:50051").connect().await?; 11 | 12 | let token: MetadataValue<_> = "Bearer some-auth-token".parse()?; 13 | 14 | let mut client = EchoClient::with_interceptor(channel, move |mut req: Request<()>| { 15 | req.metadata_mut().insert("authorization", token.clone()); 16 | Ok(req) 17 | }); 18 | 19 | let request = tonic::Request::new(EchoRequest { 20 | message: "hello".into(), 21 | }); 22 | 23 | let response = client.unary_echo(request).await?; 24 | 25 | println!("RESPONSE={response:?}"); 26 | 27 | Ok(()) 28 | } 29 | -------------------------------------------------------------------------------- /examples/src/authentication/server.rs: -------------------------------------------------------------------------------- 1 | pub mod pb { 2 | tonic::include_proto!("grpc.examples.unaryecho"); 3 | } 4 | 5 | use pb::{EchoRequest, EchoResponse}; 6 | use tonic::{metadata::MetadataValue, transport::Server, Request, Response, Status}; 7 | 8 | type EchoResult = Result, Status>; 9 | 10 | #[derive(Default)] 11 | pub struct EchoServer {} 12 | 13 | #[tonic::async_trait] 14 | impl pb::echo_server::Echo for EchoServer { 15 | async fn unary_echo(&self, request: Request) -> EchoResult { 16 | let message = request.into_inner().message; 17 | Ok(Response::new(EchoResponse { message })) 18 | } 19 | } 20 | 21 | #[tokio::main] 22 | async fn main() -> Result<(), Box> { 23 | let addr = "[::1]:50051".parse().unwrap(); 24 | let server = EchoServer::default(); 25 | 26 | let svc = pb::echo_server::EchoServer::with_interceptor(server, check_auth); 27 | 28 | Server::builder().add_service(svc).serve(addr).await?; 29 | 30 | Ok(()) 31 | } 32 | 33 | fn check_auth(req: Request<()>) -> Result, Status> { 34 | let token: MetadataValue<_> = "Bearer some-secret-token".parse().unwrap(); 35 | 36 | match req.metadata().get("authorization") { 37 | Some(t) if token == t => Ok(req), 38 | _ => Err(Status::unauthenticated("No valid auth token")), 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /examples/src/autoreload/server.rs: -------------------------------------------------------------------------------- 1 | use tonic::{transport::Server, Request, Response, Status}; 2 | 3 | use hello_world::greeter_server::{Greeter, GreeterServer}; 4 | use hello_world::{HelloReply, HelloRequest}; 5 | 6 | pub mod hello_world { 7 | tonic::include_proto!("helloworld"); 8 | } 9 | 10 | #[derive(Default)] 11 | pub struct MyGreeter {} 12 | 13 | #[tonic::async_trait] 14 | impl Greeter for MyGreeter { 15 | async fn say_hello( 16 | &self, 17 | request: Request, 18 | ) -> Result, Status> { 19 | println!("Got a request from {:?}", request.remote_addr()); 20 | 21 | let reply = hello_world::HelloReply { 22 | message: format!("Hello {}!", request.into_inner().name), 23 | }; 24 | Ok(Response::new(reply)) 25 | } 26 | } 27 | 28 | #[tokio::main] 29 | async fn main() -> Result<(), Box> { 30 | let addr = "[::1]:50051".parse().unwrap(); 31 | let greeter = MyGreeter::default(); 32 | 33 | println!("GreeterServer listening on {addr}"); 34 | 35 | let server = Server::builder().add_service(GreeterServer::new(greeter)); 36 | 37 | match listenfd::ListenFd::from_env().take_tcp_listener(0)? { 38 | Some(listener) => { 39 | listener.set_nonblocking(true)?; 40 | let listener = tokio_stream::wrappers::TcpListenerStream::new( 41 | tokio::net::TcpListener::from_std(listener)?, 42 | ); 43 | 44 | server.serve_with_incoming(listener).await?; 45 | } 46 | None => { 47 | server.serve(addr).await?; 48 | } 49 | } 50 | 51 | Ok(()) 52 | } 53 | -------------------------------------------------------------------------------- /examples/src/blocking/client.rs: -------------------------------------------------------------------------------- 1 | use tokio::runtime::{Builder, Runtime}; 2 | 3 | pub mod hello_world { 4 | tonic::include_proto!("helloworld"); 5 | } 6 | 7 | use hello_world::{greeter_client::GreeterClient, HelloReply, HelloRequest}; 8 | 9 | type StdError = Box; 10 | type Result = ::std::result::Result; 11 | 12 | // The order of the fields in this struct is important. They must be ordered 13 | // such that when `BlockingClient` is dropped the client is dropped 14 | // before the runtime. Not doing this will result in a deadlock when dropped. 15 | // Rust drops struct fields in declaration order. 16 | struct BlockingClient { 17 | client: GreeterClient, 18 | rt: Runtime, 19 | } 20 | 21 | impl BlockingClient { 22 | pub fn connect(dst: D) -> Result 23 | where 24 | D: TryInto, 25 | D::Error: Into, 26 | { 27 | let rt = Builder::new_multi_thread().enable_all().build().unwrap(); 28 | let client = rt.block_on(GreeterClient::connect(dst))?; 29 | 30 | Ok(Self { client, rt }) 31 | } 32 | 33 | pub fn say_hello( 34 | &mut self, 35 | request: impl tonic::IntoRequest, 36 | ) -> Result, tonic::Status> { 37 | self.rt.block_on(self.client.say_hello(request)) 38 | } 39 | } 40 | 41 | fn main() -> Result<()> { 42 | let mut client = BlockingClient::connect("http://[::1]:50051")?; 43 | 44 | let request = tonic::Request::new(HelloRequest { 45 | name: "Tonic".into(), 46 | }); 47 | 48 | let response = client.say_hello(request)?; 49 | 50 | println!("RESPONSE={response:?}"); 51 | 52 | Ok(()) 53 | } 54 | -------------------------------------------------------------------------------- /examples/src/blocking/server.rs: -------------------------------------------------------------------------------- 1 | use tonic::{transport::Server, Request, Response, Status}; 2 | 3 | use hello_world::greeter_server::{Greeter, GreeterServer}; 4 | use hello_world::{HelloReply, HelloRequest}; 5 | 6 | use tokio::runtime::Runtime; 7 | 8 | pub mod hello_world { 9 | tonic::include_proto!("helloworld"); 10 | } 11 | 12 | #[derive(Debug, Default)] 13 | pub struct MyGreeter {} 14 | 15 | #[tonic::async_trait] 16 | impl Greeter for MyGreeter { 17 | async fn say_hello( 18 | &self, 19 | request: Request, 20 | ) -> Result, Status> { 21 | println!("Got a request: {:?}", request); 22 | 23 | let reply = hello_world::HelloReply { 24 | message: format!("Hello {}!", request.into_inner().name), 25 | }; 26 | 27 | Ok(Response::new(reply)) 28 | } 29 | } 30 | 31 | fn main() { 32 | let addr = "[::1]:50051".parse().unwrap(); 33 | let greeter = MyGreeter::default(); 34 | 35 | let rt = Runtime::new().expect("failed to obtain a new RunTime object"); 36 | let server_future = Server::builder() 37 | .add_service(GreeterServer::new(greeter)) 38 | .serve(addr); 39 | rt.block_on(server_future) 40 | .expect("failed to successfully run the future on RunTime"); 41 | } 42 | -------------------------------------------------------------------------------- /examples/src/cancellation/client.rs: -------------------------------------------------------------------------------- 1 | use hello_world::greeter_client::GreeterClient; 2 | use hello_world::HelloRequest; 3 | 4 | use tokio::time::{timeout, Duration}; 5 | 6 | pub mod hello_world { 7 | tonic::include_proto!("helloworld"); 8 | } 9 | 10 | #[tokio::main] 11 | async fn main() -> Result<(), Box> { 12 | let mut client = GreeterClient::connect("http://[::1]:50051").await?; 13 | 14 | let request = tonic::Request::new(HelloRequest { 15 | name: "Tonic".into(), 16 | }); 17 | 18 | // Cancelling the request by dropping the request future after 1 second 19 | let response = match timeout(Duration::from_secs(1), client.say_hello(request)).await { 20 | Ok(response) => response?, 21 | Err(_) => { 22 | println!("Cancelled request after 1s"); 23 | return Ok(()); 24 | } 25 | }; 26 | 27 | println!("RESPONSE={response:?}"); 28 | 29 | Ok(()) 30 | } 31 | -------------------------------------------------------------------------------- /examples/src/codec_buffers/client.rs: -------------------------------------------------------------------------------- 1 | //! A HelloWorld example that uses a custom codec instead of the default Prost codec. 2 | //! 3 | //! Generated code is the output of codegen as defined in the `examples/build.rs` file. 4 | //! The generation is the one with .codec_path("crate::common::SmallBufferCodec") 5 | //! The generated code assumes that a module `crate::common` exists which defines 6 | //! `SmallBufferCodec`, and `SmallBufferCodec` must have a Default implementation. 7 | 8 | pub mod common; 9 | 10 | pub mod small_buf { 11 | include!(concat!(env!("OUT_DIR"), "/smallbuf/helloworld.rs")); 12 | } 13 | use small_buf::greeter_client::GreeterClient; 14 | 15 | use crate::small_buf::HelloRequest; 16 | 17 | #[tokio::main] 18 | async fn main() -> Result<(), Box> { 19 | let mut client = GreeterClient::connect("http://[::1]:50051").await?; 20 | 21 | let request = tonic::Request::new(HelloRequest { 22 | name: "Tonic".into(), 23 | }); 24 | 25 | let response = client.say_hello(request).await?; 26 | 27 | println!("RESPONSE={response:?}"); 28 | 29 | Ok(()) 30 | } 31 | -------------------------------------------------------------------------------- /examples/src/codec_buffers/common.rs: -------------------------------------------------------------------------------- 1 | //! This module defines a common encoder with small buffers. This is useful 2 | //! when you have many concurrent RPC's, and not a huge volume of data per 3 | //! rpc normally. 4 | //! 5 | //! Note that you can customize your codecs per call to the code generator's 6 | //! compile function. This lets you group services by their codec needs. 7 | //! 8 | //! While this codec demonstrates customizing the built-in Prost codec, you 9 | //! can use this to implement other codecs as well, as long as they have a 10 | //! Default implementation. 11 | 12 | use std::marker::PhantomData; 13 | 14 | use prost::Message; 15 | use tonic::codec::{BufferSettings, Codec, ProstCodec}; 16 | 17 | #[derive(Debug, Clone, Copy, Default)] 18 | pub struct SmallBufferCodec(PhantomData<(T, U)>); 19 | 20 | impl Codec for SmallBufferCodec 21 | where 22 | T: Message + Send + 'static, 23 | U: Message + Default + Send + 'static, 24 | { 25 | type Encode = T; 26 | type Decode = U; 27 | 28 | type Encoder = as Codec>::Encoder; 29 | type Decoder = as Codec>::Decoder; 30 | 31 | fn encoder(&mut self) -> Self::Encoder { 32 | // Here, we will just customize the prost codec's internal buffer settings. 33 | // You can of course implement a complete Codec, Encoder, and Decoder if 34 | // you wish! 35 | ProstCodec::::raw_encoder(BufferSettings::new(512, 4096)) 36 | } 37 | 38 | fn decoder(&mut self) -> Self::Decoder { 39 | ProstCodec::::raw_decoder(BufferSettings::new(512, 4096)) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/src/codec_buffers/server.rs: -------------------------------------------------------------------------------- 1 | //! A HelloWorld example that uses a custom codec instead of the default Prost codec. 2 | //! 3 | //! Generated code is the output of codegen as defined in the `examples/build.rs` file. 4 | //! The generation is the one with .codec_path("crate::common::SmallBufferCodec") 5 | //! The generated code assumes that a module `crate::common` exists which defines 6 | //! `SmallBufferCodec`, and `SmallBufferCodec` must have a Default implementation. 7 | 8 | use tonic::{transport::Server, Request, Response, Status}; 9 | 10 | pub mod common; 11 | 12 | pub mod small_buf { 13 | include!(concat!(env!("OUT_DIR"), "/smallbuf/helloworld.rs")); 14 | } 15 | use small_buf::{ 16 | greeter_server::{Greeter, GreeterServer}, 17 | HelloReply, HelloRequest, 18 | }; 19 | 20 | #[derive(Default)] 21 | pub struct MyGreeter {} 22 | 23 | #[tonic::async_trait] 24 | impl Greeter for MyGreeter { 25 | async fn say_hello( 26 | &self, 27 | request: Request, 28 | ) -> Result, Status> { 29 | println!("Got a request from {:?}", request.remote_addr()); 30 | 31 | let reply = HelloReply { 32 | message: format!("Hello {}!", request.into_inner().name), 33 | }; 34 | Ok(Response::new(reply)) 35 | } 36 | } 37 | 38 | #[tokio::main] 39 | async fn main() -> Result<(), Box> { 40 | let addr = "[::1]:50051".parse().unwrap(); 41 | let greeter = MyGreeter::default(); 42 | 43 | println!("GreeterServer listening on {addr}"); 44 | 45 | Server::builder() 46 | .add_service(GreeterServer::new(greeter)) 47 | .serve(addr) 48 | .await?; 49 | 50 | Ok(()) 51 | } 52 | -------------------------------------------------------------------------------- /examples/src/compression/client.rs: -------------------------------------------------------------------------------- 1 | use hello_world::greeter_client::GreeterClient; 2 | use hello_world::HelloRequest; 3 | use tonic::codec::CompressionEncoding; 4 | use tonic::transport::Channel; 5 | 6 | pub mod hello_world { 7 | tonic::include_proto!("helloworld"); 8 | } 9 | 10 | #[tokio::main] 11 | async fn main() -> Result<(), Box> { 12 | let channel = Channel::builder("http://[::1]:50051".parse().unwrap()) 13 | .connect() 14 | .await 15 | .unwrap(); 16 | 17 | let mut client = GreeterClient::new(channel) 18 | .send_compressed(CompressionEncoding::Gzip) 19 | .accept_compressed(CompressionEncoding::Gzip); 20 | 21 | let request = tonic::Request::new(HelloRequest { 22 | name: "Tonic".into(), 23 | }); 24 | 25 | let response = client.say_hello(request).await?; 26 | 27 | dbg!(response); 28 | 29 | Ok(()) 30 | } 31 | -------------------------------------------------------------------------------- /examples/src/compression/server.rs: -------------------------------------------------------------------------------- 1 | use tonic::{transport::Server, Request, Response, Status}; 2 | 3 | use hello_world::greeter_server::{Greeter, GreeterServer}; 4 | use hello_world::{HelloReply, HelloRequest}; 5 | use tonic::codec::CompressionEncoding; 6 | 7 | pub mod hello_world { 8 | tonic::include_proto!("helloworld"); 9 | } 10 | 11 | #[derive(Default)] 12 | pub struct MyGreeter {} 13 | 14 | #[tonic::async_trait] 15 | impl Greeter for MyGreeter { 16 | async fn say_hello( 17 | &self, 18 | request: Request, 19 | ) -> Result, Status> { 20 | println!("Got a request from {:?}", request.remote_addr()); 21 | 22 | let reply = hello_world::HelloReply { 23 | message: format!("Hello {}!", request.into_inner().name), 24 | }; 25 | Ok(Response::new(reply)) 26 | } 27 | } 28 | 29 | #[tokio::main] 30 | async fn main() -> Result<(), Box> { 31 | let addr = "[::1]:50051".parse().unwrap(); 32 | let greeter = MyGreeter::default(); 33 | 34 | println!("GreeterServer listening on {addr}"); 35 | 36 | let service = GreeterServer::new(greeter) 37 | .send_compressed(CompressionEncoding::Gzip) 38 | .accept_compressed(CompressionEncoding::Gzip); 39 | 40 | Server::builder().add_service(service).serve(addr).await?; 41 | 42 | Ok(()) 43 | } 44 | -------------------------------------------------------------------------------- /examples/src/dynamic_load_balance/server.rs: -------------------------------------------------------------------------------- 1 | pub mod pb { 2 | tonic::include_proto!("grpc.examples.unaryecho"); 3 | } 4 | 5 | use std::net::SocketAddr; 6 | use tokio::sync::mpsc; 7 | use tonic::{transport::Server, Request, Response, Status}; 8 | 9 | use pb::{EchoRequest, EchoResponse}; 10 | 11 | type EchoResult = Result, Status>; 12 | 13 | #[derive(Debug)] 14 | pub struct EchoServer { 15 | addr: SocketAddr, 16 | } 17 | 18 | #[tonic::async_trait] 19 | impl pb::echo_server::Echo for EchoServer { 20 | async fn unary_echo(&self, request: Request) -> EchoResult { 21 | let message = format!("{} (from {})", request.into_inner().message, self.addr); 22 | 23 | Ok(Response::new(EchoResponse { message })) 24 | } 25 | } 26 | 27 | #[tokio::main] 28 | async fn main() -> Result<(), Box> { 29 | let addrs = ["[::1]:50051", "[::1]:50052"]; 30 | 31 | let (tx, mut rx) = mpsc::unbounded_channel(); 32 | 33 | for addr in &addrs { 34 | let addr = addr.parse()?; 35 | let tx = tx.clone(); 36 | 37 | let server = EchoServer { addr }; 38 | let serve = Server::builder() 39 | .add_service(pb::echo_server::EchoServer::new(server)) 40 | .serve(addr); 41 | 42 | tokio::spawn(async move { 43 | if let Err(e) = serve.await { 44 | eprintln!("Error = {e:?}"); 45 | } 46 | 47 | tx.send(()).unwrap(); 48 | }); 49 | } 50 | 51 | rx.recv().await; 52 | 53 | Ok(()) 54 | } 55 | -------------------------------------------------------------------------------- /examples/src/gcp/README.md: -------------------------------------------------------------------------------- 1 | # Google Cloud Pubsub example 2 | 3 | This example will attempt to fetch a list of topics using the google 4 | gRPC protobuf specification. This will use an OAuth token and TLS to 5 | fetch the list of topics. 6 | 7 | First, you must generate a access token via the [OAuth playground]. From here 8 | select the `Cloud Pub/Sub API v1` and its urls as the scope. This will start 9 | the OAuth flow. Then you must hit the `Exchange authorization code for tokens` 10 | button to generate an `access_token` which will show up in the HTTP response 11 | to the right under the `access_token` field in the response json. 12 | 13 | Once, you have this token you must fetch your GCP project id which can be found 14 | from the main page on the dashboard. When you have both of these items you can 15 | run the example like so: 16 | 17 | ```shell 18 | GCP_AUTH_TOKEN="" cargo run --bin gcp-client -- 19 | ``` 20 | 21 | [OAuth playground]: https://developers.google.com/oauthplayground/ 22 | -------------------------------------------------------------------------------- /examples/src/gcp/client.rs: -------------------------------------------------------------------------------- 1 | pub mod api { 2 | tonic::include_proto!("google.pubsub.v1"); 3 | } 4 | 5 | use api::{publisher_client::PublisherClient, ListTopicsRequest}; 6 | use tonic::{ 7 | metadata::MetadataValue, 8 | transport::{Certificate, Channel, ClientTlsConfig}, 9 | Request, 10 | }; 11 | 12 | const ENDPOINT: &str = "https://pubsub.googleapis.com"; 13 | 14 | #[tokio::main] 15 | async fn main() -> Result<(), Box> { 16 | let token = std::env::var("GCP_AUTH_TOKEN").map_err(|_| { 17 | "Pass a valid 0Auth bearer token via `GCP_AUTH_TOKEN` environment variable.".to_string() 18 | })?; 19 | 20 | let project = std::env::args() 21 | .nth(1) 22 | .ok_or_else(|| "Expected a project name as the first argument.".to_string())?; 23 | 24 | let bearer_token = format!("Bearer {token}"); 25 | let header_value: MetadataValue<_> = bearer_token.parse()?; 26 | 27 | let data_dir = std::path::PathBuf::from_iter([std::env!("CARGO_MANIFEST_DIR"), "data"]); 28 | let certs = std::fs::read_to_string(data_dir.join("gcp/roots.pem"))?; 29 | 30 | let tls_config = ClientTlsConfig::new() 31 | .ca_certificate(Certificate::from_pem(certs)) 32 | .domain_name("pubsub.googleapis.com"); 33 | 34 | let channel = Channel::from_static(ENDPOINT) 35 | .tls_config(tls_config)? 36 | .connect() 37 | .await?; 38 | 39 | let mut service = PublisherClient::with_interceptor(channel, move |mut req: Request<()>| { 40 | req.metadata_mut() 41 | .insert("authorization", header_value.clone()); 42 | Ok(req) 43 | }); 44 | 45 | let response = service 46 | .list_topics(Request::new(ListTopicsRequest { 47 | project: format!("projects/{project}"), 48 | page_size: 10, 49 | ..Default::default() 50 | })) 51 | .await?; 52 | 53 | println!("RESPONSE={response:?}"); 54 | 55 | Ok(()) 56 | } 57 | -------------------------------------------------------------------------------- /examples/src/grpc-web/client.rs: -------------------------------------------------------------------------------- 1 | use hello_world::{greeter_client::GreeterClient, HelloRequest}; 2 | use hyper_util::rt::TokioExecutor; 3 | use tonic_web::GrpcWebClientLayer; 4 | 5 | pub mod hello_world { 6 | tonic::include_proto!("helloworld"); 7 | } 8 | 9 | #[tokio::main] 10 | async fn main() -> Result<(), Box> { 11 | // Must use hyper directly... 12 | let client = hyper_util::client::legacy::Client::builder(TokioExecutor::new()).build_http(); 13 | 14 | let svc = tower::ServiceBuilder::new() 15 | .layer(GrpcWebClientLayer::new()) 16 | .service(client); 17 | 18 | let mut client = GreeterClient::with_origin(svc, "http://127.0.0.1:3000".try_into()?); 19 | 20 | let request = tonic::Request::new(HelloRequest { 21 | name: "Tonic".into(), 22 | }); 23 | 24 | let response = client.say_hello(request).await?; 25 | 26 | println!("RESPONSE={response:?}"); 27 | 28 | Ok(()) 29 | } 30 | -------------------------------------------------------------------------------- /examples/src/grpc-web/server.rs: -------------------------------------------------------------------------------- 1 | use tonic::{service::LayerExt as _, transport::Server, Request, Response, Status}; 2 | 3 | use hello_world::greeter_server::{Greeter, GreeterServer}; 4 | use hello_world::{HelloReply, HelloRequest}; 5 | 6 | pub mod hello_world { 7 | tonic::include_proto!("helloworld"); 8 | } 9 | 10 | #[derive(Default)] 11 | pub struct MyGreeter {} 12 | 13 | #[tonic::async_trait] 14 | impl Greeter for MyGreeter { 15 | async fn say_hello( 16 | &self, 17 | request: Request, 18 | ) -> Result, Status> { 19 | println!("Got a request from {:?}", request.remote_addr()); 20 | 21 | let reply = hello_world::HelloReply { 22 | message: format!("Hello {}!", request.into_inner().name), 23 | }; 24 | Ok(Response::new(reply)) 25 | } 26 | } 27 | 28 | #[tokio::main] 29 | async fn main() -> Result<(), Box> { 30 | tracing_subscriber::fmt::init(); 31 | 32 | let addr = "127.0.0.1:3000".parse().unwrap(); 33 | 34 | let greeter = MyGreeter::default(); 35 | let greeter = tower::ServiceBuilder::new() 36 | .layer(tower_http::cors::CorsLayer::new()) 37 | .layer(tonic_web::GrpcWebLayer::new()) 38 | .into_inner() 39 | .named_layer(GreeterServer::new(greeter)); 40 | 41 | println!("GreeterServer listening on {addr}"); 42 | 43 | Server::builder() 44 | // GrpcWeb is over http1 so we must enable it. 45 | .accept_http1(true) 46 | .add_service(greeter) 47 | .serve(addr) 48 | .await?; 49 | 50 | Ok(()) 51 | } 52 | -------------------------------------------------------------------------------- /examples/src/health/README.md: -------------------------------------------------------------------------------- 1 | # Health checks 2 | 3 | gRPC has a [health checking protocol](https://github.com/grpc/grpc/blob/master/doc/health-checking.md) that defines how health checks for services should be carried out. Tonic supports this protocol with the optional [tonic health crate](https://docs.rs/tonic-health). 4 | 5 | This example uses the crate to set up a HealthServer that will run alongside the application service. In order to test it, you may use community tools like [grpc_health_probe](https://github.com/grpc-ecosystem/grpc-health-probe). 6 | 7 | For example, running the following bash script: 8 | 9 | ```bash 10 | while [ true ]; do 11 | ./grpc_health_probe -addr=[::1]:50051 -service=helloworld.Greeter 12 | sleep 1 13 | done 14 | ``` 15 | 16 | will show the change in health status of the service over time. -------------------------------------------------------------------------------- /examples/src/health/server.rs: -------------------------------------------------------------------------------- 1 | use tonic::{transport::Server, Request, Response, Status}; 2 | 3 | use hello_world::greeter_server::{Greeter, GreeterServer}; 4 | use hello_world::{HelloReply, HelloRequest}; 5 | use std::time::Duration; 6 | use tonic_health::server::HealthReporter; 7 | 8 | pub mod hello_world { 9 | tonic::include_proto!("helloworld"); 10 | } 11 | 12 | #[derive(Default)] 13 | pub struct MyGreeter {} 14 | 15 | #[tonic::async_trait] 16 | impl Greeter for MyGreeter { 17 | async fn say_hello( 18 | &self, 19 | request: Request, 20 | ) -> Result, Status> { 21 | println!("Got a request from {:?}", request.remote_addr()); 22 | 23 | let reply = hello_world::HelloReply { 24 | message: format!("Hello {}!", request.into_inner().name), 25 | }; 26 | Ok(Response::new(reply)) 27 | } 28 | } 29 | 30 | /// This function (somewhat improbably) flips the status of a service every second, in order 31 | /// that the effect of `tonic_health::HealthReporter::watch` can be easily observed. 32 | async fn twiddle_service_status(reporter: HealthReporter) { 33 | let mut iter = 0u64; 34 | loop { 35 | iter += 1; 36 | tokio::time::sleep(Duration::from_secs(1)).await; 37 | 38 | if iter % 2 == 0 { 39 | reporter.set_serving::>().await; 40 | } else { 41 | reporter.set_not_serving::>().await; 42 | }; 43 | } 44 | } 45 | 46 | #[tokio::main] 47 | async fn main() -> Result<(), Box> { 48 | let (health_reporter, health_service) = tonic_health::server::health_reporter(); 49 | health_reporter 50 | .set_serving::>() 51 | .await; 52 | 53 | tokio::spawn(twiddle_service_status(health_reporter.clone())); 54 | 55 | let addr = "[::1]:50051".parse().unwrap(); 56 | let greeter = MyGreeter::default(); 57 | 58 | println!("HealthServer + GreeterServer listening on {addr}"); 59 | 60 | Server::builder() 61 | .add_service(health_service) 62 | .add_service(GreeterServer::new(greeter)) 63 | .serve(addr) 64 | .await?; 65 | 66 | Ok(()) 67 | } 68 | -------------------------------------------------------------------------------- /examples/src/helloworld/client.rs: -------------------------------------------------------------------------------- 1 | use hello_world::greeter_client::GreeterClient; 2 | use hello_world::HelloRequest; 3 | 4 | pub mod hello_world { 5 | tonic::include_proto!("helloworld"); 6 | } 7 | 8 | #[tokio::main] 9 | async fn main() -> Result<(), Box> { 10 | let mut client = GreeterClient::connect("http://[::1]:50051").await?; 11 | 12 | let request = tonic::Request::new(HelloRequest { 13 | name: "Tonic".into(), 14 | }); 15 | 16 | let response = client.say_hello(request).await?; 17 | 18 | println!("RESPONSE={response:?}"); 19 | 20 | Ok(()) 21 | } 22 | -------------------------------------------------------------------------------- /examples/src/helloworld/server.rs: -------------------------------------------------------------------------------- 1 | use tonic::{transport::Server, Request, Response, Status}; 2 | 3 | use hello_world::greeter_server::{Greeter, GreeterServer}; 4 | use hello_world::{HelloReply, HelloRequest}; 5 | 6 | pub mod hello_world { 7 | tonic::include_proto!("helloworld"); 8 | } 9 | 10 | #[derive(Default)] 11 | pub struct MyGreeter {} 12 | 13 | #[tonic::async_trait] 14 | impl Greeter for MyGreeter { 15 | async fn say_hello( 16 | &self, 17 | request: Request, 18 | ) -> Result, Status> { 19 | println!("Got a request from {:?}", request.remote_addr()); 20 | 21 | let reply = hello_world::HelloReply { 22 | message: format!("Hello {}!", request.into_inner().name), 23 | }; 24 | Ok(Response::new(reply)) 25 | } 26 | } 27 | 28 | #[tokio::main] 29 | async fn main() -> Result<(), Box> { 30 | let addr = "[::1]:50051".parse().unwrap(); 31 | let greeter = MyGreeter::default(); 32 | 33 | println!("GreeterServer listening on {addr}"); 34 | 35 | Server::builder() 36 | .add_service(GreeterServer::new(greeter)) 37 | .serve(addr) 38 | .await?; 39 | 40 | Ok(()) 41 | } 42 | -------------------------------------------------------------------------------- /examples/src/interceptor/server.rs: -------------------------------------------------------------------------------- 1 | use tonic::{transport::Server, Request, Response, Status}; 2 | 3 | use hello_world::greeter_server::{Greeter, GreeterServer}; 4 | use hello_world::{HelloReply, HelloRequest}; 5 | 6 | pub mod hello_world { 7 | tonic::include_proto!("helloworld"); 8 | } 9 | 10 | #[derive(Default)] 11 | pub struct MyGreeter {} 12 | 13 | #[tonic::async_trait] 14 | impl Greeter for MyGreeter { 15 | async fn say_hello( 16 | &self, 17 | request: Request, 18 | ) -> Result, Status> { 19 | let extension = request.extensions().get::().unwrap(); 20 | println!("extension data = {}", extension.some_piece_of_data); 21 | 22 | let reply = hello_world::HelloReply { 23 | message: format!("Hello {}!", request.into_inner().name), 24 | }; 25 | Ok(Response::new(reply)) 26 | } 27 | } 28 | 29 | #[tokio::main] 30 | async fn main() -> Result<(), Box> { 31 | let addr = "[::1]:50051".parse().unwrap(); 32 | let greeter = MyGreeter::default(); 33 | 34 | // See examples/src/interceptor/client.rs for an example of how to create a 35 | // named interceptor that can be returned from functions or stored in 36 | // structs. 37 | let svc = GreeterServer::with_interceptor(greeter, intercept); 38 | 39 | println!("GreeterServer listening on {addr}"); 40 | 41 | Server::builder().add_service(svc).serve(addr).await?; 42 | 43 | Ok(()) 44 | } 45 | 46 | /// This function will get called on each inbound request, if a `Status` 47 | /// is returned, it will cancel the request and return that status to the 48 | /// client. 49 | fn intercept(mut req: Request<()>) -> Result, Status> { 50 | println!("Intercepting request: {req:?}"); 51 | 52 | // Set an extension that can be retrieved by `say_hello` 53 | req.extensions_mut().insert(MyExtension { 54 | some_piece_of_data: "foo".to_string(), 55 | }); 56 | 57 | Ok(req) 58 | } 59 | 60 | #[derive(Clone)] 61 | struct MyExtension { 62 | some_piece_of_data: String, 63 | } 64 | -------------------------------------------------------------------------------- /examples/src/json-codec/client.rs: -------------------------------------------------------------------------------- 1 | //! A HelloWorld example that uses JSON instead of protobuf as the message serialization format. 2 | //! 3 | //! Generated code is the output of codegen as defined in the `build_json_codec_service` function 4 | //! in the `examples/build.rs` file. As defined there, the generated code assumes that a module 5 | //! `crate::common` exists which defines `HelloRequest`, `HelloResponse`, and `JsonCodec`. 6 | 7 | pub mod common; 8 | use common::HelloRequest; 9 | 10 | pub mod hello_world { 11 | include!(concat!(env!("OUT_DIR"), "/json.helloworld.Greeter.rs")); 12 | } 13 | use hello_world::greeter_client::GreeterClient; 14 | 15 | #[tokio::main] 16 | async fn main() -> Result<(), Box> { 17 | let mut client = GreeterClient::connect("http://[::1]:50051").await?; 18 | 19 | let request = tonic::Request::new(HelloRequest { 20 | name: "Tonic".into(), 21 | }); 22 | 23 | let response = client.say_hello(request).await?; 24 | 25 | println!("RESPONSE={response:?}"); 26 | 27 | Ok(()) 28 | } 29 | -------------------------------------------------------------------------------- /examples/src/json-codec/server.rs: -------------------------------------------------------------------------------- 1 | //! A HelloWorld example that uses JSON instead of protobuf as the message serialization format. 2 | //! 3 | //! Generated code is the output of codegen as defined in the `build_json_codec_service` function 4 | //! in the `examples/build.rs` file. As defined there, the generated code assumes that a module 5 | //! `crate::common` exists which defines `HelloRequest`, `HelloResponse`, and `JsonCodec`. 6 | 7 | use tonic::{transport::Server, Request, Response, Status}; 8 | 9 | pub mod common; 10 | use common::{HelloRequest, HelloResponse}; 11 | 12 | pub mod hello_world { 13 | include!(concat!(env!("OUT_DIR"), "/json.helloworld.Greeter.rs")); 14 | } 15 | use hello_world::greeter_server::{Greeter, GreeterServer}; 16 | 17 | #[derive(Default)] 18 | pub struct MyGreeter {} 19 | 20 | #[tonic::async_trait] 21 | impl Greeter for MyGreeter { 22 | async fn say_hello( 23 | &self, 24 | request: Request, 25 | ) -> Result, Status> { 26 | println!("Got a request from {:?}", request.remote_addr()); 27 | 28 | let reply = HelloResponse { 29 | message: format!("Hello {}!", request.into_inner().name), 30 | }; 31 | Ok(Response::new(reply)) 32 | } 33 | } 34 | 35 | #[tokio::main] 36 | async fn main() -> Result<(), Box> { 37 | let addr = "[::1]:50051".parse().unwrap(); 38 | let greeter = MyGreeter::default(); 39 | 40 | println!("GreeterServer listening on {addr}"); 41 | 42 | Server::builder() 43 | .add_service(GreeterServer::new(greeter)) 44 | .serve(addr) 45 | .await?; 46 | 47 | Ok(()) 48 | } 49 | -------------------------------------------------------------------------------- /examples/src/load_balance/client.rs: -------------------------------------------------------------------------------- 1 | pub mod pb { 2 | tonic::include_proto!("grpc.examples.unaryecho"); 3 | } 4 | 5 | use pb::{echo_client::EchoClient, EchoRequest}; 6 | use tonic::transport::Channel; 7 | 8 | #[tokio::main] 9 | async fn main() -> Result<(), Box> { 10 | let endpoints = ["http://[::1]:50051", "http://[::1]:50052"] 11 | .iter() 12 | .map(|a| Channel::from_static(a)); 13 | 14 | let channel = Channel::balance_list(endpoints); 15 | 16 | let mut client = EchoClient::new(channel); 17 | 18 | for _ in 0..12usize { 19 | let request = tonic::Request::new(EchoRequest { 20 | message: "hello".into(), 21 | }); 22 | 23 | let response = client.unary_echo(request).await?; 24 | 25 | println!("RESPONSE={response:?}"); 26 | } 27 | 28 | Ok(()) 29 | } 30 | -------------------------------------------------------------------------------- /examples/src/load_balance/server.rs: -------------------------------------------------------------------------------- 1 | pub mod pb { 2 | tonic::include_proto!("grpc.examples.unaryecho"); 3 | } 4 | 5 | use std::net::SocketAddr; 6 | use tokio::sync::mpsc; 7 | use tonic::{transport::Server, Request, Response, Status}; 8 | 9 | use pb::{EchoRequest, EchoResponse}; 10 | 11 | type EchoResult = Result, Status>; 12 | 13 | #[derive(Debug)] 14 | pub struct EchoServer { 15 | addr: SocketAddr, 16 | } 17 | 18 | #[tonic::async_trait] 19 | impl pb::echo_server::Echo for EchoServer { 20 | async fn unary_echo(&self, request: Request) -> EchoResult { 21 | let message = format!("{} (from {})", request.into_inner().message, self.addr); 22 | 23 | Ok(Response::new(EchoResponse { message })) 24 | } 25 | } 26 | 27 | #[tokio::main] 28 | async fn main() -> Result<(), Box> { 29 | let addrs = ["[::1]:50051", "[::1]:50052"]; 30 | 31 | let (tx, mut rx) = mpsc::unbounded_channel(); 32 | 33 | for addr in &addrs { 34 | let addr = addr.parse()?; 35 | let tx = tx.clone(); 36 | 37 | let server = EchoServer { addr }; 38 | let serve = Server::builder() 39 | .add_service(pb::echo_server::EchoServer::new(server)) 40 | .serve(addr); 41 | 42 | tokio::spawn(async move { 43 | if let Err(e) = serve.await { 44 | eprintln!("Error = {e:?}"); 45 | } 46 | 47 | tx.send(()).unwrap(); 48 | }); 49 | } 50 | 51 | rx.recv().await; 52 | 53 | Ok(()) 54 | } 55 | -------------------------------------------------------------------------------- /examples/src/mock/mock.rs: -------------------------------------------------------------------------------- 1 | use hyper_util::rt::TokioIo; 2 | use tonic::{ 3 | transport::{Endpoint, Server, Uri}, 4 | Request, Response, Status, 5 | }; 6 | use tower::service_fn; 7 | 8 | pub mod hello_world { 9 | tonic::include_proto!("helloworld"); 10 | } 11 | 12 | use hello_world::{ 13 | greeter_client::GreeterClient, 14 | greeter_server::{Greeter, GreeterServer}, 15 | HelloReply, HelloRequest, 16 | }; 17 | 18 | #[tokio::main] 19 | async fn main() -> Result<(), Box> { 20 | let (client, server) = tokio::io::duplex(1024); 21 | 22 | let greeter = MyGreeter::default(); 23 | 24 | tokio::spawn(async move { 25 | Server::builder() 26 | .add_service(GreeterServer::new(greeter)) 27 | .serve_with_incoming(tokio_stream::once(Ok::<_, std::io::Error>(server))) 28 | .await 29 | }); 30 | 31 | // Move client to an option so we can _move_ the inner value 32 | // on the first attempt to connect. All other attempts will fail. 33 | let mut client = Some(client); 34 | let channel = Endpoint::try_from("http://[::]:50051")? 35 | .connect_with_connector(service_fn(move |_: Uri| { 36 | let client = client.take(); 37 | 38 | async move { 39 | if let Some(client) = client { 40 | Ok(TokioIo::new(client)) 41 | } else { 42 | Err(std::io::Error::other("Client already taken")) 43 | } 44 | } 45 | })) 46 | .await?; 47 | 48 | let mut client = GreeterClient::new(channel); 49 | 50 | let request = tonic::Request::new(HelloRequest { 51 | name: "Tonic".into(), 52 | }); 53 | 54 | let response = client.say_hello(request).await?; 55 | 56 | println!("RESPONSE={response:?}"); 57 | 58 | Ok(()) 59 | } 60 | 61 | #[derive(Default)] 62 | pub struct MyGreeter {} 63 | 64 | #[tonic::async_trait] 65 | impl Greeter for MyGreeter { 66 | async fn say_hello( 67 | &self, 68 | request: Request, 69 | ) -> Result, Status> { 70 | println!("Got a request: {:?}", request); 71 | 72 | let reply = hello_world::HelloReply { 73 | message: format!("Hello {}!", request.into_inner().name), 74 | }; 75 | Ok(Response::new(reply)) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /examples/src/multiplex/client.rs: -------------------------------------------------------------------------------- 1 | pub mod hello_world { 2 | tonic::include_proto!("helloworld"); 3 | } 4 | 5 | pub mod echo { 6 | tonic::include_proto!("grpc.examples.unaryecho"); 7 | } 8 | 9 | use echo::{echo_client::EchoClient, EchoRequest}; 10 | use hello_world::{greeter_client::GreeterClient, HelloRequest}; 11 | use tonic::transport::Endpoint; 12 | 13 | #[tokio::main] 14 | async fn main() -> Result<(), Box> { 15 | let channel = Endpoint::from_static("http://[::1]:50051") 16 | .connect() 17 | .await?; 18 | 19 | let mut greeter_client = GreeterClient::new(channel.clone()); 20 | let mut echo_client = EchoClient::new(channel); 21 | 22 | let request = tonic::Request::new(HelloRequest { 23 | name: "Tonic".into(), 24 | }); 25 | 26 | let response = greeter_client.say_hello(request).await?; 27 | 28 | println!("GREETER RESPONSE={response:?}"); 29 | 30 | let request = tonic::Request::new(EchoRequest { 31 | message: "hello".into(), 32 | }); 33 | 34 | let response = echo_client.unary_echo(request).await?; 35 | 36 | println!("ECHO RESPONSE={response:?}"); 37 | 38 | Ok(()) 39 | } 40 | -------------------------------------------------------------------------------- /examples/src/multiplex/server.rs: -------------------------------------------------------------------------------- 1 | use tonic::{transport::Server, Request, Response, Status}; 2 | 3 | pub mod hello_world { 4 | tonic::include_proto!("helloworld"); 5 | } 6 | 7 | pub mod echo { 8 | tonic::include_proto!("grpc.examples.unaryecho"); 9 | } 10 | 11 | use hello_world::{ 12 | greeter_server::{Greeter, GreeterServer}, 13 | HelloReply, HelloRequest, 14 | }; 15 | 16 | use echo::{ 17 | echo_server::{Echo, EchoServer}, 18 | EchoRequest, EchoResponse, 19 | }; 20 | 21 | #[tokio::main] 22 | async fn main() -> Result<(), Box> { 23 | let addr = "[::1]:50051".parse().unwrap(); 24 | 25 | let greeter = GreeterServer::new(MyGreeter::default()); 26 | let echo = EchoServer::new(MyEcho::default()); 27 | 28 | Server::builder() 29 | .add_service(greeter) 30 | .add_service(echo) 31 | .serve(addr) 32 | .await?; 33 | 34 | Ok(()) 35 | } 36 | 37 | #[derive(Default)] 38 | pub struct MyGreeter {} 39 | 40 | #[tonic::async_trait] 41 | impl Greeter for MyGreeter { 42 | async fn say_hello( 43 | &self, 44 | request: Request, 45 | ) -> Result, Status> { 46 | let reply = hello_world::HelloReply { 47 | message: format!("Hello {}!", request.into_inner().name), 48 | }; 49 | Ok(Response::new(reply)) 50 | } 51 | } 52 | 53 | #[derive(Default)] 54 | pub struct MyEcho {} 55 | 56 | #[tonic::async_trait] 57 | impl Echo for MyEcho { 58 | async fn unary_echo( 59 | &self, 60 | request: Request, 61 | ) -> Result, Status> { 62 | let message = request.into_inner().message; 63 | Ok(Response::new(EchoResponse { message })) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /examples/src/reflection/server.rs: -------------------------------------------------------------------------------- 1 | use tonic::transport::Server; 2 | use tonic::{Request, Response, Status}; 3 | 4 | mod proto { 5 | tonic::include_proto!("helloworld"); 6 | 7 | pub(crate) const FILE_DESCRIPTOR_SET: &[u8] = 8 | tonic::include_file_descriptor_set!("helloworld_descriptor"); 9 | } 10 | 11 | #[derive(Default)] 12 | pub struct MyGreeter {} 13 | 14 | #[tonic::async_trait] 15 | impl proto::greeter_server::Greeter for MyGreeter { 16 | async fn say_hello( 17 | &self, 18 | request: Request, 19 | ) -> Result, Status> { 20 | println!("Got a request from {:?}", request.remote_addr()); 21 | 22 | let reply = proto::HelloReply { 23 | message: format!("Hello {}!", request.into_inner().name), 24 | }; 25 | Ok(Response::new(reply)) 26 | } 27 | } 28 | 29 | #[tokio::main] 30 | async fn main() -> Result<(), Box> { 31 | let service = tonic_reflection::server::Builder::configure() 32 | .register_encoded_file_descriptor_set(proto::FILE_DESCRIPTOR_SET) 33 | .build_v1() 34 | .unwrap(); 35 | 36 | let addr = "[::1]:50052".parse().unwrap(); 37 | let greeter = MyGreeter::default(); 38 | 39 | Server::builder() 40 | .add_service(service) 41 | .add_service(proto::greeter_server::GreeterServer::new(greeter)) 42 | .serve(addr) 43 | .await?; 44 | 45 | Ok(()) 46 | } 47 | -------------------------------------------------------------------------------- /examples/src/richer-error/client.rs: -------------------------------------------------------------------------------- 1 | use tonic_types::StatusExt; 2 | 3 | use hello_world::greeter_client::GreeterClient; 4 | use hello_world::HelloRequest; 5 | 6 | pub mod hello_world { 7 | tonic::include_proto!("helloworld"); 8 | } 9 | 10 | #[tokio::main] 11 | async fn main() -> Result<(), Box> { 12 | let mut client = GreeterClient::connect("http://[::1]:50051").await?; 13 | 14 | let request = tonic::Request::new(HelloRequest { 15 | // Valid request 16 | // name: "Tonic".into(), 17 | // Name cannot be empty 18 | name: "".into(), 19 | // Name is too long 20 | // name: "some excessively long name".into(), 21 | }); 22 | 23 | let response = match client.say_hello(request).await { 24 | Ok(response) => response, 25 | Err(status) => { 26 | println!(" Error status received. Extracting error details...\n"); 27 | 28 | let err_details = status.get_error_details(); 29 | 30 | if let Some(bad_request) = err_details.bad_request() { 31 | // Handle bad_request details 32 | println!(" {bad_request:?}"); 33 | } 34 | if let Some(help) = err_details.help() { 35 | // Handle help details 36 | println!(" {help:?}"); 37 | } 38 | if let Some(localized_message) = err_details.localized_message() { 39 | // Handle localized_message details 40 | println!(" {localized_message:?}"); 41 | } 42 | 43 | println!(); 44 | 45 | return Ok(()); 46 | } 47 | }; 48 | 49 | println!(" Successful response received.\n\n {response:?}\n"); 50 | 51 | Ok(()) 52 | } 53 | -------------------------------------------------------------------------------- /examples/src/richer-error/client_vec.rs: -------------------------------------------------------------------------------- 1 | use tonic_types::{ErrorDetail, StatusExt}; 2 | 3 | use hello_world::greeter_client::GreeterClient; 4 | use hello_world::HelloRequest; 5 | 6 | pub mod hello_world { 7 | tonic::include_proto!("helloworld"); 8 | } 9 | 10 | #[tokio::main] 11 | async fn main() -> Result<(), Box> { 12 | let mut client = GreeterClient::connect("http://[::1]:50051").await?; 13 | 14 | let request = tonic::Request::new(HelloRequest { 15 | // Valid request 16 | // name: "Tonic".into(), 17 | // Name cannot be empty 18 | name: "".into(), 19 | // Name is too long 20 | // name: "some excessively long name".into(), 21 | }); 22 | 23 | let response = match client.say_hello(request).await { 24 | Ok(response) => response, 25 | Err(status) => { 26 | println!(" Error status received. Extracting error details...\n"); 27 | 28 | let err_details = status.get_error_details_vec(); 29 | 30 | for (i, err_detail) in err_details.iter().enumerate() { 31 | println!("err_detail[{i}]"); 32 | match err_detail { 33 | ErrorDetail::BadRequest(bad_request) => { 34 | // Handle bad_request details 35 | println!(" {bad_request:?}"); 36 | } 37 | ErrorDetail::Help(help) => { 38 | // Handle help details 39 | println!(" {help:?}"); 40 | } 41 | ErrorDetail::LocalizedMessage(localized_message) => { 42 | // Handle localized_message details 43 | println!(" {localized_message:?}"); 44 | } 45 | _ => {} 46 | } 47 | } 48 | 49 | println!(); 50 | 51 | return Ok(()); 52 | } 53 | }; 54 | 55 | println!(" Successful response received.\n\n {response:?}\n"); 56 | 57 | Ok(()) 58 | } 59 | -------------------------------------------------------------------------------- /examples/src/richer-error/server.rs: -------------------------------------------------------------------------------- 1 | use tonic::{transport::Server, Code, Request, Response, Status}; 2 | use tonic_types::{ErrorDetails, StatusExt}; 3 | 4 | use hello_world::greeter_server::{Greeter, GreeterServer}; 5 | use hello_world::{HelloReply, HelloRequest}; 6 | 7 | pub mod hello_world { 8 | tonic::include_proto!("helloworld"); 9 | } 10 | 11 | #[derive(Default)] 12 | pub struct MyGreeter {} 13 | 14 | #[tonic::async_trait] 15 | impl Greeter for MyGreeter { 16 | async fn say_hello( 17 | &self, 18 | request: Request, 19 | ) -> Result, Status> { 20 | println!("Got a request from {:?}", request.remote_addr()); 21 | 22 | // Extract request data 23 | let name = request.into_inner().name; 24 | 25 | // Create empty ErrorDetails struct 26 | let mut err_details = ErrorDetails::new(); 27 | 28 | // Add error details conditionally 29 | if name.is_empty() { 30 | err_details.add_bad_request_violation("name", "name cannot be empty"); 31 | } else if name.len() > 20 { 32 | err_details.add_bad_request_violation("name", "name is too long"); 33 | } 34 | 35 | if err_details.has_bad_request_violations() { 36 | // Add additional error details if necessary 37 | err_details 38 | .add_help_link("description of link", "https://resource.example.local") 39 | .set_localized_message("en-US", "message for the user"); 40 | 41 | // Generate error status 42 | let status = Status::with_error_details( 43 | Code::InvalidArgument, 44 | "request contains invalid arguments", 45 | err_details, 46 | ); 47 | 48 | return Err(status); 49 | } 50 | 51 | let reply = hello_world::HelloReply { 52 | message: format!("Hello {name}!"), 53 | }; 54 | Ok(Response::new(reply)) 55 | } 56 | } 57 | 58 | #[tokio::main] 59 | async fn main() -> Result<(), Box> { 60 | let addr = "[::1]:50051".parse().unwrap(); 61 | let greeter = MyGreeter::default(); 62 | 63 | println!("GreeterServer listening on {addr}"); 64 | 65 | Server::builder() 66 | .add_service(GreeterServer::new(greeter)) 67 | .serve(addr) 68 | .await?; 69 | 70 | Ok(()) 71 | } 72 | -------------------------------------------------------------------------------- /examples/src/richer-error/server_vec.rs: -------------------------------------------------------------------------------- 1 | use tonic::{transport::Server, Code, Request, Response, Status}; 2 | use tonic_types::{BadRequest, Help, LocalizedMessage, StatusExt}; 3 | 4 | use hello_world::greeter_server::{Greeter, GreeterServer}; 5 | use hello_world::{HelloReply, HelloRequest}; 6 | 7 | pub mod hello_world { 8 | tonic::include_proto!("helloworld"); 9 | } 10 | 11 | #[derive(Default)] 12 | pub struct MyGreeter {} 13 | 14 | #[tonic::async_trait] 15 | impl Greeter for MyGreeter { 16 | async fn say_hello( 17 | &self, 18 | request: Request, 19 | ) -> Result, Status> { 20 | println!("Got a request from {:?}", request.remote_addr()); 21 | 22 | // Extract request data 23 | let name = request.into_inner().name; 24 | 25 | // Create empty BadRequest struct 26 | let mut bad_request = BadRequest::new(vec![]); 27 | 28 | // Add violations conditionally 29 | if name.is_empty() { 30 | bad_request.add_violation("name", "name cannot be empty"); 31 | } else if name.len() > 20 { 32 | bad_request.add_violation("name", "name is too long"); 33 | } 34 | 35 | if !bad_request.is_empty() { 36 | // Add additional error details if necessary 37 | let help = Help::with_link("description of link", "https://resource.example.local"); 38 | 39 | let localized_message = LocalizedMessage::new("en-US", "message for the user"); 40 | 41 | // Generate error status 42 | let status = Status::with_error_details_vec( 43 | Code::InvalidArgument, 44 | "request contains invalid arguments", 45 | vec![bad_request.into(), help.into(), localized_message.into()], 46 | ); 47 | 48 | return Err(status); 49 | } 50 | 51 | let reply = hello_world::HelloReply { 52 | message: format!("Hello {name}!"), 53 | }; 54 | Ok(Response::new(reply)) 55 | } 56 | } 57 | 58 | #[tokio::main] 59 | async fn main() -> Result<(), Box> { 60 | let addr = "[::1]:50051".parse().unwrap(); 61 | let greeter = MyGreeter::default(); 62 | 63 | println!("GreeterServer listening on {addr}"); 64 | 65 | Server::builder() 66 | .add_service(GreeterServer::new(greeter)) 67 | .serve(addr) 68 | .await?; 69 | 70 | Ok(()) 71 | } 72 | -------------------------------------------------------------------------------- /examples/src/routeguide/data.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | use std::fs::File; 3 | 4 | #[derive(Debug, Deserialize)] 5 | struct Feature { 6 | location: Location, 7 | name: String, 8 | } 9 | 10 | #[derive(Debug, Deserialize)] 11 | struct Location { 12 | latitude: i32, 13 | longitude: i32, 14 | } 15 | 16 | #[allow(dead_code)] 17 | pub fn load() -> Vec { 18 | let data_dir = std::path::PathBuf::from_iter([std::env!("CARGO_MANIFEST_DIR"), "data"]); 19 | let file = File::open(data_dir.join("route_guide_db.json")).expect("failed to open data file"); 20 | 21 | let decoded: Vec = 22 | serde_json::from_reader(&file).expect("failed to deserialize features"); 23 | 24 | decoded 25 | .into_iter() 26 | .map(|feature| crate::routeguide::Feature { 27 | name: feature.name, 28 | location: Some(crate::routeguide::Point { 29 | longitude: feature.location.longitude, 30 | latitude: feature.location.latitude, 31 | }), 32 | }) 33 | .collect() 34 | } 35 | -------------------------------------------------------------------------------- /examples/src/tls/client.rs: -------------------------------------------------------------------------------- 1 | pub mod pb { 2 | tonic::include_proto!("/grpc.examples.unaryecho"); 3 | } 4 | 5 | use pb::{echo_client::EchoClient, EchoRequest}; 6 | use tonic::transport::{Certificate, Channel, ClientTlsConfig}; 7 | 8 | #[tokio::main] 9 | async fn main() -> Result<(), Box> { 10 | let data_dir = std::path::PathBuf::from_iter([std::env!("CARGO_MANIFEST_DIR"), "data"]); 11 | let pem = std::fs::read_to_string(data_dir.join("tls/ca.pem"))?; 12 | let ca = Certificate::from_pem(pem); 13 | 14 | let tls = ClientTlsConfig::new() 15 | .ca_certificate(ca) 16 | .domain_name("example.com"); 17 | 18 | let channel = Channel::from_static("https://[::1]:50051") 19 | .tls_config(tls)? 20 | .connect() 21 | .await?; 22 | 23 | let mut client = EchoClient::new(channel); 24 | let request = tonic::Request::new(EchoRequest { 25 | message: "hello".into(), 26 | }); 27 | 28 | let response = client.unary_echo(request).await?; 29 | 30 | println!("RESPONSE={response:?}"); 31 | 32 | Ok(()) 33 | } 34 | -------------------------------------------------------------------------------- /examples/src/tls/server.rs: -------------------------------------------------------------------------------- 1 | pub mod pb { 2 | tonic::include_proto!("/grpc.examples.unaryecho"); 3 | } 4 | 5 | use pb::{EchoRequest, EchoResponse}; 6 | use tonic::{ 7 | transport::{ 8 | server::{TcpConnectInfo, TlsConnectInfo}, 9 | Identity, Server, ServerTlsConfig, 10 | }, 11 | Request, Response, Status, 12 | }; 13 | 14 | type EchoResult = Result, Status>; 15 | 16 | #[derive(Default)] 17 | pub struct EchoServer {} 18 | 19 | #[tonic::async_trait] 20 | impl pb::echo_server::Echo for EchoServer { 21 | async fn unary_echo(&self, request: Request) -> EchoResult { 22 | let conn_info = request 23 | .extensions() 24 | .get::>() 25 | .unwrap(); 26 | println!( 27 | "Got a request from {:?} with info {:?}", 28 | request.remote_addr(), 29 | conn_info 30 | ); 31 | 32 | let message = request.into_inner().message; 33 | Ok(Response::new(EchoResponse { message })) 34 | } 35 | } 36 | 37 | #[tokio::main] 38 | async fn main() -> Result<(), Box> { 39 | let data_dir = std::path::PathBuf::from_iter([std::env!("CARGO_MANIFEST_DIR"), "data"]); 40 | let cert = std::fs::read_to_string(data_dir.join("tls/server.pem"))?; 41 | let key = std::fs::read_to_string(data_dir.join("tls/server.key"))?; 42 | 43 | let identity = Identity::from_pem(cert, key); 44 | 45 | let addr = "[::1]:50051".parse().unwrap(); 46 | let server = EchoServer::default(); 47 | 48 | Server::builder() 49 | .tls_config(ServerTlsConfig::new().identity(identity))? 50 | .add_service(pb::echo_server::EchoServer::new(server)) 51 | .serve(addr) 52 | .await?; 53 | 54 | Ok(()) 55 | } 56 | -------------------------------------------------------------------------------- /examples/src/tls_client_auth/client.rs: -------------------------------------------------------------------------------- 1 | pub mod pb { 2 | tonic::include_proto!("grpc.examples.unaryecho"); 3 | } 4 | 5 | use pb::{echo_client::EchoClient, EchoRequest}; 6 | use tonic::transport::{Certificate, Channel, ClientTlsConfig, Identity}; 7 | 8 | #[tokio::main] 9 | async fn main() -> Result<(), Box> { 10 | let data_dir = std::path::PathBuf::from_iter([std::env!("CARGO_MANIFEST_DIR"), "data"]); 11 | let server_root_ca_cert = std::fs::read_to_string(data_dir.join("tls/ca.pem"))?; 12 | let server_root_ca_cert = Certificate::from_pem(server_root_ca_cert); 13 | let client_cert = std::fs::read_to_string(data_dir.join("tls/client1.pem"))?; 14 | let client_key = std::fs::read_to_string(data_dir.join("tls/client1.key"))?; 15 | let client_identity = Identity::from_pem(client_cert, client_key); 16 | 17 | let tls = ClientTlsConfig::new() 18 | .domain_name("localhost") 19 | .ca_certificate(server_root_ca_cert) 20 | .identity(client_identity); 21 | 22 | let channel = Channel::from_static("https://[::1]:50051") 23 | .tls_config(tls)? 24 | .connect() 25 | .await?; 26 | 27 | let mut client = EchoClient::new(channel); 28 | 29 | let request = tonic::Request::new(EchoRequest { 30 | message: "hello".into(), 31 | }); 32 | 33 | let response = client.unary_echo(request).await?; 34 | 35 | println!("RESPONSE={response:?}"); 36 | 37 | Ok(()) 38 | } 39 | -------------------------------------------------------------------------------- /examples/src/tls_client_auth/server.rs: -------------------------------------------------------------------------------- 1 | pub mod pb { 2 | tonic::include_proto!("grpc.examples.unaryecho"); 3 | } 4 | 5 | use pb::{EchoRequest, EchoResponse}; 6 | use tonic::transport::{Certificate, Identity, Server, ServerTlsConfig}; 7 | use tonic::{Request, Response, Status}; 8 | 9 | type EchoResult = Result, Status>; 10 | 11 | #[derive(Default)] 12 | pub struct EchoServer {} 13 | 14 | #[tonic::async_trait] 15 | impl pb::echo_server::Echo for EchoServer { 16 | async fn unary_echo(&self, request: Request) -> EchoResult { 17 | let certs = request 18 | .peer_certs() 19 | .expect("Client did not send its certs!"); 20 | 21 | println!("Got {} peer certs!", certs.len()); 22 | 23 | let message = request.into_inner().message; 24 | Ok(Response::new(EchoResponse { message })) 25 | } 26 | } 27 | 28 | #[tokio::main] 29 | async fn main() -> Result<(), Box> { 30 | let data_dir = std::path::PathBuf::from_iter([std::env!("CARGO_MANIFEST_DIR"), "data"]); 31 | let cert = std::fs::read_to_string(data_dir.join("tls/server.pem"))?; 32 | let key = std::fs::read_to_string(data_dir.join("tls/server.key"))?; 33 | let server_identity = Identity::from_pem(cert, key); 34 | 35 | let client_ca_cert = std::fs::read_to_string(data_dir.join("tls/client_ca.pem"))?; 36 | let client_ca_cert = Certificate::from_pem(client_ca_cert); 37 | 38 | let addr = "[::1]:50051".parse().unwrap(); 39 | let server = EchoServer::default(); 40 | 41 | let tls = ServerTlsConfig::new() 42 | .identity(server_identity) 43 | .client_ca_root(client_ca_cert); 44 | 45 | Server::builder() 46 | .tls_config(tls)? 47 | .add_service(pb::echo_server::EchoServer::new(server)) 48 | .serve(addr) 49 | .await?; 50 | 51 | Ok(()) 52 | } 53 | -------------------------------------------------------------------------------- /examples/src/tracing/client.rs: -------------------------------------------------------------------------------- 1 | pub mod hello_world { 2 | tonic::include_proto!("helloworld"); 3 | } 4 | 5 | use hello_world::{greeter_client::GreeterClient, HelloRequest}; 6 | 7 | #[tokio::main] 8 | async fn main() -> Result<(), Box> { 9 | tracing_subscriber::FmtSubscriber::builder() 10 | .with_max_level(tracing::Level::DEBUG) 11 | .init(); 12 | 13 | say_hi("Bob".into()).await?; 14 | 15 | Ok(()) 16 | } 17 | 18 | #[tracing::instrument] 19 | async fn say_hi(name: String) -> Result<(), Box> { 20 | let mut client = GreeterClient::connect("http://[::1]:50051").await?; 21 | 22 | let request = tonic::Request::new(HelloRequest { name }); 23 | 24 | tracing::info!( 25 | message = "Sending request.", 26 | request = %request.get_ref().name 27 | ); 28 | 29 | let response = client.say_hello(request).await?; 30 | 31 | tracing::info!( 32 | message = "Got a response.", 33 | response = %response.get_ref().message 34 | ); 35 | 36 | Ok(()) 37 | } 38 | -------------------------------------------------------------------------------- /examples/src/tracing/server.rs: -------------------------------------------------------------------------------- 1 | use tonic::{transport::Server, Request, Response, Status}; 2 | 3 | pub mod hello_world { 4 | tonic::include_proto!("helloworld"); 5 | } 6 | 7 | use hello_world::{ 8 | greeter_server::{Greeter, GreeterServer}, 9 | HelloReply, HelloRequest, 10 | }; 11 | 12 | #[derive(Debug, Default)] 13 | pub struct MyGreeter {} 14 | 15 | #[tonic::async_trait] 16 | impl Greeter for MyGreeter { 17 | #[tracing::instrument] 18 | async fn say_hello( 19 | &self, 20 | request: Request, 21 | ) -> Result, Status> { 22 | tracing::info!("received request"); 23 | 24 | let reply = hello_world::HelloReply { 25 | message: format!("Hello {}!", request.into_inner().name), 26 | }; 27 | 28 | tracing::debug!("sending response"); 29 | 30 | Ok(Response::new(reply)) 31 | } 32 | } 33 | 34 | #[tokio::main] 35 | async fn main() -> Result<(), Box> { 36 | tracing_subscriber::fmt() 37 | .with_max_level(tracing::Level::DEBUG) 38 | .init(); 39 | 40 | let addr = "[::1]:50051".parse().unwrap(); 41 | let greeter = MyGreeter::default(); 42 | 43 | tracing::info!(message = "Starting server.", %addr); 44 | 45 | Server::builder() 46 | .trace_fn(|_| tracing::info_span!("helloworld_server")) 47 | .add_service(GreeterServer::new(greeter)) 48 | .serve(addr) 49 | .await?; 50 | 51 | Ok(()) 52 | } 53 | -------------------------------------------------------------------------------- /examples/src/uds/client_standard.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(unix), allow(unused_imports))] 2 | 3 | pub mod hello_world { 4 | tonic::include_proto!("helloworld"); 5 | } 6 | 7 | use hello_world::{greeter_client::GreeterClient, HelloRequest}; 8 | 9 | #[cfg(unix)] 10 | #[tokio::main] 11 | async fn main() -> Result<(), Box> { 12 | // Unix socket URI follows [RFC-3986](https://datatracker.ietf.org/doc/html/rfc3986) 13 | // which is aligned with [the gRPC naming convention](https://github.com/grpc/grpc/blob/master/doc/naming.md). 14 | // - unix:relative_path 15 | // - unix:///absolute_path 16 | let path = "unix:///tmp/tonic/helloworld"; 17 | 18 | let mut client = GreeterClient::connect(path).await?; 19 | 20 | let request = tonic::Request::new(HelloRequest { 21 | name: "Tonic".into(), 22 | }); 23 | 24 | let response = client.say_hello(request).await?; 25 | 26 | println!("RESPONSE={response:?}"); 27 | 28 | Ok(()) 29 | } 30 | 31 | #[cfg(not(unix))] 32 | fn main() { 33 | panic!("The `uds` example only works on unix systems!"); 34 | } 35 | -------------------------------------------------------------------------------- /examples/src/uds/client_with_connector.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(unix), allow(unused_imports))] 2 | 3 | pub mod hello_world { 4 | tonic::include_proto!("helloworld"); 5 | } 6 | 7 | use hello_world::{greeter_client::GreeterClient, HelloRequest}; 8 | use hyper_util::rt::TokioIo; 9 | #[cfg(unix)] 10 | use tokio::net::UnixStream; 11 | use tonic::transport::{Endpoint, Uri}; 12 | use tower::service_fn; 13 | 14 | #[cfg(unix)] 15 | #[tokio::main] 16 | async fn main() -> Result<(), Box> { 17 | // We will ignore this uri because uds do not use it 18 | // if your connector does use the uri it will be provided 19 | // as the request to the `MakeConnection`. 20 | 21 | let channel = Endpoint::try_from("http://[::]:50051")? 22 | .connect_with_connector(service_fn(|_: Uri| async { 23 | let path = "/tmp/tonic/helloworld"; 24 | 25 | // Connect to a Uds socket 26 | Ok::<_, std::io::Error>(TokioIo::new(UnixStream::connect(path).await?)) 27 | })) 28 | .await?; 29 | 30 | let mut client = GreeterClient::new(channel); 31 | 32 | let request = tonic::Request::new(HelloRequest { 33 | name: "Tonic".into(), 34 | }); 35 | 36 | let response = client.say_hello(request).await?; 37 | 38 | println!("RESPONSE={response:?}"); 39 | 40 | Ok(()) 41 | } 42 | 43 | #[cfg(not(unix))] 44 | fn main() { 45 | panic!("The `uds` example only works on unix systems!"); 46 | } 47 | -------------------------------------------------------------------------------- /examples/src/uds/server.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(unix), allow(unused_imports))] 2 | 3 | use std::path::Path; 4 | #[cfg(unix)] 5 | use tokio::net::UnixListener; 6 | #[cfg(unix)] 7 | use tokio_stream::wrappers::UnixListenerStream; 8 | #[cfg(unix)] 9 | use tonic::transport::server::UdsConnectInfo; 10 | use tonic::{transport::Server, Request, Response, Status}; 11 | 12 | pub mod hello_world { 13 | tonic::include_proto!("helloworld"); 14 | } 15 | 16 | use hello_world::{ 17 | greeter_server::{Greeter, GreeterServer}, 18 | HelloReply, HelloRequest, 19 | }; 20 | 21 | #[derive(Default)] 22 | pub struct MyGreeter {} 23 | 24 | #[tonic::async_trait] 25 | impl Greeter for MyGreeter { 26 | async fn say_hello( 27 | &self, 28 | request: Request, 29 | ) -> Result, Status> { 30 | #[cfg(unix)] 31 | { 32 | let conn_info = request.extensions().get::().unwrap(); 33 | println!("Got a request {request:?} with info {conn_info:?}"); 34 | } 35 | 36 | let reply = hello_world::HelloReply { 37 | message: format!("Hello {}!", request.into_inner().name), 38 | }; 39 | Ok(Response::new(reply)) 40 | } 41 | } 42 | 43 | #[cfg(unix)] 44 | #[tokio::main] 45 | async fn main() -> Result<(), Box> { 46 | let path = "/tmp/tonic/helloworld"; 47 | 48 | std::fs::create_dir_all(Path::new(path).parent().unwrap())?; 49 | 50 | let greeter = MyGreeter::default(); 51 | 52 | let uds = UnixListener::bind(path)?; 53 | let uds_stream = UnixListenerStream::new(uds); 54 | 55 | Server::builder() 56 | .add_service(GreeterServer::new(greeter)) 57 | .serve_with_incoming(uds_stream) 58 | .await?; 59 | 60 | Ok(()) 61 | } 62 | 63 | #[cfg(not(unix))] 64 | fn main() { 65 | panic!("The `uds` example only works on unix systems!"); 66 | } 67 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Description for the project"; 3 | 4 | inputs = { 5 | flake-parts.url = "github:hercules-ci/flake-parts"; 6 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 7 | fenix = { 8 | url = "github:nix-community/fenix"; 9 | inputs.nixpkgs.follows = "nixpkgs"; 10 | }; 11 | 12 | git-hooks = { 13 | url = "github:cachix/git-hooks.nix"; 14 | inputs = { nixpkgs.follows = "nixpkgs"; }; 15 | }; 16 | 17 | treefmt-nix = { 18 | url = "github:numtide/treefmt-nix"; 19 | inputs.nixpkgs.follows = "nixpkgs"; 20 | }; 21 | }; 22 | 23 | outputs = inputs@{ self, flake-parts, ... }: 24 | flake-parts.lib.mkFlake { inherit inputs; } { 25 | imports = [ inputs.git-hooks.flakeModule inputs.treefmt-nix.flakeModule ]; 26 | systems = 27 | [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" ]; 28 | perSystem = { config, pkgs, system, ... }: 29 | let rustToolchain = pkgs.fenix.stable; 30 | in { 31 | _module.args.pkgs = import inputs.nixpkgs { 32 | inherit system; 33 | overlays = [ inputs.fenix.overlays.default ]; 34 | config = { }; 35 | }; 36 | 37 | formatter = config.treefmt.build.wrapper; 38 | checks.formatting = config.treefmt.build.check self; 39 | 40 | pre-commit = { 41 | check.enable = true; 42 | settings.hooks = { 43 | actionlint.enable = true; 44 | shellcheck.enable = true; 45 | treefmt.enable = true; 46 | }; 47 | }; 48 | 49 | treefmt = { 50 | settings = { rustfmt.enable = true; }; 51 | projectRootFile = ".git/config"; 52 | flakeCheck = false; # Covered by git-hooks check 53 | }; 54 | 55 | devShells.default = pkgs.mkShell { 56 | packages = with pkgs; [ 57 | nixd 58 | nixfmt 59 | 60 | (rustToolchain.withComponents [ 61 | "cargo" 62 | "clippy" 63 | "rust-src" 64 | "rustc" 65 | "rustfmt" 66 | "rust-analyzer" 67 | ]) 68 | protobuf 69 | ]; 70 | }; 71 | }; 72 | }; 73 | } 74 | -------------------------------------------------------------------------------- /grpc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "grpc" 3 | version = "0.9.0-alpha.1" 4 | edition = "2021" 5 | authors = ["gRPC Authors"] 6 | license = "Apache-2.0" 7 | 8 | [dependencies] 9 | url = "2.5.0" 10 | tokio = { version = "1.37.0", features = ["sync"] } 11 | tonic = { version = "0.13.0", path = "../tonic", default-features = false, features = ["codegen"] } 12 | -------------------------------------------------------------------------------- /grpc/NOTICE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2025 gRPC authors. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /grpc/src/attributes.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2025 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | /// A key-value store for arbitrary configuration data between multiple 20 | /// pluggable components. 21 | #[derive(Debug, Default, Clone)] 22 | pub struct Attributes; 23 | -------------------------------------------------------------------------------- /grpc/src/client/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2025 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | pub(crate) mod load_balancing; 20 | pub(crate) mod name_resolution; 21 | pub mod service; 22 | pub mod service_config; 23 | 24 | /// A representation of the current state of a gRPC channel, also used for the 25 | /// state of subchannels (individual connections within the channel). 26 | /// 27 | /// A gRPC channel begins in the Idle state. When an RPC is attempted, the 28 | /// channel will automatically transition to Connecting. If connections to a 29 | /// backend service are available, the state becomes Ready. Otherwise, if RPCs 30 | /// would fail due to a lack of connections, the state becomes TransientFailure 31 | /// and continues to attempt to reconnect. 32 | /// 33 | /// Channels may re-enter the Idle state if they are unused for longer than 34 | /// their configured idleness timeout. 35 | #[derive(Copy, Clone, PartialEq, Debug)] 36 | pub enum ConnectivityState { 37 | Idle, 38 | Connecting, 39 | Ready, 40 | TransientFailure, 41 | } 42 | -------------------------------------------------------------------------------- /grpc/src/client/service.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2025 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | /// A gRPC Request. 20 | pub struct Request; 21 | 22 | /// A gRPC Response. 23 | pub struct Response; 24 | -------------------------------------------------------------------------------- /grpc/src/client/service_config.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2025 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | /// An in-memory representation of a service config, usually provided to gRPC as 20 | /// a JSON object. 21 | #[derive(Debug, Default)] 22 | pub(crate) struct ServiceConfig; 23 | -------------------------------------------------------------------------------- /grpc/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2025 gRPC authors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | //! The official Rust implementation of [gRPC], a high performance, open source, 20 | //! universal RPC framework 21 | //! 22 | //! This version is in progress and not recommended for any production use. All 23 | //! APIs are unstable. Proceed at your own risk. 24 | //! 25 | //! [gRPC]: https://grpc.io 26 | 27 | #![allow(dead_code)] 28 | 29 | pub mod client; 30 | 31 | pub(crate) mod attributes; 32 | -------------------------------------------------------------------------------- /interop/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Lucio Franco "] 3 | edition = "2021" 4 | license = "MIT" 5 | name = "interop" 6 | 7 | [[bin]] 8 | name = "client" 9 | path = "src/bin/client.rs" 10 | 11 | [[bin]] 12 | name = "server" 13 | path = "src/bin/server.rs" 14 | 15 | [dependencies] 16 | async-stream = "0.3" 17 | strum = {version = "0.27", features = ["derive"]} 18 | pico-args = {version = "0.5", features = ["eq-separator"]} 19 | console = "0.15" 20 | http = "1" 21 | http-body-util = "0.1" 22 | prost = "0.13" 23 | tokio = {version = "1.0", features = ["rt-multi-thread", "time", "macros"]} 24 | tokio-stream = "0.1" 25 | tonic = {path = "../tonic", features = ["tls-ring"]} 26 | tower = "0.5" 27 | tracing-subscriber = {version = "0.3"} 28 | 29 | [build-dependencies] 30 | tonic-build = {path = "../tonic-build", features = ["prost"]} 31 | -------------------------------------------------------------------------------- /interop/bin/client_darwin_amd64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperium/tonic/689a86dbad107fa721714bb02e7f8e39264a377d/interop/bin/client_darwin_amd64 -------------------------------------------------------------------------------- /interop/bin/client_linux_amd64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperium/tonic/689a86dbad107fa721714bb02e7f8e39264a377d/interop/bin/client_linux_amd64 -------------------------------------------------------------------------------- /interop/bin/client_windows_amd64.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperium/tonic/689a86dbad107fa721714bb02e7f8e39264a377d/interop/bin/client_windows_amd64.exe -------------------------------------------------------------------------------- /interop/bin/server_darwin_amd64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperium/tonic/689a86dbad107fa721714bb02e7f8e39264a377d/interop/bin/server_darwin_amd64 -------------------------------------------------------------------------------- /interop/bin/server_linux_amd64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperium/tonic/689a86dbad107fa721714bb02e7f8e39264a377d/interop/bin/server_linux_amd64 -------------------------------------------------------------------------------- /interop/bin/server_windows_amd64.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperium/tonic/689a86dbad107fa721714bb02e7f8e39264a377d/interop/bin/server_windows_amd64.exe -------------------------------------------------------------------------------- /interop/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let proto = "proto/grpc/testing/test.proto"; 3 | 4 | tonic_build::compile_protos(proto).unwrap(); 5 | 6 | // prevent needing to rebuild if files (or deps) haven't changed 7 | println!("cargo:rerun-if-changed={proto}"); 8 | } 9 | -------------------------------------------------------------------------------- /interop/data/README.md: -------------------------------------------------------------------------------- 1 | # Tonic Testing Certificates 2 | 3 | This directory contains certificates used for testing interop between Tonic's 4 | implementation of gRPC and the Go implementation. Certificates are generated 5 | using [`terraform`][tf]. 6 | 7 | To regenerate certificates for some reason, do the following: 8 | 9 | 1. Install Terraform 0.12 (or higher) 10 | 1. From the `cert-generator` directory, run: 11 | 1. `terraform init` 12 | 1. `terraform apply` 13 | 14 | This will generate certificates and write them to the filesystem. The effective 15 | version should be committed to git. 16 | 17 | [tf]: https://terraform.io 18 | -------------------------------------------------------------------------------- /interop/data/ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDRjCCAi6gAwIBAgIQd5pnuFdwgGxb4RiClYEPMTANBgkqhkiG9w0BAQsFADA9 3 | MQ4wDAYDVQQKEwVUb2tpbzEQMA4GA1UECxMHVGVzdGluZzEZMBcGA1UEAxMQVG9u 4 | aWMgVGVzdGluZyBDQTAeFw0yNTA1MDExNzQyNThaFw0zNTA0MjkxNzQyNThaMD0x 5 | DjAMBgNVBAoTBVRva2lvMRAwDgYDVQQLEwdUZXN0aW5nMRkwFwYDVQQDExBUb25p 6 | YyBUZXN0aW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2C37 7 | LVCs4RfNdwv8NMZfIdFNqrUdwzXZ+a5B7Pee1nOL+JD9feOGn1qGZI1ZgFMqVygN 8 | ejSkzlbouN9RAGgyBmOFFo3oEc+nz7kPrezBLoM3oVgNzhEixz2IQoafoZX3j48Y 9 | fpGYmrTHUp4MAwUAt6Zb+kD7YGqD8//I5OMM4Y5R8yuYGsJHUUSZqYfgXCk0ZvVG 10 | EX7zyr31cVLqto1vpuv5Uvp6WX5oGgbZVB0wvlqs9Ak+dblWBZQIsrUPU8kn/6kx 11 | HilF8Lw24dRXr5oveFDMdD/n4sIh7Gr/O+VGH83gP/PawXy0WWn5qGAhdx+P99jI 12 | UGAWNetu4vGgASLFkwIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAgQwDwYDVR0TAQH/ 13 | BAUwAwEB/zAdBgNVHQ4EFgQU4AI0rUoGKFxe0gXIYujrhnuNsocwDQYJKoZIhvcN 14 | AQELBQADggEBAEtSrTgz+suVMDRsdvxG7+BAcXfVc8pmou3KIFbDtIyog2BEU5nU 15 | Z+zBqrVgbGv9B4D7XLcZgTShY17cvJP8QpUwT/gzI9uR8Lig9JGF7n1K43+aiAk9 16 | s7H8e74rwyPX6mRmuznd1sJdDsc5lohUPpZVI+7pRywedQw+QG6/n2cVvR0k0Txh 17 | pF1XBpzuFA5t5uqW/v/QFqfGEuIDDMdW2JQSEB7UyH4V2yWswoYb/uf/xoNXWWqs 18 | Y6RVSp6qVW8748rPPwmLaN8hHGIUNUnilQIXr67bX8i3FjoLHhQvKqUEKciXJWj9 19 | ssGOvq0QoVZNPltcZp9yID3W2kyxv6Hq8VA= 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /interop/data/cert-generator/.gitignore: -------------------------------------------------------------------------------- 1 | .terraform/ 2 | *.tfstate 3 | *.tfstate.backup 4 | -------------------------------------------------------------------------------- /interop/data/cert-generator/ca.tf: -------------------------------------------------------------------------------- 1 | resource "tls_private_key" "root" { 2 | algorithm = "RSA" 3 | rsa_bits = "2048" 4 | } 5 | 6 | resource "tls_self_signed_cert" "root" { 7 | private_key_pem = tls_private_key.root.private_key_pem 8 | 9 | validity_period_hours = 87600 10 | early_renewal_hours = 8760 11 | 12 | is_ca_certificate = true 13 | 14 | allowed_uses = ["cert_signing"] 15 | 16 | subject { 17 | common_name = "Tonic Testing CA" 18 | organization = "Tokio" 19 | organizational_unit = "Testing" 20 | } 21 | } 22 | 23 | resource "local_file" "ca_cert" { 24 | filename = "../ca.pem" 25 | content = tls_self_signed_cert.root.cert_pem 26 | } 27 | -------------------------------------------------------------------------------- /interop/data/cert-generator/server_certs.tf: -------------------------------------------------------------------------------- 1 | resource "tls_private_key" "server" { 2 | algorithm = "RSA" 3 | rsa_bits = "2048" 4 | } 5 | 6 | resource "tls_cert_request" "server" { 7 | private_key_pem = tls_private_key.server.private_key_pem 8 | 9 | subject { 10 | common_name = "Tonic Test Server Cert" 11 | } 12 | 13 | dns_names = [ 14 | "*.test.google.fr", 15 | ] 16 | } 17 | 18 | resource "tls_locally_signed_cert" "server" { 19 | cert_request_pem = tls_cert_request.server.cert_request_pem 20 | 21 | ca_private_key_pem = tls_private_key.root.private_key_pem 22 | ca_cert_pem = tls_self_signed_cert.root.cert_pem 23 | 24 | validity_period_hours = 43800 25 | early_renewal_hours = 8760 26 | 27 | allowed_uses = ["server_auth"] 28 | } 29 | 30 | resource "local_file" "server_cert" { 31 | filename = "../server1.pem" 32 | content = tls_locally_signed_cert.server.cert_pem 33 | } 34 | 35 | resource "local_file" "server_key" { 36 | filename = "../server1.key" 37 | content = tls_private_key.server.private_key_pem 38 | } 39 | -------------------------------------------------------------------------------- /interop/data/server1.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEA7iJJ8gLlKsp+r15CR15Iz2rmi3f3OZmA8FZ0hpB5hNkQHfVA 3 | RlC2yawIfHiLO4tpUmjtX8iq3RXPkKPYP5Zfd1BDLdR/2qhd3vFJnRVfoiqTMNOV 4 | 3R3+tIm04gDbtGxIDuWL+No/r/KldFxwbLqYTXDOaa145YI2aZ3GZ3P6GFYls6h7 5 | PeUqlXv7yWx1jfcMIZPeupHwWESYCCLkpvBHFZftWhc/FChUmgr417vmQC2eGwuX 6 | LyRdu+Lv9NmSzsO6A+w8ss62ewC02LXkXAG2Prd1GYuScsq94GcE5lguC5TVkdTm 7 | tQMDmET/KvitG3vLB9AIkKnjZdi1Ml7Ow6dByQIDAQABAoIBAFo7ketrH2z8d855 8 | mAG0/z/hEOSuG3au7MWk7NiEbBdjrJC9epJqSSjX0AtiHdf9NnZsne2aeuv1NMZo 9 | 3ysRDrGGLz5xc9Tl0VQF98/W5nrrSQTKV9IGaJn+SBUPIDEYiqFiZ4xvHozME9eo 10 | o0z/03Acm4o9mj7U/Us95o0SzCRl3QKgAWeSS36Ks0OmDJfNuYBWf+WA7Fte9NUp 11 | yOOm2fGejoge9eMcJY3/7HckrESscMECZMUL1hBbVD939d4S4AvM6YWTErAa9uq9 12 | APsXdu5IYglonqw6oc4TtN9bI9gbHKTyiFgi42gM6qcN2ixpQ78ufktLcJLBTLzi 13 | jP5f5cUCgYEA7whtTRG+KN3nAaRy5gU3JDdOIM1tlAVjwtvUIre3sf6p6Bzs0+RL 14 | DVdOidJB+8wnV6hF64+juHS27Y7t4ONt2VRFNmY3yRlb9MwqOYlqGaOOewgY+Gab 15 | ZC4GBKmMRKW0LpeRHghpCeyeRRKr5tkYalyU9/C+mxIFpb0/NZZXh6sCgYEA/wmG 16 | s+npJH2Xs17Yd3wwroZpFAlG2wRCd0IS14brKr+w5czbGis8Pz/LCVrNH+6ZkvoI 17 | gUpTDwY7egt9O2iCIeSeq82Ov+g9WiDa150YTq8av09N7AZ13Na+SU5aNpPwIOEZ 18 | WX8dygNloSh4JDjOhrwigRtcMmYCtpKVS792GFsCgYEA6QEB6rp870E/ya4QAoDa 19 | +4adtgQJ6NxIHs5Cv4Tun6dq4EQx52sGbf7JJDe88kJTp3L0lWbzZP8AwhktcKbB 20 | kbQ/s4N4paL+rGXIU0XMEyoH3Y5LKPh8SO9EFo9fmBsexLwiTXBNU8s/jH1i7Ch7 21 | UFLnM7mNU4QB1Ungr8/ZivkCgYA6sA2ATz5oOEJ1c0jqzfhB4QpDIxNcCPHmkZzW 22 | XeS11KC3cNmmfvaBM4PcZjm3tGdArCrS3bCZT3zWS9iImDcB56Mfs9C6lo2vtMnH 23 | Pg4+5QqJpY0v2Bi9NelZ4x7dWlOyrTnxH1BSkU+Ms0xaQXw9AwQJo6smqdTMAJU8 24 | dhWN6wKBgQDRAjpfV77FE32XsBkHyGlaIanxu+gSJVLaHStZLV8qTvn//lnmr5Be 25 | abK4smlwHp5ZnTwqP3gMh/b8vfyPYHdK/2rCKSCEPf/JHtoABsw9Hvkr/N99MZQd 26 | S2l3PYQoQ8smUVYNWhdYvdRER8WFTk5rPX6fEoVne/sArxlwk8+8nw== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /interop/data/server1.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDTDCCAjSgAwIBAgIRAL1ZcIwdi/AfgLm2T41fHO4wDQYJKoZIhvcNAQELBQAw 3 | PTEOMAwGA1UEChMFVG9raW8xEDAOBgNVBAsTB1Rlc3RpbmcxGTAXBgNVBAMTEFRv 4 | bmljIFRlc3RpbmcgQ0EwHhcNMjUwNTAxMTc0MjU4WhcNMzAwNDMwMTc0MjU4WjAh 5 | MR8wHQYDVQQDExZUb25pYyBUZXN0IFNlcnZlciBDZXJ0MIIBIjANBgkqhkiG9w0B 6 | AQEFAAOCAQ8AMIIBCgKCAQEA7iJJ8gLlKsp+r15CR15Iz2rmi3f3OZmA8FZ0hpB5 7 | hNkQHfVARlC2yawIfHiLO4tpUmjtX8iq3RXPkKPYP5Zfd1BDLdR/2qhd3vFJnRVf 8 | oiqTMNOV3R3+tIm04gDbtGxIDuWL+No/r/KldFxwbLqYTXDOaa145YI2aZ3GZ3P6 9 | GFYls6h7PeUqlXv7yWx1jfcMIZPeupHwWESYCCLkpvBHFZftWhc/FChUmgr417vm 10 | QC2eGwuXLyRdu+Lv9NmSzsO6A+w8ss62ewC02LXkXAG2Prd1GYuScsq94GcE5lgu 11 | C5TVkdTmtQMDmET/KvitG3vLB9AIkKnjZdi1Ml7Ow6dByQIDAQABo2MwYTATBgNV 12 | HSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaAFOACNK1K 13 | BihcXtIFyGLo64Z7jbKHMBsGA1UdEQQUMBKCECoudGVzdC5nb29nbGUuZnIwDQYJ 14 | KoZIhvcNAQELBQADggEBAJP9h4voqemt8Jiw9lgXKOfZyydIHKvL8oeeNQLnn+Ch 15 | S8D32xRxDeql0oghbTFj1AUxs5X415YgyP4JBoQ8X+L7z3hvSHHildJjbDAM5l+D 16 | jHIr/G6+N6DzLi75WUpZkHFa0ZZ+jHkrxRFq3SsS2hzL93sZ8HoLoEXgGJYcuVYh 17 | duWmy1pv/TW8j3GcRE358rLyIzsAK2tJZOHC3MeDqvITfGfzeHxy/UG2bbGmXU8Z 18 | UoCFUGHhukNuESQFfPxoHsWnsxvCIvcIxGPj4NXSO0WJ9r7/A+UczSr+Vuc55h0E 19 | qrAl9EXltUWTjRZwdIvvas9N3y0ApxkMFNIRmMwUBGE= 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /interop/proto/grpc/testing/empty.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2015 gRPC authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package grpc.testing; 18 | 19 | // An empty message that you can re-use to avoid defining duplicated empty 20 | // messages in your project. A typical example is to use it as argument or the 21 | // return value of a service API. For instance: 22 | // 23 | // service Foo { 24 | // rpc Bar (grpc.testing.Empty) returns (grpc.testing.Empty) { }; 25 | // }; 26 | // 27 | message Empty {} -------------------------------------------------------------------------------- /interop/src/bin/server.rs: -------------------------------------------------------------------------------- 1 | use interop::server; 2 | use tonic::transport::Server; 3 | use tonic::transport::{Identity, ServerTlsConfig}; 4 | 5 | #[derive(Debug)] 6 | struct Opts { 7 | use_tls: bool, 8 | } 9 | 10 | impl Opts { 11 | fn parse() -> Result { 12 | let mut pargs = pico_args::Arguments::from_env(); 13 | Ok(Self { 14 | use_tls: pargs.contains("--use_tls"), 15 | }) 16 | } 17 | } 18 | 19 | #[tokio::main] 20 | async fn main() -> std::result::Result<(), Box> { 21 | interop::trace_init(); 22 | 23 | let matches = Opts::parse()?; 24 | 25 | let addr = "127.0.0.1:10000".parse().unwrap(); 26 | 27 | let mut builder = Server::builder(); 28 | 29 | if matches.use_tls { 30 | let cert = std::fs::read_to_string("interop/data/server1.pem")?; 31 | let key = std::fs::read_to_string("interop/data/server1.key")?; 32 | let identity = Identity::from_pem(cert, key); 33 | 34 | builder = builder.tls_config(ServerTlsConfig::new().identity(identity))?; 35 | } 36 | 37 | let test_service = server::TestServiceServer::new(server::TestService::default()); 38 | let unimplemented_service = 39 | server::UnimplementedServiceServer::new(server::UnimplementedService::default()); 40 | 41 | // Wrap this test_service with a service that will echo headers as trailers. 42 | let test_service_svc = server::EchoHeadersSvc::new(test_service); 43 | 44 | builder 45 | .add_service(test_service_svc) 46 | .add_service(unimplemented_service) 47 | .serve(addr) 48 | .await?; 49 | 50 | Ok(()) 51 | } 52 | -------------------------------------------------------------------------------- /interop/update_binaries.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # This script updates server and client go binaries for interop tests. 5 | # It clones grpc-go, compiles interop clients and servers for linux, windows 6 | # and macos and finally deletes the cloned repo. 7 | # 8 | # It is not meant to be executed on every test run or CI and should run from 9 | # inside tonic/interop. 10 | 11 | command -v go >/dev/null 2>&1 || { 12 | echo >&2 "go executable is not available" 13 | exit 1 14 | } 15 | 16 | if [ ! -d "./grpc-go" ]; then 17 | git clone https://github.com/grpc/grpc-go.git 18 | fi 19 | 20 | cd grpc-go 21 | 22 | PLATFORMS="darwin linux windows" 23 | ROLES="client server" 24 | ARCH=amd64 25 | 26 | for ROLE in $ROLES; do 27 | for OS in $PLATFORMS; do 28 | FILENAME="${ROLE}_${OS}_${ARCH}" 29 | if [[ "${OS}" == "windows" ]]; then FILENAME="${FILENAME}.exe"; fi 30 | GOOS=$OS GOARCH=$ARCH go build -o "../bin/$FILENAME" "./interop/$ROLE" 31 | done 32 | done 33 | 34 | rm -rf ../grpc-go 35 | -------------------------------------------------------------------------------- /prepare-release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Script which automates modifying source version fields, and creating a release 4 | # commit and tag. The commit and tag are not automatically pushed, nor are the 5 | # crates published (see publish-release.sh). 6 | 7 | set -ex 8 | 9 | if [ "$#" -ne 1 ] 10 | then 11 | echo "Usage: $0 " 12 | exit 1 13 | fi 14 | 15 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 16 | VERSION="$1" 17 | MINOR="$( echo "${VERSION}" | cut -d\. -f1-2 )" 18 | 19 | VERSION_MATCHER="([a-z0-9\\.-]+)" 20 | TONIC_CRATE_MATCHER="(tonic|tonic-[a-z]+)" 21 | 22 | # Update the README.md. 23 | sed -i -E "s/${TONIC_CRATE_MATCHER} = \"${VERSION_MATCHER}\"/\1 = \"${MINOR}\"/" "$DIR/examples/helloworld-tutorial.md" 24 | sed -i -E "s/${TONIC_CRATE_MATCHER} = \"${VERSION_MATCHER}\"/\1 = \"${MINOR}\"/" "$DIR/examples/routeguide-tutorial.md" 25 | 26 | CRATES=( \ 27 | "tonic" \ 28 | "tonic-build" \ 29 | "tonic-types" \ 30 | "tonic-reflection" \ 31 | "tonic-health" \ 32 | "tonic-web" \ 33 | ) 34 | 35 | for CRATE in "${CRATES[@]}"; do 36 | # Update Cargo.toml version fields. 37 | sed -i -E "s/^version = \"${VERSION_MATCHER}\"$/version = \"${VERSION}\"/" \ 38 | "$DIR/$CRATE/Cargo.toml" 39 | done 40 | -------------------------------------------------------------------------------- /publish-release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Script which automates publishing a crates.io release of the prost crates. 4 | 5 | set -ex 6 | 7 | if [ "$#" -ne 0 ] 8 | then 9 | echo "Usage: $0" 10 | exit 1 11 | fi 12 | 13 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 14 | 15 | CRATES=( \ 16 | "tonic" \ 17 | "tonic-build" \ 18 | "tonic-types" \ 19 | "tonic-reflection" \ 20 | "tonic-health" \ 21 | "tonic-web" \ 22 | ) 23 | 24 | for CRATE in "${CRATES[@]}"; do 25 | pushd "$DIR/$CRATE" 26 | 27 | echo "Publishing $CRATE" 28 | 29 | cargo publish 30 | 31 | popd 32 | done 33 | -------------------------------------------------------------------------------- /tests/ambiguous_methods/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Yonathan Randolph "] 3 | edition = "2021" 4 | license = "MIT" 5 | name = "ambiguous_methods" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | prost = "0.13" 11 | tonic = {path = "../../tonic"} 12 | 13 | [build-dependencies] 14 | tonic-build = {path = "../../tonic-build"} 15 | -------------------------------------------------------------------------------- /tests/ambiguous_methods/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tonic_build::compile_protos("proto/ambiguous_methods.proto").unwrap(); 3 | } 4 | -------------------------------------------------------------------------------- /tests/ambiguous_methods/proto/ambiguous_methods.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package ambiguous_methods; 4 | 5 | message DropReq {} 6 | message DropResp {} 7 | 8 | // The generated stubs can confuse drop and clone 9 | // with the same method names from Arc, 10 | // resulting in a compile error. 11 | service HelloService { 12 | rpc Drop (DropReq) returns (DropResp); 13 | rpc Clone (DropReq) returns (DropResp); 14 | } 15 | 16 | service HelloStreamingService { 17 | rpc Drop (DropReq) returns (stream DropResp); 18 | rpc Clone (DropReq) returns (stream DropResp); 19 | } 20 | -------------------------------------------------------------------------------- /tests/ambiguous_methods/src/lib.rs: -------------------------------------------------------------------------------- 1 | tonic::include_proto!("ambiguous_methods"); 2 | -------------------------------------------------------------------------------- /tests/compression/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Lucio Franco "] 3 | edition = "2021" 4 | license = "MIT" 5 | name = "compression" 6 | 7 | [dependencies] 8 | bytes = "1" 9 | http = "1" 10 | http-body = "1" 11 | http-body-util = "0.1" 12 | hyper-util = "0.1" 13 | paste = "1.0.12" 14 | pin-project = "1.0" 15 | prost = "0.13" 16 | tokio = {version = "1.0", features = ["macros", "rt-multi-thread", "net"]} 17 | tokio-stream = "0.1" 18 | tonic = {path = "../../tonic", features = ["gzip", "deflate", "zstd"]} 19 | tower = "0.5" 20 | tower-http = {version = "0.6", features = ["map-response-body", "map-request-body"]} 21 | 22 | [build-dependencies] 23 | tonic-build = {path = "../../tonic-build" } 24 | -------------------------------------------------------------------------------- /tests/compression/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tonic_build::compile_protos("proto/test.proto").unwrap(); 3 | } 4 | -------------------------------------------------------------------------------- /tests/compression/proto/test.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package test; 4 | 5 | import "google/protobuf/empty.proto"; 6 | 7 | service Test { 8 | rpc CompressOutputUnary(google.protobuf.Empty) returns (SomeData); 9 | rpc CompressInputUnary(SomeData) returns (google.protobuf.Empty); 10 | rpc CompressOutputServerStream(google.protobuf.Empty) returns (stream SomeData); 11 | rpc CompressInputClientStream(stream SomeData) returns (google.protobuf.Empty); 12 | rpc CompressOutputClientStream(stream SomeData) returns (SomeData); 13 | rpc CompressInputOutputBidirectionalStream(stream SomeData) returns (stream SomeData); 14 | } 15 | 16 | message SomeData { 17 | // include a bunch of data so there actually is something to compress 18 | bytes data = 1; 19 | } 20 | -------------------------------------------------------------------------------- /tests/default_stubs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Jordan Singh "] 3 | edition = "2021" 4 | license = "MIT" 5 | name = "default_stubs" 6 | 7 | [dependencies] 8 | tokio = {version = "1.0", features = ["macros", "rt-multi-thread", "net"]} 9 | tokio-stream = {version = "0.1", features = ["net"]} 10 | rand = "0.9" 11 | tonic = {path = "../../tonic"} 12 | 13 | [build-dependencies] 14 | tonic-build = {path = "../../tonic-build" } 15 | -------------------------------------------------------------------------------- /tests/default_stubs/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tonic_build::configure() 3 | .compile_protos(&["proto/test.proto"], &["proto"]) 4 | .unwrap(); 5 | tonic_build::configure() 6 | .generate_default_stubs(true) 7 | .compile_protos(&["proto/test_default.proto"], &["proto"]) 8 | .unwrap(); 9 | } 10 | -------------------------------------------------------------------------------- /tests/default_stubs/proto/test.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package test; 4 | 5 | import "google/protobuf/empty.proto"; 6 | 7 | service Test { 8 | rpc Unary(google.protobuf.Empty) returns (google.protobuf.Empty); 9 | rpc ServerStream(google.protobuf.Empty) returns (stream google.protobuf.Empty); 10 | rpc ClientStream(stream google.protobuf.Empty) returns (google.protobuf.Empty); 11 | rpc BidirectionalStream(stream google.protobuf.Empty) returns (stream google.protobuf.Empty); 12 | } -------------------------------------------------------------------------------- /tests/default_stubs/proto/test_default.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package test_default; 4 | 5 | import "google/protobuf/empty.proto"; 6 | 7 | service TestDefault { 8 | rpc Unary(google.protobuf.Empty) returns (google.protobuf.Empty); 9 | rpc ServerStream(google.protobuf.Empty) returns (stream google.protobuf.Empty); 10 | rpc ClientStream(stream google.protobuf.Empty) returns (google.protobuf.Empty); 11 | rpc BidirectionalStream(stream google.protobuf.Empty) returns (stream google.protobuf.Empty); 12 | } -------------------------------------------------------------------------------- /tests/default_stubs/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_imports)] 2 | 3 | mod test_defaults; 4 | 5 | use std::pin::Pin; 6 | use tokio_stream::{Stream, StreamExt}; 7 | use tonic::{Request, Response, Status, Streaming}; 8 | 9 | tonic::include_proto!("test"); 10 | tonic::include_proto!("test_default"); 11 | 12 | #[derive(Debug, Default)] 13 | struct Svc; 14 | 15 | #[tonic::async_trait] 16 | impl test_server::Test for Svc { 17 | type ServerStreamStream = Pin> + Send + 'static>>; 18 | type BidirectionalStreamStream = 19 | Pin> + Send + 'static>>; 20 | 21 | async fn unary(&self, _: Request<()>) -> Result, Status> { 22 | Err(Status::permission_denied("")) 23 | } 24 | 25 | async fn server_stream( 26 | &self, 27 | _: Request<()>, 28 | ) -> Result, Status> { 29 | Err(Status::permission_denied("")) 30 | } 31 | 32 | async fn client_stream(&self, _: Request>) -> Result, Status> { 33 | Err(Status::permission_denied("")) 34 | } 35 | 36 | async fn bidirectional_stream( 37 | &self, 38 | _: Request>, 39 | ) -> Result, Status> { 40 | Err(Status::permission_denied("")) 41 | } 42 | } 43 | 44 | #[tonic::async_trait] 45 | impl test_default_server::TestDefault for Svc { 46 | // Default unimplemented stubs provided here. 47 | } 48 | -------------------------------------------------------------------------------- /tests/deprecated_methods/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "deprecated_methods" 3 | edition = "2021" 4 | license = "MIT" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | prost = "0.13" 10 | tonic = { path = "../../tonic" } 11 | 12 | [build-dependencies] 13 | tonic-build = { path = "../../tonic-build" } 14 | -------------------------------------------------------------------------------- /tests/deprecated_methods/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tonic_build::compile_protos("proto/test.proto").unwrap(); 3 | } 4 | -------------------------------------------------------------------------------- /tests/deprecated_methods/proto/test.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package test; 4 | 5 | service Service1 { 6 | rpc Deprecated(Request) returns (Response) { 7 | option deprecated = true; 8 | } 9 | rpc NotDeprecated(Request) returns (Response); 10 | } 11 | 12 | message Request {} 13 | 14 | message Response {} 15 | -------------------------------------------------------------------------------- /tests/deprecated_methods/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod pb { 2 | tonic::include_proto!("test"); 3 | } 4 | -------------------------------------------------------------------------------- /tests/deprecated_methods/tests/deprecated_methods.rs: -------------------------------------------------------------------------------- 1 | use std::{fs, path::PathBuf}; 2 | 3 | #[test] 4 | fn test() { 5 | let path = PathBuf::from(std::env::var("OUT_DIR").unwrap()).join("test.rs"); 6 | let s = fs::read_to_string(path) 7 | .unwrap() 8 | .split_whitespace() 9 | .collect::>() 10 | .join(" "); 11 | assert!(s.contains("#[deprecated] pub async fn deprecated(")); 12 | assert!(!s.contains("#[deprecated] pub async fn not_deprecated(")); 13 | } 14 | -------------------------------------------------------------------------------- /tests/disable_comments/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["bouzuya "] 3 | edition = "2021" 4 | license = "MIT" 5 | name = "disable-comments" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | prost = "0.13" 11 | tonic = { path = "../../tonic" } 12 | 13 | [build-dependencies] 14 | prost-build = "0.13" 15 | tonic-build = { path = "../../tonic-build" } 16 | -------------------------------------------------------------------------------- /tests/disable_comments/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let mut config = prost_build::Config::default(); 3 | config.disable_comments(["test.Input1", "test.Output1"]); 4 | tonic_build::configure() 5 | .disable_comments("test.Service1") 6 | .disable_comments("test.Service1.Rpc1") 7 | .build_client(true) 8 | .build_server(true) 9 | .compile_protos_with_config(config, &["proto/test.proto"], &["proto"]) 10 | .unwrap(); 11 | } 12 | -------------------------------------------------------------------------------- /tests/disable_comments/proto/test.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package test; 4 | 5 | // This comment will be removed. 6 | service Service1 { 7 | // This comment will be removed. 8 | rpc Rpc1(Input1) returns (Output1); 9 | // This comment will not be removed. 10 | rpc Rpc2(Input2) returns (Output2); 11 | } 12 | 13 | // This comment will not be removed. 14 | service Service2 { 15 | // This comment will not be removed. 16 | rpc Rpc(Input1) returns (Output1); 17 | } 18 | 19 | // This comment will be removed. 20 | message Input1 {} 21 | 22 | // This comment will not be removed. 23 | message Input2 {} 24 | 25 | // This comment will be removed. 26 | message Output1 {} 27 | 28 | // This comment will not be removed. 29 | message Output2 {} 30 | -------------------------------------------------------------------------------- /tests/disable_comments/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod pb { 2 | tonic::include_proto!("test"); 3 | } 4 | -------------------------------------------------------------------------------- /tests/disable_comments/tests/disable_comments.rs: -------------------------------------------------------------------------------- 1 | use std::{fs, path::PathBuf}; 2 | 3 | #[test] 4 | fn test() { 5 | let path = PathBuf::from(std::env::var("OUT_DIR").unwrap()).join("test.rs"); 6 | let s = fs::read_to_string(path).unwrap(); 7 | assert!(!s.contains("This comment will be removed.")); 8 | let mut count = 0_usize; 9 | let mut index = 0_usize; 10 | while let Some(found) = s[index..].find("This comment will not be removed.") { 11 | index += found + 1; 12 | count += 1; 13 | } 14 | assert_eq!(count, 2 + 3 + 3); // message: 2, client: 3, server: 3 15 | } 16 | -------------------------------------------------------------------------------- /tests/extern_path/my_application/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Danny Hua "] 3 | edition = "2021" 4 | license = "MIT" 5 | name = "my_application" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | prost = "0.13" 11 | tonic = {path = "../../../tonic"} 12 | uuid = {package = "uuid1", path = "../uuid"} 13 | 14 | [build-dependencies] 15 | tonic-build = {path = "../../../tonic-build"} 16 | -------------------------------------------------------------------------------- /tests/extern_path/my_application/build.rs: -------------------------------------------------------------------------------- 1 | fn main() -> Result<(), std::io::Error> { 2 | tonic_build::configure() 3 | .build_server(false) 4 | .build_client(true) 5 | .extern_path(".uuid", "::uuid") 6 | .compile_protos( 7 | &["service.proto", "uuid.proto"], 8 | &["../proto/my_application", "../proto/uuid"], 9 | )?; 10 | Ok(()) 11 | } 12 | -------------------------------------------------------------------------------- /tests/extern_path/my_application/src/main.rs: -------------------------------------------------------------------------------- 1 | use uuid::DoSomething; 2 | mod pb { 3 | tonic::include_proto!("my_application"); 4 | } 5 | fn main() { 6 | // verify that extern_path to replace proto's with impl's from other crates works. 7 | let message = pb::MyMessage { 8 | message_id: Some(::uuid::Uuid { 9 | uuid_str: "".to_string(), 10 | }), 11 | some_payload: "".to_string(), 12 | }; 13 | dbg!(message.message_id.unwrap().do_it()); 14 | } 15 | #[cfg(test)] 16 | #[test] 17 | fn service_types_have_extern_types() { 18 | // verify that extern_path to replace proto's with impl's from other crates works. 19 | let message = pb::MyMessage { 20 | message_id: Some(::uuid::Uuid { 21 | uuid_str: "not really a uuid".to_string(), 22 | }), 23 | some_payload: "payload".to_string(), 24 | }; 25 | assert_eq!(message.message_id.unwrap().do_it(), "Done"); 26 | } 27 | -------------------------------------------------------------------------------- /tests/extern_path/proto/my_application/service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package my_application; 4 | 5 | option go_package = "my_applicationpb"; 6 | option java_multiple_files = true; 7 | option java_outer_classname = "ServiceProto"; 8 | option java_package = "com.my_application"; 9 | 10 | 11 | import "uuid.proto"; 12 | 13 | message MyMessage { 14 | uuid.Uuid message_id = 1; 15 | string some_payload = 2; 16 | } 17 | 18 | service MyService { 19 | rpc GetUuid(MyMessage) returns (uuid.Uuid){ 20 | 21 | } 22 | rpc GetMyMessage(uuid.Uuid) returns (MyMessage){ 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/extern_path/proto/uuid/uuid.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package uuid; 4 | 5 | option go_package = "uuidpb"; 6 | option java_multiple_files = true; 7 | option java_outer_classname = "UuidProto"; 8 | option java_package = "com.uuid"; 9 | 10 | 11 | message Uuid { 12 | string uuid_str = 1; 13 | } 14 | -------------------------------------------------------------------------------- /tests/extern_path/uuid/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Danny Hua "] 3 | edition = "2021" 4 | license = "MIT" 5 | name = "uuid1" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | prost = "0.13" 11 | [build-dependencies] 12 | prost-build = "0.13" 13 | -------------------------------------------------------------------------------- /tests/extern_path/uuid/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | prost_build::compile_protos(&["uuid/uuid.proto"], &["../proto/"]).unwrap(); 3 | } 4 | -------------------------------------------------------------------------------- /tests/extern_path/uuid/src/lib.rs: -------------------------------------------------------------------------------- 1 | include!(concat!(env!("OUT_DIR"), "/uuid.rs")); 2 | 3 | pub trait DoSomething { 4 | fn do_it(&self) -> String; 5 | } 6 | 7 | impl DoSomething for Uuid { 8 | fn do_it(&self) -> String { 9 | "Done".to_string() 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/included_service/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Lucio Franco "] 3 | edition = "2021" 4 | license = "MIT" 5 | name = "included_service" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | prost = "0.13" 11 | tonic = {path = "../../tonic"} 12 | 13 | [build-dependencies] 14 | tonic-build = {path = "../../tonic-build"} 15 | -------------------------------------------------------------------------------- /tests/included_service/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tonic_build::compile_protos("proto/includer.proto").unwrap(); 3 | } 4 | -------------------------------------------------------------------------------- /tests/included_service/proto/includee.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package includee; 4 | 5 | service Included { 6 | rpc SomeMethod(SomeRequest) returns (SomeResponse) {} 7 | } 8 | 9 | message SomeRequest {} 10 | 11 | message SomeResponse {} 12 | -------------------------------------------------------------------------------- /tests/included_service/proto/includer.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package includer; 4 | 5 | message TopMessage {} 6 | 7 | service TopService { 8 | rpc TopMethod(TopMessage) returns (TopMessage) {} 9 | } 10 | 11 | import "includee.proto"; 12 | -------------------------------------------------------------------------------- /tests/included_service/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod pb { 2 | tonic::include_proto!("includer"); 3 | } 4 | 5 | // Ensure that an RPC service, defined before including a file that defines 6 | // another service in a different protocol buffer package, is not incorrectly 7 | // cleared from the context of its package. 8 | type _Test = dyn pb::top_service_server::TopService; 9 | -------------------------------------------------------------------------------- /tests/integration_tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Lucio Franco "] 3 | edition = "2021" 4 | license = "MIT" 5 | name = "integration-tests" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | bytes = "1.0" 11 | prost = "0.13" 12 | tokio = {version = "1.0", features = ["macros", "rt-multi-thread", "net", "sync"]} 13 | tonic = {path = "../../tonic"} 14 | tracing-subscriber = {version = "0.3"} 15 | 16 | [dev-dependencies] 17 | http = "1" 18 | http-body = "1" 19 | hyper-util = "0.1" 20 | rustls = {version = "0.23", features = ["ring"]} 21 | tokio-stream = {version = "0.1.5", features = ["net"]} 22 | tower = "0.5" 23 | tower-http = { version = "0.6", features = ["set-header", "trace"] } 24 | tower-service = "0.3" 25 | 26 | [build-dependencies] 27 | tonic-build = {path = "../../tonic-build"} 28 | -------------------------------------------------------------------------------- /tests/integration_tests/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tonic_build::compile_protos("proto/test.proto").unwrap(); 3 | tonic_build::compile_protos("proto/stream.proto").unwrap(); 4 | } 5 | -------------------------------------------------------------------------------- /tests/integration_tests/proto/stream.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package stream; 4 | 5 | service TestStream { 6 | rpc StreamCall(InputStream) returns (stream OutputStream); 7 | } 8 | 9 | message InputStream {} 10 | message OutputStream {} 11 | -------------------------------------------------------------------------------- /tests/integration_tests/proto/test.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package test; 4 | 5 | service Test { 6 | rpc UnaryCall(Input) returns (Output); 7 | } 8 | 9 | message Input {} 10 | message Output {} 11 | 12 | service Test1 { 13 | rpc UnaryCall(Input1) returns (Output1); 14 | 15 | rpc StreamCall(Input1) returns (stream Output1); 16 | } 17 | 18 | message Input1 { 19 | bytes buf = 1; 20 | } 21 | message Output1 { 22 | bytes buf = 1; 23 | } 24 | -------------------------------------------------------------------------------- /tests/integration_tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod pb { 2 | tonic::include_proto!("test"); 3 | tonic::include_proto!("stream"); 4 | } 5 | 6 | pub mod mock { 7 | use std::{ 8 | io::IoSlice, 9 | pin::Pin, 10 | task::{Context, Poll}, 11 | }; 12 | 13 | use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; 14 | use tonic::transport::server::Connected; 15 | 16 | #[derive(Debug)] 17 | pub struct MockStream(pub tokio::io::DuplexStream); 18 | 19 | impl Connected for MockStream { 20 | type ConnectInfo = (); 21 | 22 | /// Create type holding information about the connection. 23 | fn connect_info(&self) -> Self::ConnectInfo {} 24 | } 25 | 26 | impl AsyncRead for MockStream { 27 | fn poll_read( 28 | mut self: Pin<&mut Self>, 29 | cx: &mut Context<'_>, 30 | buf: &mut ReadBuf<'_>, 31 | ) -> Poll> { 32 | Pin::new(&mut self.0).poll_read(cx, buf) 33 | } 34 | } 35 | 36 | impl AsyncWrite for MockStream { 37 | fn poll_write( 38 | mut self: Pin<&mut Self>, 39 | cx: &mut Context<'_>, 40 | buf: &[u8], 41 | ) -> Poll> { 42 | Pin::new(&mut self.0).poll_write(cx, buf) 43 | } 44 | 45 | fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 46 | Pin::new(&mut self.0).poll_flush(cx) 47 | } 48 | 49 | fn poll_shutdown( 50 | mut self: Pin<&mut Self>, 51 | cx: &mut Context<'_>, 52 | ) -> Poll> { 53 | Pin::new(&mut self.0).poll_shutdown(cx) 54 | } 55 | 56 | fn poll_write_vectored( 57 | mut self: Pin<&mut Self>, 58 | cx: &mut Context<'_>, 59 | bufs: &[IoSlice<'_>], 60 | ) -> Poll> { 61 | Pin::new(&mut self.0).poll_write_vectored(cx, bufs) 62 | } 63 | 64 | fn is_write_vectored(&self) -> bool { 65 | self.0.is_write_vectored() 66 | } 67 | } 68 | } 69 | 70 | pub fn trace_init() { 71 | let _ = tracing_subscriber::fmt::try_init(); 72 | } 73 | 74 | pub type BoxFuture<'a, T> = std::pin::Pin + Send + 'a>>; 75 | -------------------------------------------------------------------------------- /tests/integration_tests/tests/client_layer.rs: -------------------------------------------------------------------------------- 1 | use http::{header::HeaderName, HeaderValue}; 2 | use integration_tests::pb::{test_client::TestClient, test_server, Input, Output}; 3 | use std::time::Duration; 4 | use tokio::{net::TcpListener, sync::oneshot}; 5 | use tonic::{ 6 | transport::{server::TcpIncoming, Endpoint, Server}, 7 | Request, Response, Status, 8 | }; 9 | use tower::ServiceBuilder; 10 | use tower_http::{set_header::SetRequestHeaderLayer, trace::TraceLayer}; 11 | 12 | #[tokio::test] 13 | async fn connect_supports_standard_tower_layers() { 14 | struct Svc; 15 | 16 | #[tonic::async_trait] 17 | impl test_server::Test for Svc { 18 | async fn unary_call(&self, req: Request) -> Result, Status> { 19 | match req.metadata().get("x-test") { 20 | Some(_) => Ok(Response::new(Output {})), 21 | None => Err(Status::internal("user-agent header is missing")), 22 | } 23 | } 24 | } 25 | 26 | let (tx, rx) = oneshot::channel(); 27 | let svc = test_server::TestServer::new(Svc); 28 | 29 | let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); 30 | let addr = listener.local_addr().unwrap(); 31 | let incoming = TcpIncoming::from(listener).with_nodelay(Some(true)); 32 | 33 | // Start the server now, second call should succeed 34 | let jh = tokio::spawn(async move { 35 | Server::builder() 36 | .add_service(svc) 37 | .serve_with_incoming_shutdown(incoming, async { drop(rx.await) }) 38 | .await 39 | .unwrap(); 40 | }); 41 | 42 | let channel = Endpoint::from_shared(format!("http://{addr}")) 43 | .unwrap() 44 | .connect_lazy(); 45 | 46 | // prior to https://github.com/hyperium/tonic/pull/974 47 | // this would not compile. (specifically the `TraceLayer`) 48 | let mut client = TestClient::new( 49 | ServiceBuilder::new() 50 | .layer(SetRequestHeaderLayer::overriding( 51 | HeaderName::from_static("x-test"), 52 | HeaderValue::from_static("test-header"), 53 | )) 54 | .layer(TraceLayer::new_for_grpc()) 55 | .service(channel), 56 | ); 57 | 58 | tokio::time::sleep(Duration::from_millis(100)).await; 59 | client.unary_call(Request::new(Input {})).await.unwrap(); 60 | 61 | tx.send(()).unwrap(); 62 | jh.await.unwrap(); 63 | } 64 | -------------------------------------------------------------------------------- /tests/integration_tests/tests/http2_max_header_list_size.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use integration_tests::pb::{test_client, test_server, Input, Output}; 4 | use tokio::sync::oneshot; 5 | use tonic::{ 6 | transport::{Endpoint, Server}, 7 | Request, Response, Status, 8 | }; 9 | 10 | /// This test checks that the max header list size is respected, and that 11 | /// it allows for error messages up to that size. 12 | #[tokio::test] 13 | async fn test_http_max_header_list_size_and_long_errors() { 14 | struct Svc; 15 | 16 | // The default value is 16k. 17 | const N: usize = 20_000; 18 | 19 | fn long_message() -> String { 20 | "a".repeat(N) 21 | } 22 | 23 | #[tonic::async_trait] 24 | impl test_server::Test for Svc { 25 | async fn unary_call(&self, _: Request) -> Result, Status> { 26 | Err(Status::internal(long_message())) 27 | } 28 | } 29 | 30 | let svc = test_server::TestServer::new(Svc); 31 | 32 | let (tx, rx) = oneshot::channel::<()>(); 33 | let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); 34 | let addr = format!("http://{}", listener.local_addr().unwrap()); 35 | 36 | let jh = tokio::spawn(async move { 37 | let (nodelay, keepalive) = (Some(true), Some(Duration::from_secs(1))); 38 | let listener = tonic::transport::server::TcpIncoming::from(listener) 39 | .with_nodelay(nodelay) 40 | .with_keepalive(keepalive); 41 | Server::builder() 42 | .http2_max_pending_accept_reset_streams(Some(0)) 43 | .add_service(svc) 44 | .serve_with_incoming_shutdown(listener, async { drop(rx.await) }) 45 | .await 46 | .unwrap(); 47 | }); 48 | 49 | tokio::time::sleep(Duration::from_millis(100)).await; 50 | 51 | let channel = Endpoint::from_shared(addr) 52 | .unwrap() 53 | // Increase the max header list size to 2x the default value. If this is 54 | // not set, this test will hang. See 55 | // . 56 | .http2_max_header_list_size(u32::try_from(N * 2).unwrap()) 57 | .connect() 58 | .await 59 | .unwrap(); 60 | 61 | let mut client = test_client::TestClient::new(channel); 62 | 63 | let err = client.unary_call(Request::new(Input {})).await.unwrap_err(); 64 | 65 | assert_eq!(err.message(), long_message()); 66 | 67 | tx.send(()).unwrap(); 68 | 69 | jh.await.unwrap(); 70 | } 71 | -------------------------------------------------------------------------------- /tests/integration_tests/tests/interceptor.rs: -------------------------------------------------------------------------------- 1 | use integration_tests::pb::{test_client::TestClient, test_server, Input, Output}; 2 | use std::time::Duration; 3 | use tokio::{net::TcpListener, sync::oneshot}; 4 | use tonic::{ 5 | transport::{server::TcpIncoming, Endpoint, Server}, 6 | GrpcMethod, Request, Response, Status, 7 | }; 8 | 9 | #[tokio::test] 10 | async fn interceptor_retrieves_grpc_method() { 11 | use test_server::Test; 12 | 13 | struct Svc; 14 | 15 | #[tonic::async_trait] 16 | impl Test for Svc { 17 | async fn unary_call(&self, _: Request) -> Result, Status> { 18 | Ok(Response::new(Output {})) 19 | } 20 | } 21 | 22 | let svc = test_server::TestServer::new(Svc); 23 | 24 | let (tx, rx) = oneshot::channel(); 25 | 26 | let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); 27 | let addr = listener.local_addr().unwrap(); 28 | let incoming = TcpIncoming::from(listener).with_nodelay(Some(true)); 29 | 30 | // Start the server now, second call should succeed 31 | let jh = tokio::spawn(async move { 32 | Server::builder() 33 | .add_service(svc) 34 | .serve_with_incoming_shutdown(incoming, async { drop(rx.await) }) 35 | .await 36 | .unwrap(); 37 | }); 38 | 39 | let channel = Endpoint::from_shared(format!("http://{addr}")) 40 | .unwrap() 41 | .connect_lazy(); 42 | 43 | fn client_intercept(req: Request<()>) -> Result, Status> { 44 | println!("Intercepting client request: {req:?}"); 45 | 46 | let gm = req.extensions().get::().unwrap(); 47 | assert_eq!(gm.service(), "test.Test"); 48 | assert_eq!(gm.method(), "UnaryCall"); 49 | 50 | Ok(req) 51 | } 52 | let mut client = TestClient::with_interceptor(channel, client_intercept); 53 | 54 | tokio::time::sleep(Duration::from_millis(100)).await; 55 | client.unary_call(Request::new(Input {})).await.unwrap(); 56 | 57 | tx.send(()).unwrap(); 58 | jh.await.unwrap(); 59 | } 60 | -------------------------------------------------------------------------------- /tests/integration_tests/tests/streams.rs: -------------------------------------------------------------------------------- 1 | use integration_tests::pb::{test_stream_server, InputStream, OutputStream}; 2 | use tokio::sync::oneshot; 3 | use tonic::{transport::Server, Request, Response, Status}; 4 | 5 | type Stream = std::pin::Pin< 6 | Box> + Send + 'static>, 7 | >; 8 | 9 | #[tokio::test] 10 | async fn status_from_server_stream_with_source() { 11 | struct Svc; 12 | 13 | #[tonic::async_trait] 14 | impl test_stream_server::TestStream for Svc { 15 | type StreamCallStream = Stream; 16 | 17 | async fn stream_call( 18 | &self, 19 | _: Request, 20 | ) -> Result, Status> { 21 | let s = Unsync(std::ptr::null_mut::<()>()); 22 | 23 | Ok(Response::new(Box::pin(s) as Self::StreamCallStream)) 24 | } 25 | } 26 | 27 | let svc = test_stream_server::TestStreamServer::new(Svc); 28 | 29 | let (tx, rx) = oneshot::channel::<()>(); 30 | 31 | let jh = tokio::spawn(async move { 32 | Server::builder() 33 | .add_service(svc) 34 | .serve_with_shutdown("127.0.0.1:0".parse().unwrap(), async { drop(rx.await) }) 35 | .await 36 | .unwrap(); 37 | }); 38 | 39 | tx.send(()).unwrap(); 40 | 41 | jh.await.unwrap(); 42 | } 43 | 44 | #[allow(dead_code)] 45 | struct Unsync(*mut ()); 46 | 47 | unsafe impl Send for Unsync {} 48 | 49 | impl tokio_stream::Stream for Unsync { 50 | type Item = Result; 51 | 52 | fn poll_next( 53 | self: std::pin::Pin<&mut Self>, 54 | _cx: &mut std::task::Context<'_>, 55 | ) -> std::task::Poll> { 56 | unimplemented!() 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/integration_tests/tests/user_agent.rs: -------------------------------------------------------------------------------- 1 | use integration_tests::pb::{test_client, test_server, Input, Output}; 2 | use std::time::Duration; 3 | use tokio::{net::TcpListener, sync::oneshot}; 4 | use tonic::{ 5 | transport::{server::TcpIncoming, Endpoint, Server}, 6 | Request, Response, Status, 7 | }; 8 | 9 | #[tokio::test] 10 | async fn writes_user_agent_header() { 11 | struct Svc; 12 | 13 | #[tonic::async_trait] 14 | impl test_server::Test for Svc { 15 | async fn unary_call(&self, req: Request) -> Result, Status> { 16 | match req.metadata().get("user-agent") { 17 | Some(_) => Ok(Response::new(Output {})), 18 | None => Err(Status::internal("user-agent header is missing")), 19 | } 20 | } 21 | } 22 | 23 | let svc = test_server::TestServer::new(Svc); 24 | 25 | let (tx, rx) = oneshot::channel::<()>(); 26 | 27 | let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); 28 | let addr = listener.local_addr().unwrap(); 29 | let incoming = TcpIncoming::from(listener).with_nodelay(Some(true)); 30 | 31 | let jh = tokio::spawn(async move { 32 | Server::builder() 33 | .add_service(svc) 34 | .serve_with_incoming_shutdown(incoming, async { drop(rx.await) }) 35 | .await 36 | .unwrap(); 37 | }); 38 | 39 | tokio::time::sleep(Duration::from_millis(100)).await; 40 | 41 | let channel = Endpoint::from_shared(format!("http://{addr}")) 42 | .unwrap() 43 | .user_agent("my-client") 44 | .expect("valid user agent") 45 | .connect() 46 | .await 47 | .unwrap(); 48 | 49 | let mut client = test_client::TestClient::new(channel); 50 | 51 | match client.unary_call(Input {}).await { 52 | Ok(_) => {} 53 | Err(status) => panic!("{}", status.message()), 54 | } 55 | 56 | tx.send(()).unwrap(); 57 | 58 | jh.await.unwrap(); 59 | } 60 | -------------------------------------------------------------------------------- /tests/root-crate-path/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Lucio Franco "] 3 | edition = "2021" 4 | license = "MIT" 5 | name = "root-crate-path" 6 | 7 | [dependencies] 8 | prost = "0.13" 9 | tonic = {path = "../../tonic"} 10 | 11 | [build-dependencies] 12 | tonic-build = {path = "../../tonic-build"} 13 | -------------------------------------------------------------------------------- /tests/root-crate-path/build.rs: -------------------------------------------------------------------------------- 1 | fn main() -> Result<(), Box> { 2 | tonic_build::configure() 3 | .extern_path(".foo.bar.baz.Animal", "crate::Animal") 4 | .compile_protos(&["foo.proto"], &["."])?; 5 | 6 | Ok(()) 7 | } 8 | -------------------------------------------------------------------------------- /tests/root-crate-path/foo.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | package foo.bar.baz; 4 | 5 | message Animal { 6 | optional string name = 1; 7 | } 8 | service Zoo { 9 | rpc process_animal(Animal) returns (Animal) {}; 10 | } 11 | -------------------------------------------------------------------------------- /tests/root-crate-path/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, PartialEq, ::prost::Message)] 2 | pub struct Animal { 3 | #[prost(string, optional, tag = "1")] 4 | pub name: ::core::option::Option<::prost::alloc::string::String>, 5 | } 6 | 7 | // pub mod foo; 8 | 9 | pub mod foo { 10 | pub mod bar { 11 | pub mod baz { 12 | tonic::include_proto!("foo.bar.baz"); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/same_name/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Lucio Franco "] 3 | edition = "2021" 4 | license = "MIT" 5 | name = "same_name" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | prost = "0.13" 11 | tonic = {path = "../../tonic"} 12 | 13 | [build-dependencies] 14 | tonic-build = {path = "../../tonic-build"} 15 | -------------------------------------------------------------------------------- /tests/same_name/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tonic_build::compile_protos("proto/foo.proto").unwrap(); 3 | } 4 | -------------------------------------------------------------------------------- /tests/same_name/proto/foo.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package foo; 4 | 5 | service Foo { 6 | rpc Foo(stream FooRequest) returns (stream FooResponse) {} 7 | } 8 | 9 | message FooRequest {} 10 | 11 | message FooResponse {} -------------------------------------------------------------------------------- /tests/same_name/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod pb { 2 | tonic::include_proto!("foo"); 3 | } 4 | -------------------------------------------------------------------------------- /tests/service_named_result/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "service_named_result" 3 | edition = "2021" 4 | license = "MIT" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | prost = "0.13" 10 | tonic = {path = "../../tonic"} 11 | 12 | [build-dependencies] 13 | tonic-build = {path = "../../tonic-build"} 14 | -------------------------------------------------------------------------------- /tests/service_named_result/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tonic_build::compile_protos("proto/result.proto").unwrap(); 3 | } 4 | -------------------------------------------------------------------------------- /tests/service_named_result/proto/result.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "google/protobuf/empty.proto"; 4 | 5 | package result; 6 | 7 | service Result { 8 | rpc Listen(google.protobuf.Empty) returns (Reply) {} 9 | } 10 | 11 | message Reply { 12 | int32 step = 1; 13 | } -------------------------------------------------------------------------------- /tests/service_named_result/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod pb { 2 | tonic::include_proto!("result"); 3 | } 4 | -------------------------------------------------------------------------------- /tests/service_named_service/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Lucio Franco "] 3 | edition = "2021" 4 | license = "MIT" 5 | name = "service_named_service" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | prost = "0.13" 11 | tonic = {path = "../../tonic"} 12 | 13 | [build-dependencies] 14 | tonic-build = {path = "../../tonic-build"} 15 | -------------------------------------------------------------------------------- /tests/service_named_service/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tonic_build::compile_protos("proto/foo.proto").unwrap(); 3 | } 4 | -------------------------------------------------------------------------------- /tests/service_named_service/proto/foo.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package foo; 4 | 5 | service Service { 6 | rpc Foo(stream FooRequest) returns (stream FooResponse) {} 7 | } 8 | 9 | message FooRequest {} 10 | 11 | message FooResponse {} 12 | -------------------------------------------------------------------------------- /tests/service_named_service/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod pb { 2 | tonic::include_proto!("foo"); 3 | } 4 | -------------------------------------------------------------------------------- /tests/skip_debug/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Andrew Yuan "] 3 | edition = "2021" 4 | license = "MIT" 5 | name = "skip_debug" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | prost = "0.13" 11 | tonic = { path = "../../tonic" } 12 | 13 | [build-dependencies] 14 | tonic-build = { path = "../../tonic-build" } 15 | -------------------------------------------------------------------------------- /tests/skip_debug/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tonic_build::configure() 3 | .skip_debug("test.Test") 4 | .skip_debug("test.Output") 5 | .build_client(true) 6 | .build_server(true) 7 | .compile_protos(&["proto/test.proto"], &["proto"]) 8 | .unwrap(); 9 | } 10 | -------------------------------------------------------------------------------- /tests/skip_debug/proto/test.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package test; 4 | 5 | service Test { 6 | rpc Rpc(Input) returns (Output); 7 | } 8 | 9 | message Input {} 10 | 11 | message Output {} 12 | -------------------------------------------------------------------------------- /tests/skip_debug/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod pb { 2 | tonic::include_proto!("test"); 3 | 4 | // Add a dummy impl Debug to the skipped debug implementations to avoid 5 | // missing impl Debug errors and check debug is not implemented for Output. 6 | impl std::fmt::Debug for Output { 7 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 8 | f.debug_struct("Output").finish() 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/skip_debug/tests/skip_debug.rs: -------------------------------------------------------------------------------- 1 | use std::{fs, path::PathBuf}; 2 | 3 | #[test] 4 | fn skip_debug() { 5 | let path = PathBuf::from(std::env::var("OUT_DIR").unwrap()).join("test.rs"); 6 | let s = fs::read_to_string(path).unwrap(); 7 | assert!(s.contains("#[prost(skip_debug)]\npub struct Output {}")); 8 | } 9 | -------------------------------------------------------------------------------- /tests/stream_conflict/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Ben Sully "] 3 | edition = "2021" 4 | name = "stream_conflict" 5 | license = "MIT" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | prost = "0.13" 11 | tonic = { path = "../../tonic" } 12 | 13 | [build-dependencies] 14 | tonic-build = { path = "../../tonic-build" } 15 | -------------------------------------------------------------------------------- /tests/stream_conflict/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tonic_build::compile_protos("proto/stream_conflict.proto").unwrap(); 3 | } 4 | -------------------------------------------------------------------------------- /tests/stream_conflict/proto/stream_conflict.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package StreamConflict; 3 | 4 | message Message { 5 | bool ok = 1; 6 | } 7 | 8 | service Stream { 9 | rpc RunStream(Message) returns (stream Message); 10 | } 11 | -------------------------------------------------------------------------------- /tests/stream_conflict/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod stream_conflict { 2 | tonic::include_proto!("stream_conflict"); 3 | } 4 | -------------------------------------------------------------------------------- /tests/use_arc_self/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Aurimas Blažulionis "] 3 | edition = "2021" 4 | license = "MIT" 5 | name = "use_arc_self" 6 | 7 | [dependencies] 8 | tokio-stream = "0.1" 9 | prost = "0.13" 10 | tonic = {path = "../../tonic", features = ["gzip"]} 11 | 12 | [build-dependencies] 13 | tonic-build = {path = "../../tonic-build" } 14 | -------------------------------------------------------------------------------- /tests/use_arc_self/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tonic_build::configure() 3 | .use_arc_self(true) 4 | .compile_protos(&["proto/test.proto"], &["proto"]) 5 | .unwrap(); 6 | } 7 | -------------------------------------------------------------------------------- /tests/use_arc_self/proto/test.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package test; 4 | 5 | service Test { 6 | rpc TestRequest(SomeData) returns (SomeData); 7 | } 8 | 9 | message SomeData { 10 | // include a bunch of data so there actually is something to compress 11 | bytes data = 1; 12 | } 13 | -------------------------------------------------------------------------------- /tests/use_arc_self/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_imports)] 2 | 3 | use std::sync::Arc; 4 | use tokio_stream::{Stream, StreamExt}; 5 | use tonic::{Request, Response, Status}; 6 | 7 | tonic::include_proto!("test"); 8 | 9 | #[derive(Debug, Default)] 10 | struct Svc; 11 | 12 | #[tonic::async_trait] 13 | impl test_server::Test for Svc { 14 | async fn test_request( 15 | self: Arc, 16 | req: Request, 17 | ) -> Result, Status> { 18 | Ok(Response::new(req.into_inner())) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/web/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Juan Alvarez "] 3 | edition = "2021" 4 | name = "test_web" 5 | license = "MIT" 6 | 7 | [dependencies] 8 | base64 = "0.22" 9 | bytes = "1.0" 10 | http-body-util = "0.1" 11 | hyper = "1" 12 | hyper-util = "0.1" 13 | prost = "0.13" 14 | tokio = { version = "1", features = ["macros", "rt", "net"] } 15 | tokio-stream = { version = "0.1", features = ["net"] } 16 | tonic = { path = "../../tonic" } 17 | 18 | [dev-dependencies] 19 | tonic-web = { path = "../../tonic-web" } 20 | 21 | [build-dependencies] 22 | tonic-build = { path = "../../tonic-build" } 23 | -------------------------------------------------------------------------------- /tests/web/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let protos = &["proto/test.proto"]; 3 | 4 | tonic_build::configure() 5 | .compile_protos(protos, &["proto"]) 6 | .unwrap(); 7 | 8 | protos 9 | .iter() 10 | .for_each(|file| println!("cargo:rerun-if-changed={file}")); 11 | } 12 | -------------------------------------------------------------------------------- /tests/web/proto/test.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package test; 4 | 5 | service Test { 6 | rpc UnaryCall(Input) returns (Output); 7 | rpc ServerStream(Input) returns (stream Output); 8 | rpc ClientStream(stream Input) returns (Output); 9 | } 10 | 11 | message Input { 12 | int32 id = 1; 13 | string desc = 2; 14 | } 15 | 16 | message Output { 17 | int32 id = 1; 18 | string desc = 2; 19 | } 20 | -------------------------------------------------------------------------------- /tests/wellknown-compiled/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Lucio Franco "] 3 | edition = "2021" 4 | license = "MIT" 5 | name = "wellknown-compiled" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [lib] 10 | doctest = false 11 | 12 | [dependencies] 13 | prost = "0.13" 14 | tonic = {path = "../../tonic"} 15 | 16 | [build-dependencies] 17 | prost-build = "0.13" 18 | tonic-build = {path = "../../tonic-build"} 19 | -------------------------------------------------------------------------------- /tests/wellknown-compiled/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let mut config = prost_build::Config::new(); 3 | config.extern_path(".google.protobuf.Empty", "()"); 4 | 5 | tonic_build::configure() 6 | .compile_well_known_types(true) 7 | .compile_protos_with_config( 8 | config, 9 | &["proto/google.proto", "proto/test.proto"], 10 | &["proto"], 11 | ) 12 | .unwrap(); 13 | } 14 | -------------------------------------------------------------------------------- /tests/wellknown-compiled/proto/google.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package google.protobuf; 4 | 5 | import "google/protobuf/any.proto"; 6 | import "google/protobuf/api.proto"; 7 | import "google/protobuf/descriptor.proto"; 8 | import "google/protobuf/duration.proto"; 9 | import "google/protobuf/empty.proto"; 10 | import "google/protobuf/field_mask.proto"; 11 | import "google/protobuf/source_context.proto"; 12 | import "google/protobuf/struct.proto"; 13 | import "google/protobuf/timestamp.proto"; 14 | import "google/protobuf/type.proto"; 15 | import "google/protobuf/wrappers.proto"; 16 | 17 | -------------------------------------------------------------------------------- /tests/wellknown-compiled/proto/test.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package test; 3 | 4 | import "google/protobuf/empty.proto"; 5 | 6 | message Input { 7 | } 8 | 9 | service Service { 10 | rpc Call(Input) returns (google.protobuf.Empty); 11 | } 12 | -------------------------------------------------------------------------------- /tests/wellknown-compiled/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod gen { 2 | pub mod google { 3 | pub mod protobuf { 4 | #![allow(clippy::doc_overindented_list_items)] 5 | tonic::include_proto!("google.protobuf"); 6 | } 7 | } 8 | 9 | pub mod test { 10 | tonic::include_proto!("test"); 11 | } 12 | } 13 | 14 | pub fn grok() { 15 | let _any = crate::gen::google::protobuf::Any { 16 | type_url: "foo".to_owned(), 17 | value: Vec::new(), 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /tests/wellknown/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Lucio Franco "] 3 | edition = "2021" 4 | license = "MIT" 5 | name = "wellknown" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | prost = "0.13" 11 | prost-types = "0.13" 12 | tonic = {path = "../../tonic"} 13 | 14 | [build-dependencies] 15 | tonic-build = {path = "../../tonic-build"} 16 | -------------------------------------------------------------------------------- /tests/wellknown/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tonic_build::compile_protos("proto/wellknown.proto").unwrap(); 3 | } 4 | -------------------------------------------------------------------------------- /tests/wellknown/proto/wellknown.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package wellknown; 4 | 5 | import "google/protobuf/empty.proto"; 6 | import "google/protobuf/wrappers.proto"; 7 | import "google/protobuf/any.proto"; 8 | 9 | service Admin { 10 | rpc EmptyCall(google.protobuf.Empty) returns (google.protobuf.Empty); 11 | rpc StringCall(google.protobuf.StringValue) returns (google.protobuf.Empty); 12 | rpc AnyCall(google.protobuf.Any) returns (google.protobuf.Empty); 13 | } 14 | -------------------------------------------------------------------------------- /tests/wellknown/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod pb { 2 | tonic::include_proto!("wellknown"); 3 | } 4 | -------------------------------------------------------------------------------- /tonic-build/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Lucio Franco "] 3 | categories = ["network-programming", "asynchronous"] 4 | description = """ 5 | Codegen module of `tonic` gRPC implementation. 6 | """ 7 | edition = "2021" 8 | homepage = "https://github.com/hyperium/tonic" 9 | keywords = ["rpc", "grpc", "async", "codegen", "protobuf"] 10 | license = "MIT" 11 | name = "tonic-build" 12 | readme = "README.md" 13 | repository = "https://github.com/hyperium/tonic" 14 | version = "0.13.1" 15 | rust-version = { workspace = true } 16 | 17 | [dependencies] 18 | prettyplease = { version = "0.2" } 19 | proc-macro2 = "1.0" 20 | prost-build = { version = "0.13", optional = true } 21 | prost-types = { version = "0.13", optional = true } 22 | quote = "1.0" 23 | syn = "2.0" 24 | 25 | [features] 26 | default = ["transport", "prost"] 27 | prost = ["prost-build", "dep:prost-types"] 28 | cleanup-markdown = ["prost-build?/cleanup-markdown"] 29 | transport = [] 30 | 31 | [lints] 32 | workspace = true 33 | 34 | [package.metadata.docs.rs] 35 | all-features = true 36 | 37 | [package.metadata.cargo_check_external_types] 38 | allowed_external_types = [ 39 | # major released 40 | "proc_macro2::*", 41 | 42 | # not major released 43 | "prost_build::*", 44 | "prost_types::*", 45 | ] 46 | -------------------------------------------------------------------------------- /tonic-build/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /tonic-build/src/compile_settings.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone)] 2 | pub(crate) struct CompileSettings { 3 | #[cfg(feature = "prost")] 4 | pub(crate) codec_path: String, 5 | } 6 | 7 | impl Default for CompileSettings { 8 | fn default() -> Self { 9 | Self { 10 | #[cfg(feature = "prost")] 11 | codec_path: "tonic::codec::ProstCodec".to_string(), 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tonic-health/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["James Nugent "] 3 | categories = ["network-programming", "asynchronous"] 4 | description = """ 5 | Health Checking module of `tonic` gRPC implementation. 6 | """ 7 | edition = "2021" 8 | homepage = "https://github.com/hyperium/tonic" 9 | keywords = ["rpc", "grpc", "async", "healthcheck"] 10 | license = "MIT" 11 | name = "tonic-health" 12 | readme = "README.md" 13 | repository = "https://github.com/hyperium/tonic" 14 | version = "0.13.1" 15 | rust-version = { workspace = true } 16 | 17 | [dependencies] 18 | prost = "0.13" 19 | tokio = {version = "1.0", features = ["sync"]} 20 | tokio-stream = {version = "0.1", default-features = false, features = ["sync"]} 21 | tonic = { version = "0.13.0", path = "../tonic", default-features = false, features = ["codegen", "prost"] } 22 | 23 | [dev-dependencies] 24 | tokio = {version = "1.0", features = ["rt-multi-thread", "macros"]} 25 | prost-types = "0.13.0" 26 | 27 | [lints] 28 | workspace = true 29 | 30 | [package.metadata.cargo_check_external_types] 31 | allowed_external_types = [ 32 | "tonic::*", 33 | 34 | # major released 35 | "bytes::*", 36 | "http::*", 37 | "http_body::*", 38 | 39 | # not major released 40 | "prost::*", 41 | 42 | "futures_core::stream::Stream", 43 | "tower_service::Service", 44 | ] 45 | -------------------------------------------------------------------------------- /tonic-health/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /tonic-health/README.md: -------------------------------------------------------------------------------- 1 | # tonic-health 2 | 3 | A `tonic` based gRPC healthcheck implementation. It closely follows the official [health checking protocol](https://github.com/grpc/grpc/blob/master/doc/health-checking.md), although it may not implement all features described in the specs. 4 | 5 | Please follow the example in the [main repo](https://github.com/hyperium/tonic/tree/master/examples/src/health) to see how it works. 6 | 7 | ## Features 8 | 9 | - transport: Provides the ability to set the service by using the type system and the 10 | `NamedService` trait. You can use it like that: 11 | ```rust 12 | let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; 13 | let client = HealthClient::new(conn); 14 | ``` 15 | -------------------------------------------------------------------------------- /tonic-reflection/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = [ 3 | "James Nugent ", 4 | "Samani G. Gikandi ", 5 | ] 6 | categories = ["network-programming", "asynchronous"] 7 | description = """ 8 | Server Reflection module of `tonic` gRPC implementation. 9 | """ 10 | edition = "2021" 11 | homepage = "https://github.com/hyperium/tonic" 12 | keywords = ["rpc", "grpc", "async", "reflection"] 13 | license = "MIT" 14 | name = "tonic-reflection" 15 | readme = "README.md" 16 | repository = "https://github.com/hyperium/tonic" 17 | version = "0.13.1" 18 | rust-version = { workspace = true } 19 | 20 | [package.metadata.docs.rs] 21 | all-features = true 22 | 23 | [features] 24 | server = ["dep:prost-types", "dep:tokio", "dep:tokio-stream"] 25 | default = ["server"] 26 | 27 | [dependencies] 28 | prost = "0.13" 29 | prost-types = {version = "0.13", optional = true} 30 | tokio = { version = "1.0", features = ["sync", "rt"], optional = true } 31 | tokio-stream = {version = "0.1", default-features = false, optional = true } 32 | tonic = { version = "0.13.0", path = "../tonic", default-features = false, features = ["codegen", "prost"] } 33 | 34 | [dev-dependencies] 35 | tokio-stream = {version = "0.1", default-features = false, features = ["net"]} 36 | tonic = { version = "0.13.0", path = "../tonic", default-features = false, features = ["transport"] } 37 | 38 | [lints] 39 | workspace = true 40 | 41 | [package.metadata.cargo_check_external_types] 42 | allowed_external_types = [ 43 | "tonic::*", 44 | 45 | # major released 46 | "bytes::*", 47 | "http::*", 48 | "http_body::*", 49 | 50 | # not major released 51 | "prost::*", 52 | "prost_types::*", 53 | 54 | "futures_core::stream::Stream", 55 | "tower_service::Service", 56 | ] 57 | -------------------------------------------------------------------------------- /tonic-reflection/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /tonic-reflection/README.md: -------------------------------------------------------------------------------- 1 | # tonic-reflection 2 | 3 | A `tonic` based gRPC reflection implementation. 4 | -------------------------------------------------------------------------------- /tonic-reflection/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A `tonic` based gRPC Server Reflection implementation. 2 | 3 | #![doc( 4 | html_logo_url = "https://github.com/hyperium/tonic/raw/master/.github/assets/tonic-docs.png" 5 | )] 6 | #![doc(issue_tracker_base_url = "https://github.com/hyperium/tonic/issues/")] 7 | #![doc(test(no_crate_inject, attr(deny(rust_2018_idioms))))] 8 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 9 | 10 | mod generated { 11 | #![allow(unreachable_pub)] 12 | #![allow(missing_docs)] 13 | #![allow(rustdoc::invalid_html_tags)] 14 | 15 | #[rustfmt::skip] 16 | pub mod grpc_reflection_v1alpha; 17 | 18 | #[rustfmt::skip] 19 | pub mod grpc_reflection_v1; 20 | 21 | #[rustfmt::skip] 22 | pub mod reflection_v1_fds; 23 | 24 | #[rustfmt::skip] 25 | pub mod reflection_v1alpha1_fds; 26 | 27 | pub use reflection_v1_fds::FILE_DESCRIPTOR_SET as FILE_DESCRIPTOR_SET_V1; 28 | pub use reflection_v1alpha1_fds::FILE_DESCRIPTOR_SET as FILE_DESCRIPTOR_SET_V1ALPHA; 29 | 30 | #[cfg(test)] 31 | mod tests { 32 | use super::{FILE_DESCRIPTOR_SET_V1, FILE_DESCRIPTOR_SET_V1ALPHA}; 33 | use prost::Message as _; 34 | 35 | #[test] 36 | fn v1alpha_file_descriptor_set_is_valid() { 37 | prost_types::FileDescriptorSet::decode(FILE_DESCRIPTOR_SET_V1ALPHA).unwrap(); 38 | } 39 | 40 | #[test] 41 | fn v1_file_descriptor_set_is_valid() { 42 | prost_types::FileDescriptorSet::decode(FILE_DESCRIPTOR_SET_V1).unwrap(); 43 | } 44 | } 45 | } 46 | 47 | /// Generated protobuf types from the `grpc.reflection` namespace. 48 | pub mod pb { 49 | /// Generated protobuf types from the `grpc.reflection.v1` package. 50 | pub mod v1 { 51 | pub use crate::generated::{ 52 | grpc_reflection_v1::*, FILE_DESCRIPTOR_SET_V1 as FILE_DESCRIPTOR_SET, 53 | }; 54 | } 55 | 56 | /// Generated protobuf types from the `grpc.reflection.v1alpha` package. 57 | pub mod v1alpha { 58 | pub use crate::generated::{ 59 | grpc_reflection_v1alpha::*, FILE_DESCRIPTOR_SET_V1ALPHA as FILE_DESCRIPTOR_SET, 60 | }; 61 | } 62 | } 63 | 64 | /// Implementation of the server component of gRPC Server Reflection. 65 | #[cfg(feature = "server")] 66 | pub mod server; 67 | -------------------------------------------------------------------------------- /tonic-types/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = [ 3 | "Lucio Franco ", 4 | "Rafael Lemos " 5 | ] 6 | categories = ["web-programming", "network-programming", "asynchronous"] 7 | description = """ 8 | A collection of useful protobuf types that can be used with `tonic`. 9 | """ 10 | edition = "2021" 11 | homepage = "https://github.com/hyperium/tonic" 12 | keywords = ["rpc", "grpc", "protobuf"] 13 | license = "MIT" 14 | name = "tonic-types" 15 | readme = "README.md" 16 | repository = "https://github.com/hyperium/tonic" 17 | version = "0.13.1" 18 | rust-version = { workspace = true } 19 | 20 | [dependencies] 21 | prost = "0.13" 22 | prost-types = "0.13" 23 | tonic = { version = "0.13.0", path = "../tonic", default-features = false } 24 | 25 | [lints] 26 | workspace = true 27 | 28 | [package.metadata.cargo_check_external_types] 29 | allowed_external_types = [ 30 | "tonic::*", 31 | 32 | # not major released 33 | "prost::*", 34 | "prost_types::*", 35 | ] 36 | -------------------------------------------------------------------------------- /tonic-types/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /tonic-types/proto/status.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package google.rpc; 18 | 19 | import "google/protobuf/any.proto"; 20 | 21 | option cc_enable_arenas = true; 22 | option go_package = "google.golang.org/genproto/googleapis/rpc/status;status"; 23 | option java_multiple_files = true; 24 | option java_outer_classname = "StatusProto"; 25 | option java_package = "com.google.rpc"; 26 | option objc_class_prefix = "RPC"; 27 | 28 | // The `Status` type defines a logical error model that is suitable for 29 | // different programming environments, including REST APIs and RPC APIs. It is 30 | // used by [gRPC](https://github.com/grpc). Each `Status` message contains 31 | // three pieces of data: error code, error message, and error details. 32 | // 33 | // You can find out more about this error model and how to work with it in the 34 | // [API Design Guide](https://cloud.google.com/apis/design/errors). 35 | message Status { 36 | // The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. 37 | int32 code = 1; 38 | 39 | // A developer-facing error message, which should be in English. Any 40 | // user-facing error message should be localized and sent in the 41 | // [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. 42 | string message = 2; 43 | 44 | // A list of messages that carry the error details. There is a common set of 45 | // message types for APIs to use. 46 | repeated google.protobuf.Any details = 3; 47 | } -------------------------------------------------------------------------------- /tonic-types/src/richer_error/std_messages/mod.rs: -------------------------------------------------------------------------------- 1 | mod retry_info; 2 | 3 | pub use retry_info::RetryInfo; 4 | 5 | mod debug_info; 6 | 7 | pub use debug_info::DebugInfo; 8 | 9 | mod quota_failure; 10 | 11 | pub use quota_failure::{QuotaFailure, QuotaViolation}; 12 | 13 | mod error_info; 14 | 15 | pub use error_info::ErrorInfo; 16 | 17 | mod prec_failure; 18 | 19 | pub use prec_failure::{PreconditionFailure, PreconditionViolation}; 20 | 21 | mod bad_request; 22 | 23 | pub use bad_request::{BadRequest, FieldViolation}; 24 | 25 | mod request_info; 26 | 27 | pub use request_info::RequestInfo; 28 | 29 | mod resource_info; 30 | 31 | pub use resource_info::ResourceInfo; 32 | 33 | mod help; 34 | 35 | pub use help::{Help, HelpLink}; 36 | 37 | mod loc_message; 38 | 39 | pub use loc_message::LocalizedMessage; 40 | -------------------------------------------------------------------------------- /tonic-web/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Juan Alvarez "] 3 | categories = ["network-programming", "asynchronous"] 4 | description = """ 5 | grpc-web protocol translation for tonic services. 6 | """ 7 | edition = "2021" 8 | homepage = "https://github.com/hyperium/tonic" 9 | keywords = ["rpc", "grpc", "grpc-web"] 10 | license = "MIT" 11 | name = "tonic-web" 12 | readme = "README.md" 13 | repository = "https://github.com/hyperium/tonic" 14 | version = "0.13.1" 15 | rust-version = { workspace = true } 16 | 17 | [dependencies] 18 | base64 = "0.22" 19 | bytes = "1" 20 | tokio-stream = { version = "0.1", default-features = false } 21 | http = "1" 22 | http-body = "1" 23 | pin-project = "1" 24 | tonic = { version = "0.13.0", path = "../tonic", default-features = false } 25 | tower-service = "0.3" 26 | tower-layer = "0.3" 27 | tracing = "0.1" 28 | 29 | [dev-dependencies] 30 | tokio = { version = "1", features = ["macros", "rt"] } 31 | tower-http = { version = "0.6", features = ["cors"] } 32 | axum = { version = "0.8", default-features = false } 33 | 34 | [lints] 35 | workspace = true 36 | 37 | [package.metadata.cargo_check_external_types] 38 | allowed_external_types = [ 39 | "tonic::*", 40 | 41 | # major released 42 | "bytes::*", 43 | "http::*", 44 | "http_body::*", 45 | 46 | # not major released 47 | "futures_core::stream::Stream", 48 | "tower_layer::Layer", 49 | "tower_service::Service", 50 | ] 51 | -------------------------------------------------------------------------------- /tonic-web/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /tonic-web/README.md: -------------------------------------------------------------------------------- 1 | # tonic-web 2 | 3 | Enables tonic servers to handle requests from `grpc-web` clients directly, 4 | without the need of an external proxy. 5 | 6 | ## Enabling tonic services 7 | 8 | The easiest way to get started, is to call the function with your tonic service 9 | and allow the tonic server to accept HTTP/1.1 requests: 10 | 11 | ```rust 12 | #[tokio::main] 13 | async fn main() -> Result<(), Box> { 14 | let addr = "[::1]:50051".parse().unwrap(); 15 | let greeter = GreeterServer::new(MyGreeter::default()); 16 | 17 | Server::builder() 18 | .accept_http1(true) 19 | .layer(GrpcWebLayer::new()) 20 | .add_service(greeter) 21 | .serve(addr) 22 | .await?; 23 | 24 | Ok(()) 25 | } 26 | ``` 27 | 28 | ## Examples 29 | 30 | See [the examples folder][example] for a server and client example. 31 | 32 | [example]: https://github.com/hyperium/tonic/tree/master/examples/src/grpc-web 33 | -------------------------------------------------------------------------------- /tonic-web/src/layer.rs: -------------------------------------------------------------------------------- 1 | use super::GrpcWebService; 2 | 3 | use tower_layer::Layer; 4 | 5 | /// Layer implementing the grpc-web protocol. 6 | #[derive(Debug, Default, Clone)] 7 | pub struct GrpcWebLayer { 8 | _priv: (), 9 | } 10 | 11 | impl GrpcWebLayer { 12 | /// Create a new grpc-web layer. 13 | pub fn new() -> GrpcWebLayer { 14 | Self::default() 15 | } 16 | } 17 | 18 | impl Layer for GrpcWebLayer { 19 | type Service = GrpcWebService; 20 | 21 | fn layer(&self, inner: S) -> Self::Service { 22 | GrpcWebService::new(inner) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tonic-web/tests/incomplete-buf-bug.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperium/tonic/689a86dbad107fa721714bb02e7f8e39264a377d/tonic-web/tests/incomplete-buf-bug.bin -------------------------------------------------------------------------------- /tonic/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /tonic/benches-disabled/README.md: -------------------------------------------------------------------------------- 1 | ## Criterion benchmarks for Tonic 2 | 3 | ### Running the benchmarks 4 | From the root Tonic directory, `cargo bench` 5 | 6 | After running, the reports can be found in `tonic/target/criterion/report/index.html` 7 | 8 | [Gnuplot](http://www.gnuplot.info/) is required for graph generation. If gnuplot is not installed, Criterion will display: `Gnuplot not found, disabling plotting` at the console. 9 | 10 | ### Notes 11 | 1) Currently, these benchmarks only test the performance of constructing Tonic Requests and Responses, not over-the-wire throughput. 12 | 2) The `thrpt` value generated by Criterion is simply a measure of bytes consumed by the target function. 13 | 3) As we are not testing tonic-build compile time, the tests reference pre-compiled .rs files in 'benchmarks/compiled_protos'. 14 | 4) The original proto files are in the `proto` directory for reference. 15 | 5) This used the Criterion 3.0 `Criterion Group` functionality. Details here: https://docs.rs/criterion/0.3.0/criterion/ 16 | 17 | ### Interpreting Results 18 | 19 | Criterion is particularly useful for establishing a first-run baseline and then comparing after code-changes - e.g. `Performance has regressed` below. 20 | 21 | ```bash 22 | Request_Response/request/100000 23 | time: [2.7231 us 2.7588 us 2.7969 us] 24 | thrpt: [33.298 GiB/s 33.758 GiB/s 34.200 GiB/s] 25 | change: 26 | time: [+16.073% +17.871% +19.980%] (p = 0.00 < 0.05) 27 | thrpt: [-16.653% -15.162% -13.847%] 28 | Performance has regressed. 29 | Found 3 outliers among 100 measurements (3.00%) 30 | 1 (1.00%) high mild 31 | 2 (2.00%) high severe 32 | ``` 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /tonic/benches-disabled/bench_main.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "broken")] 2 | 3 | use criterion::*; 4 | 5 | mod benchmarks; 6 | 7 | criterion_group!( 8 | benches, 9 | benchmarks::request_response::bench_throughput, 10 | benchmarks::request_response_diverse_types::bench_throughput, 11 | ); 12 | criterion_main!(benches); 13 | -------------------------------------------------------------------------------- /tonic/benches-disabled/benchmarks/compiled_protos/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod diverse_types; 2 | pub mod helloworld; 3 | -------------------------------------------------------------------------------- /tonic/benches-disabled/benchmarks/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod request_response; 2 | pub mod request_response_diverse_types; 3 | 4 | pub mod compiled_protos; 5 | mod utils; 6 | -------------------------------------------------------------------------------- /tonic/benches-disabled/benchmarks/request_response.rs: -------------------------------------------------------------------------------- 1 | use criterion::*; 2 | 3 | use crate::benchmarks::compiled_protos::helloworld::{HelloReply, HelloRequest}; 4 | use crate::benchmarks::utils; 5 | 6 | fn build_request(_name: String) { 7 | let _request = tonic::Request::new(HelloRequest { name: _name }); 8 | } 9 | 10 | fn build_response(_message: String) { 11 | let _response = tonic::Request::new(HelloReply { message: _message }); 12 | } 13 | 14 | pub fn bench_throughput(c: &mut Criterion) { 15 | let mut group = c.benchmark_group("Request_Response"); 16 | 17 | let plot_config = PlotConfiguration::default().summary_scale(AxisScale::Logarithmic); 18 | 19 | group.plot_config(plot_config); 20 | 21 | let tiny_string = utils::generate_rnd_string(100).unwrap(); 22 | let short_string = utils::generate_rnd_string(1_000).unwrap(); 23 | let medium_string = utils::generate_rnd_string(10_000).unwrap(); 24 | let big_string = utils::generate_rnd_string(100_000).unwrap(); 25 | let huge_string = utils::generate_rnd_string(1_000_000).unwrap(); 26 | let massive_string = utils::generate_rnd_string(10_000_000).unwrap(); 27 | 28 | for size in [ 29 | tiny_string, 30 | short_string, 31 | medium_string, 32 | big_string, 33 | huge_string, 34 | massive_string, 35 | ] 36 | .iter() 37 | { 38 | group.throughput(Throughput::Bytes(size.len() as u64)); 39 | 40 | group.bench_with_input(BenchmarkId::new("request", size.len()), size, |b, i| { 41 | b.iter(|| build_request(i.to_string())) 42 | }); 43 | group.bench_with_input(BenchmarkId::new("response", size.len()), size, |b, i| { 44 | b.iter(|| build_response(i.to_string())) 45 | }); 46 | } 47 | group.finish(); 48 | } 49 | -------------------------------------------------------------------------------- /tonic/benches-disabled/benchmarks/utils.rs: -------------------------------------------------------------------------------- 1 | use rand::distributions::Alphanumeric; 2 | use rand::{thread_rng, Rng}; 3 | 4 | pub fn generate_rnd_string(string_size: usize) -> Result> { 5 | let rand_name: String = thread_rng() 6 | .sample_iter(&Alphanumeric) 7 | .take(string_size) 8 | .collect(); 9 | 10 | Ok(rand_name) 11 | } 12 | -------------------------------------------------------------------------------- /tonic/benches-disabled/proto/diverse_types/diverse_types.proto: -------------------------------------------------------------------------------- 1 | // Benchmark messages for proto3. 2 | // Pinched from the protobuf benchmarks 3 | 4 | syntax = "proto3"; 5 | 6 | package benchmarks.proto3; 7 | option java_package = "com.google.protobuf.benchmarks"; 8 | 9 | // This is the default, but we specify it here explicitly. 10 | option optimize_for = SPEED; 11 | 12 | option cc_enable_arenas = true; 13 | 14 | message GoogleMessage1 { 15 | string field1 = 1; 16 | string field9 = 9; 17 | string field18 = 18; 18 | bool field80 = 80; 19 | bool field81 = 81; 20 | int32 field2 = 2; 21 | int32 field3 = 3; 22 | int32 field280 = 280; 23 | int32 field6 = 6; 24 | int64 field22 = 22; 25 | string field4 = 4; 26 | repeated fixed64 field5 = 5; 27 | bool field59 = 59; 28 | string field7 = 7; 29 | int32 field16 = 16; 30 | int32 field130 = 130; 31 | bool field12 = 12; 32 | bool field17 = 17; 33 | bool field13 = 13; 34 | bool field14 = 14; 35 | int32 field104 = 104; 36 | int32 field100 = 100; 37 | int32 field101 = 101; 38 | string field102 = 102; 39 | string field103 = 103; 40 | int32 field29 = 29; 41 | bool field30 = 30; 42 | int32 field60 = 60; 43 | int32 field271 = 271; 44 | int32 field272 = 272; 45 | int32 field150 = 150; 46 | int32 field23 = 23; 47 | bool field24 = 24; 48 | int32 field25 = 25; 49 | GoogleMessage1SubMessage field15 = 15; 50 | bool field78 = 78; 51 | int32 field67 = 67; 52 | int32 field68 = 68; 53 | int32 field128 = 128; 54 | string field129 = 129; 55 | int32 field131 = 131; 56 | } 57 | 58 | message GoogleMessage1SubMessage { 59 | int32 field1 = 1; 60 | int32 field2 = 2; 61 | int32 field3 = 3; 62 | string field15 = 15; 63 | bool field12 = 12; 64 | int64 field13 = 13; 65 | int64 field14 = 14; 66 | int32 field16 = 16; 67 | int32 field19 = 19; 68 | bool field20 = 20; 69 | bool field28 = 28; 70 | fixed64 field21 = 21; 71 | int32 field22 = 22; 72 | bool field23 = 23; 73 | bool field206 = 206; 74 | fixed32 field203 = 203; 75 | int32 field204 = 204; 76 | string field205 = 205; 77 | uint64 field207 = 207; 78 | uint64 field300 = 300; 79 | } 80 | -------------------------------------------------------------------------------- /tonic/benches-disabled/proto/helloworld/helloworld.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2015 gRPC authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | option java_multiple_files = true; 18 | option java_package = "io.grpc.examples.helloworld"; 19 | option java_outer_classname = "HelloWorldProto"; 20 | 21 | package helloworld; 22 | 23 | // The greeting service definition. 24 | service Greeter { 25 | // Sends a greeting 26 | rpc SayHello (HelloRequest) returns (HelloReply) {} 27 | } 28 | 29 | // The request message containing the user's name. 30 | message HelloRequest { 31 | string name = 1; 32 | } 33 | 34 | // The response message containing the greetings 35 | message HelloReply { 36 | string message = 1; 37 | } -------------------------------------------------------------------------------- /tonic/src/client/mod.rs: -------------------------------------------------------------------------------- 1 | //! Generic client implementation. 2 | //! 3 | //! This module contains the low level components to build a gRPC client. It 4 | //! provides a codec agnostic gRPC client dispatcher and a decorated tower 5 | //! service trait. 6 | //! 7 | //! This client is generally used by some code generation tool to provide stubs 8 | //! for the gRPC service. Thusly, they are a bit cumbersome to use by hand. 9 | //! 10 | //! ## Concurrent usage 11 | //! 12 | //! Upon using the your generated client, you will discover all the functions 13 | //! corresponding to your rpc methods take `&mut self`, making concurrent 14 | //! usage of the client difficult. The answer is simply to clone the client, 15 | //! which is cheap as all client instances will share the same channel for 16 | //! communication. For more details, see 17 | //! [transport::Channel](../transport/struct.Channel.html#multiplexing-requests). 18 | 19 | mod grpc; 20 | mod service; 21 | 22 | pub use self::grpc::Grpc; 23 | pub use self::service::GrpcService; 24 | -------------------------------------------------------------------------------- /tonic/src/client/service.rs: -------------------------------------------------------------------------------- 1 | use http_body::Body; 2 | use std::future::Future; 3 | use std::task::{Context, Poll}; 4 | use tower_service::Service; 5 | 6 | /// Definition of the gRPC trait alias for [`tower_service`]. 7 | /// 8 | /// This trait enforces that all tower services provided to [`Grpc`] implements 9 | /// the correct traits. 10 | /// 11 | /// [`Grpc`]: ../client/struct.Grpc.html 12 | /// [`tower_service`]: https://docs.rs/tower-service 13 | pub trait GrpcService { 14 | /// Responses body given by the service. 15 | type ResponseBody: Body; 16 | /// Errors produced by the service. 17 | type Error: Into; 18 | /// The future response value. 19 | type Future: Future, Self::Error>>; 20 | 21 | /// Returns `Ready` when the service is able to process requests. 22 | /// 23 | /// Reference [`Service::poll_ready`]. 24 | fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll>; 25 | 26 | /// Process the request and return the response asynchronously. 27 | /// 28 | /// Reference [`Service::call`]. 29 | fn call(&mut self, request: http::Request) -> Self::Future; 30 | } 31 | 32 | impl GrpcService for T 33 | where 34 | T: Service, Response = http::Response>, 35 | T::Error: Into, 36 | ResBody: Body, 37 | ::Error: Into, 38 | { 39 | type ResponseBody = ResBody; 40 | type Error = T::Error; 41 | type Future = T::Future; 42 | 43 | fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { 44 | Service::poll_ready(self, cx) 45 | } 46 | 47 | fn call(&mut self, request: http::Request) -> Self::Future { 48 | Service::call(self, request) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tonic/src/codegen.rs: -------------------------------------------------------------------------------- 1 | //! Codegen exports used by `tonic-build`. 2 | 3 | pub use async_trait::async_trait; 4 | pub use tokio_stream; 5 | 6 | pub use std::future::Future; 7 | pub use std::pin::Pin; 8 | pub use std::sync::Arc; 9 | pub use std::task::{Context, Poll}; 10 | pub use tower_service::Service; 11 | pub type StdError = Box; 12 | pub use crate::codec::{CompressionEncoding, EnabledCompressionEncodings}; 13 | pub use crate::extensions::GrpcMethod; 14 | pub use crate::service::interceptor::InterceptedService; 15 | pub use bytes::Bytes; 16 | pub use http; 17 | pub use http_body::Body; 18 | 19 | pub type BoxFuture = self::Pin> + Send + 'static>>; 20 | pub type BoxStream = 21 | self::Pin> + Send + 'static>>; 22 | -------------------------------------------------------------------------------- /tonic/src/extensions.rs: -------------------------------------------------------------------------------- 1 | /// A gRPC Method info extension. 2 | #[derive(Debug, Clone)] 3 | pub struct GrpcMethod<'a> { 4 | service: &'a str, 5 | method: &'a str, 6 | } 7 | 8 | impl<'a> GrpcMethod<'a> { 9 | /// Create a new `GrpcMethod` extension. 10 | #[doc(hidden)] 11 | pub fn new(service: &'a str, method: &'a str) -> Self { 12 | Self { service, method } 13 | } 14 | 15 | /// gRPC service name 16 | pub fn service(&self) -> &str { 17 | self.service 18 | } 19 | /// gRPC method name 20 | pub fn method(&self) -> &str { 21 | self.method 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tonic/src/metadata/mod.rs: -------------------------------------------------------------------------------- 1 | //! Contains data structures and utilities for handling gRPC custom metadata. 2 | 3 | mod encoding; 4 | mod key; 5 | mod map; 6 | mod value; 7 | 8 | pub use self::encoding::Ascii; 9 | pub use self::encoding::Binary; 10 | pub use self::key::AsciiMetadataKey; 11 | pub use self::key::BinaryMetadataKey; 12 | pub use self::key::MetadataKey; 13 | pub use self::map::Entry; 14 | pub use self::map::GetAll; 15 | pub use self::map::Iter; 16 | pub use self::map::IterMut; 17 | pub use self::map::KeyAndMutValueRef; 18 | pub use self::map::KeyAndValueRef; 19 | pub use self::map::KeyRef; 20 | pub use self::map::Keys; 21 | pub use self::map::MetadataMap; 22 | pub use self::map::OccupiedEntry; 23 | pub use self::map::VacantEntry; 24 | pub use self::map::ValueDrain; 25 | pub use self::map::ValueIter; 26 | pub use self::map::ValueRef; 27 | pub use self::map::ValueRefMut; 28 | pub use self::map::Values; 29 | pub use self::map::ValuesMut; 30 | pub use self::value::AsciiMetadataValue; 31 | pub use self::value::BinaryMetadataValue; 32 | pub use self::value::MetadataValue; 33 | use http::HeaderValue; 34 | 35 | pub(crate) use self::map::GRPC_TIMEOUT_HEADER; 36 | 37 | /// HTTP Header `content-type` value for gRPC calls. 38 | pub const GRPC_CONTENT_TYPE: HeaderValue = HeaderValue::from_static("application/grpc"); 39 | 40 | /// The metadata::errors module contains types for errors that can occur 41 | /// while handling gRPC custom metadata. 42 | pub mod errors { 43 | pub use super::encoding::InvalidMetadataValue; 44 | pub use super::encoding::InvalidMetadataValueBytes; 45 | pub use super::key::InvalidMetadataKey; 46 | pub use super::value::ToStrError; 47 | } 48 | -------------------------------------------------------------------------------- /tonic/src/server/mod.rs: -------------------------------------------------------------------------------- 1 | //! Generic server implementation. 2 | //! 3 | //! This module contains the low level components to build a gRPC server. It 4 | //! provides a codec agnostic gRPC server handler. 5 | //! 6 | //! The items in this module are generally designed to be used by some codegen 7 | //! tool that will provide the user some custom way to implement the server that 8 | //! will implement the proper gRPC service. Thusly, they are a bit hard to use 9 | //! by hand. 10 | 11 | mod grpc; 12 | mod service; 13 | 14 | pub use self::grpc::Grpc; 15 | pub use self::service::{ 16 | ClientStreamingService, ServerStreamingService, StreamingService, UnaryService, 17 | }; 18 | 19 | /// A trait to provide a static reference to the service's 20 | /// name. This is used for routing service's within the router. 21 | pub trait NamedService { 22 | /// The `Service-Name` as described [here]. 23 | /// 24 | /// [here]: https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests 25 | const NAME: &'static str; 26 | } 27 | -------------------------------------------------------------------------------- /tonic/src/service/mod.rs: -------------------------------------------------------------------------------- 1 | //! Utilities for using Tower services with Tonic. 2 | 3 | pub mod interceptor; 4 | pub(crate) mod layered; 5 | #[cfg(feature = "router")] 6 | pub(crate) mod router; 7 | 8 | #[doc(inline)] 9 | pub use self::interceptor::{Interceptor, InterceptorLayer}; 10 | pub use self::layered::{LayerExt, Layered}; 11 | #[doc(inline)] 12 | #[cfg(feature = "router")] 13 | pub use self::router::{Routes, RoutesBuilder}; 14 | #[cfg(feature = "router")] 15 | pub use axum::{body::Body as AxumBody, Router as AxumRouter}; 16 | 17 | pub mod recover_error; 18 | pub use self::recover_error::{RecoverError, RecoverErrorLayer}; 19 | -------------------------------------------------------------------------------- /tonic/src/transport/channel/service/add_origin.rs: -------------------------------------------------------------------------------- 1 | use crate::transport::channel::BoxFuture; 2 | use http::uri::Authority; 3 | use http::uri::Scheme; 4 | use http::{Request, Uri}; 5 | use std::task::{Context, Poll}; 6 | use tower_service::Service; 7 | 8 | #[derive(Debug)] 9 | pub(crate) struct AddOrigin { 10 | inner: T, 11 | scheme: Option, 12 | authority: Option, 13 | } 14 | 15 | impl AddOrigin { 16 | pub(crate) fn new(inner: T, origin: Uri) -> Self { 17 | let http::uri::Parts { 18 | scheme, authority, .. 19 | } = origin.into_parts(); 20 | 21 | Self { 22 | inner, 23 | scheme, 24 | authority, 25 | } 26 | } 27 | } 28 | 29 | impl Service> for AddOrigin 30 | where 31 | T: Service>, 32 | T::Future: Send + 'static, 33 | T::Error: Into, 34 | { 35 | type Response = T::Response; 36 | type Error = crate::BoxError; 37 | type Future = BoxFuture<'static, Result>; 38 | 39 | fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { 40 | self.inner.poll_ready(cx).map_err(Into::into) 41 | } 42 | 43 | fn call(&mut self, req: Request) -> Self::Future { 44 | if self.scheme.is_none() || self.authority.is_none() { 45 | let err = crate::transport::Error::new_invalid_uri(); 46 | return Box::pin(async move { Err::(err.into()) }); 47 | } 48 | 49 | // Split the request into the head and the body. 50 | let (mut head, body) = req.into_parts(); 51 | 52 | // Update the request URI 53 | head.uri = { 54 | // Split the request URI into parts. 55 | let mut uri: http::uri::Parts = head.uri.into(); 56 | // Update the URI parts, setting the scheme and authority 57 | uri.scheme = self.scheme.clone(); 58 | uri.authority = self.authority.clone(); 59 | 60 | http::Uri::from_parts(uri).expect("valid uri") 61 | }; 62 | 63 | let request = Request::from_parts(head, body); 64 | 65 | let fut = self.inner.call(request); 66 | 67 | Box::pin(async move { fut.await.map_err(Into::into) }) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tonic/src/transport/channel/service/discover.rs: -------------------------------------------------------------------------------- 1 | use super::super::{Connection, Endpoint}; 2 | 3 | use std::{ 4 | hash::Hash, 5 | pin::Pin, 6 | task::{Context, Poll}, 7 | }; 8 | use tokio::sync::mpsc::Receiver; 9 | use tokio_stream::Stream; 10 | use tower::discover::Change as TowerChange; 11 | 12 | /// A change in the service set. 13 | #[derive(Debug, Clone)] 14 | pub enum Change { 15 | /// A new service identified by key `K` was identified. 16 | Insert(K, V), 17 | /// The service identified by key `K` disappeared. 18 | Remove(K), 19 | } 20 | 21 | pub(crate) struct DynamicServiceStream { 22 | changes: Receiver>, 23 | } 24 | 25 | impl DynamicServiceStream { 26 | pub(crate) fn new(changes: Receiver>) -> Self { 27 | Self { changes } 28 | } 29 | } 30 | 31 | impl Stream for DynamicServiceStream { 32 | type Item = Result, crate::BoxError>; 33 | 34 | fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 35 | match Pin::new(&mut self.changes).poll_recv(cx) { 36 | Poll::Pending | Poll::Ready(None) => Poll::Pending, 37 | Poll::Ready(Some(change)) => match change { 38 | Change::Insert(k, endpoint) => { 39 | let connection = Connection::lazy(endpoint.http_connector(), endpoint); 40 | Poll::Ready(Some(Ok(TowerChange::Insert(k, connection)))) 41 | } 42 | Change::Remove(k) => Poll::Ready(Some(Ok(TowerChange::Remove(k)))), 43 | }, 44 | } 45 | } 46 | } 47 | 48 | impl Unpin for DynamicServiceStream {} 49 | -------------------------------------------------------------------------------- /tonic/src/transport/channel/service/executor.rs: -------------------------------------------------------------------------------- 1 | use crate::transport::channel::BoxFuture; 2 | use hyper_util::rt::TokioExecutor; 3 | use std::{future::Future, sync::Arc}; 4 | 5 | pub(crate) use hyper::rt::Executor; 6 | 7 | #[derive(Clone)] 8 | pub(crate) struct SharedExec { 9 | inner: Arc> + Send + Sync + 'static>, 10 | } 11 | 12 | impl SharedExec { 13 | pub(crate) fn new(exec: E) -> Self 14 | where 15 | E: Executor> + Send + Sync + 'static, 16 | { 17 | Self { 18 | inner: Arc::new(exec), 19 | } 20 | } 21 | 22 | pub(crate) fn tokio() -> Self { 23 | Self::new(TokioExecutor::new()) 24 | } 25 | } 26 | 27 | impl Executor for SharedExec 28 | where 29 | F: Future + Send + 'static, 30 | { 31 | fn execute(&self, fut: F) { 32 | self.inner.execute(Box::pin(fut)) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tonic/src/transport/channel/service/io.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, IoSlice}; 2 | use std::pin::Pin; 3 | use std::task::{Context, Poll}; 4 | 5 | use hyper::rt; 6 | use hyper_util::client::legacy::connect::{Connected as HyperConnected, Connection}; 7 | 8 | pub(in crate::transport) trait Io: 9 | rt::Read + rt::Write + Send + 'static 10 | { 11 | } 12 | 13 | impl Io for T where T: rt::Read + rt::Write + Send + 'static {} 14 | 15 | pub(crate) struct BoxedIo(Pin>); 16 | 17 | impl BoxedIo { 18 | pub(in crate::transport) fn new(io: I) -> Self { 19 | BoxedIo(Box::pin(io)) 20 | } 21 | } 22 | 23 | impl Connection for BoxedIo { 24 | fn connected(&self) -> HyperConnected { 25 | HyperConnected::new() 26 | } 27 | } 28 | 29 | impl rt::Read for BoxedIo { 30 | fn poll_read( 31 | mut self: Pin<&mut Self>, 32 | cx: &mut Context<'_>, 33 | buf: rt::ReadBufCursor<'_>, 34 | ) -> Poll> { 35 | Pin::new(&mut self.0).poll_read(cx, buf) 36 | } 37 | } 38 | 39 | impl rt::Write for BoxedIo { 40 | fn poll_write( 41 | mut self: Pin<&mut Self>, 42 | cx: &mut Context<'_>, 43 | buf: &[u8], 44 | ) -> Poll> { 45 | Pin::new(&mut self.0).poll_write(cx, buf) 46 | } 47 | 48 | fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 49 | Pin::new(&mut self.0).poll_flush(cx) 50 | } 51 | 52 | fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 53 | Pin::new(&mut self.0).poll_shutdown(cx) 54 | } 55 | 56 | fn poll_write_vectored( 57 | mut self: Pin<&mut Self>, 58 | cx: &mut Context<'_>, 59 | bufs: &[IoSlice<'_>], 60 | ) -> Poll> { 61 | Pin::new(&mut self.0).poll_write_vectored(cx, bufs) 62 | } 63 | 64 | fn is_write_vectored(&self) -> bool { 65 | self.0.is_write_vectored() 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tonic/src/transport/channel/service/mod.rs: -------------------------------------------------------------------------------- 1 | mod add_origin; 2 | use self::add_origin::AddOrigin; 3 | 4 | mod user_agent; 5 | use self::user_agent::UserAgent; 6 | 7 | mod reconnect; 8 | use self::reconnect::Reconnect; 9 | 10 | mod connection; 11 | pub(super) use self::connection::Connection; 12 | 13 | mod discover; 14 | pub use self::discover::Change; 15 | pub(super) use self::discover::DynamicServiceStream; 16 | 17 | mod io; 18 | use self::io::BoxedIo; 19 | 20 | mod connector; 21 | pub(crate) use self::connector::Connector; 22 | 23 | mod executor; 24 | pub(super) use self::executor::{Executor, SharedExec}; 25 | 26 | #[cfg(feature = "_tls-any")] 27 | mod tls; 28 | #[cfg(feature = "_tls-any")] 29 | pub(super) use self::tls::TlsConnector; 30 | -------------------------------------------------------------------------------- /tonic/src/transport/channel/uds_connector.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::pin::Pin; 3 | use std::task::{Context, Poll}; 4 | 5 | use http::Uri; 6 | use hyper_util::rt::TokioIo; 7 | 8 | use tower::Service; 9 | 10 | use crate::status::ConnectError; 11 | 12 | #[cfg(not(target_os = "windows"))] 13 | use tokio::net::UnixStream; 14 | 15 | #[cfg(not(target_os = "windows"))] 16 | async fn connect_uds(uds_path: String) -> Result { 17 | UnixStream::connect(uds_path) 18 | .await 19 | .map_err(|err| ConnectError(From::from(err))) 20 | } 21 | 22 | // Dummy type that will allow us to compile and match trait bounds 23 | // but is never used. 24 | #[cfg(target_os = "windows")] 25 | #[allow(dead_code)] 26 | type UnixStream = tokio::io::DuplexStream; 27 | 28 | #[cfg(target_os = "windows")] 29 | async fn connect_uds(_uds_path: String) -> Result { 30 | Err(ConnectError( 31 | "uds connections are not allowed on windows".into(), 32 | )) 33 | } 34 | 35 | pub(crate) struct UdsConnector { 36 | uds_filepath: String, 37 | } 38 | 39 | impl UdsConnector { 40 | pub(crate) fn new(uds_filepath: &str) -> Self { 41 | UdsConnector { 42 | uds_filepath: uds_filepath.to_string(), 43 | } 44 | } 45 | } 46 | 47 | impl Service for UdsConnector { 48 | type Response = TokioIo; 49 | type Error = ConnectError; 50 | type Future = UdsConnecting; 51 | 52 | fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { 53 | Poll::Ready(Ok(())) 54 | } 55 | 56 | fn call(&mut self, _: Uri) -> Self::Future { 57 | let uds_path = self.uds_filepath.clone(); 58 | let fut = async move { 59 | let stream = connect_uds(uds_path).await?; 60 | Ok(TokioIo::new(stream)) 61 | }; 62 | UdsConnecting { 63 | inner: Box::pin(fut), 64 | } 65 | } 66 | } 67 | 68 | type ConnectResult = Result, ConnectError>; 69 | 70 | pub(crate) struct UdsConnecting { 71 | inner: Pin + Send>>, 72 | } 73 | 74 | impl Future for UdsConnecting { 75 | type Output = ConnectResult; 76 | 77 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 78 | self.get_mut().inner.as_mut().poll(cx) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /tonic/src/transport/server/service/mod.rs: -------------------------------------------------------------------------------- 1 | mod io; 2 | pub(crate) use self::io::{ConnectInfoLayer, ServerIo}; 3 | 4 | #[cfg(feature = "_tls-any")] 5 | mod tls; 6 | #[cfg(feature = "_tls-any")] 7 | pub(crate) use self::tls::TlsAcceptor; 8 | -------------------------------------------------------------------------------- /tonic/src/transport/server/unix.rs: -------------------------------------------------------------------------------- 1 | use super::Connected; 2 | use std::sync::Arc; 3 | 4 | /// Connection info for Unix domain socket streams. 5 | /// 6 | /// This type will be accessible through [request extensions][ext] if you're using 7 | /// a unix stream. 8 | /// 9 | /// See [Connected] for more details. 10 | /// 11 | /// [ext]: crate::Request::extensions 12 | #[derive(Clone, Debug)] 13 | pub struct UdsConnectInfo { 14 | /// Peer address. This will be "unnamed" for client unix sockets. 15 | pub peer_addr: Option>, 16 | /// Process credentials for the unix socket. 17 | pub peer_cred: Option, 18 | } 19 | 20 | impl Connected for tokio::net::UnixStream { 21 | type ConnectInfo = UdsConnectInfo; 22 | 23 | fn connect_info(&self) -> Self::ConnectInfo { 24 | UdsConnectInfo { 25 | peer_addr: self.peer_addr().ok().map(Arc::new), 26 | peer_cred: self.peer_cred().ok(), 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tonic/src/transport/service/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod grpc_timeout; 2 | #[cfg(feature = "_tls-any")] 3 | pub(crate) mod tls; 4 | 5 | pub(crate) use self::grpc_timeout::GrpcTimeout; 6 | -------------------------------------------------------------------------------- /tonic/src/transport/service/tls.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt, io::Cursor}; 2 | 3 | use tokio_rustls::rustls::pki_types::{pem::PemObject as _, CertificateDer, PrivateKeyDer}; 4 | 5 | use crate::transport::{Certificate, Identity}; 6 | 7 | /// h2 alpn in plain format for rustls. 8 | pub(crate) const ALPN_H2: &[u8] = b"h2"; 9 | 10 | #[derive(Debug)] 11 | pub(crate) enum TlsError { 12 | #[cfg(feature = "channel")] 13 | H2NotNegotiated, 14 | #[cfg(feature = "tls-native-roots")] 15 | NativeCertsNotFound, 16 | CertificateParseError, 17 | PrivateKeyParseError, 18 | } 19 | 20 | impl fmt::Display for TlsError { 21 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 22 | match self { 23 | #[cfg(feature = "channel")] 24 | TlsError::H2NotNegotiated => write!(f, "HTTP/2 was not negotiated."), 25 | #[cfg(feature = "tls-native-roots")] 26 | TlsError::NativeCertsNotFound => write!(f, "no native certs found"), 27 | TlsError::CertificateParseError => write!(f, "Error parsing TLS certificate."), 28 | TlsError::PrivateKeyParseError => write!( 29 | f, 30 | "Error parsing TLS private key - no RSA or PKCS8-encoded keys found." 31 | ), 32 | } 33 | } 34 | } 35 | 36 | impl std::error::Error for TlsError {} 37 | 38 | pub(crate) fn convert_certificate_to_pki_types( 39 | certificate: &Certificate, 40 | ) -> Result>, TlsError> { 41 | CertificateDer::pem_reader_iter(&mut Cursor::new(certificate)) 42 | .collect::, _>>() 43 | .map_err(|_| TlsError::CertificateParseError) 44 | } 45 | 46 | pub(crate) fn convert_identity_to_pki_types( 47 | identity: &Identity, 48 | ) -> Result<(Vec>, PrivateKeyDer<'static>), TlsError> { 49 | let cert = convert_certificate_to_pki_types(&identity.cert)?; 50 | let key = PrivateKeyDer::from_pem_reader(&mut Cursor::new(&identity.key)) 51 | .map_err(|_| TlsError::PrivateKeyParseError)?; 52 | Ok((cert, key)) 53 | } 54 | -------------------------------------------------------------------------------- /tonic/src/transport/tls.rs: -------------------------------------------------------------------------------- 1 | /// Represents a X509 certificate. 2 | #[derive(Debug, Clone)] 3 | pub struct Certificate { 4 | pub(crate) pem: Vec, 5 | } 6 | 7 | /// Represents a private key and X509 certificate. 8 | #[derive(Debug, Clone)] 9 | pub struct Identity { 10 | pub(crate) cert: Certificate, 11 | pub(crate) key: Vec, 12 | } 13 | 14 | impl Certificate { 15 | /// Parse a PEM encoded X509 Certificate. 16 | /// 17 | /// The provided PEM should include at least one PEM encoded certificate. 18 | pub fn from_pem(pem: impl AsRef<[u8]>) -> Self { 19 | let pem = pem.as_ref().into(); 20 | Self { pem } 21 | } 22 | 23 | /// Get a immutable reference to underlying certificate 24 | pub fn get_ref(&self) -> &[u8] { 25 | self.pem.as_slice() 26 | } 27 | 28 | /// Get a mutable reference to underlying certificate 29 | pub fn get_mut(&mut self) -> &mut [u8] { 30 | self.pem.as_mut() 31 | } 32 | 33 | /// Consumes `self`, returning the underlying certificate 34 | pub fn into_inner(self) -> Vec { 35 | self.pem 36 | } 37 | } 38 | 39 | impl AsRef<[u8]> for Certificate { 40 | fn as_ref(&self) -> &[u8] { 41 | self.pem.as_ref() 42 | } 43 | } 44 | 45 | impl AsMut<[u8]> for Certificate { 46 | fn as_mut(&mut self) -> &mut [u8] { 47 | self.pem.as_mut() 48 | } 49 | } 50 | 51 | impl Identity { 52 | /// Parse a PEM encoded certificate and private key. 53 | /// 54 | /// The provided cert must contain at least one PEM encoded certificate. 55 | pub fn from_pem(cert: impl AsRef<[u8]>, key: impl AsRef<[u8]>) -> Self { 56 | let cert = Certificate::from_pem(cert); 57 | let key = key.as_ref().into(); 58 | Self { cert, key } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tonic/src/util.rs: -------------------------------------------------------------------------------- 1 | //! Various utilities used throughout tonic. 2 | 3 | // some combinations of features might cause things here not to be used 4 | #![allow(dead_code)] 5 | 6 | pub(crate) mod base64 { 7 | use base64::{ 8 | alphabet, 9 | engine::{ 10 | general_purpose::{GeneralPurpose, GeneralPurposeConfig}, 11 | DecodePaddingMode, 12 | }, 13 | }; 14 | 15 | pub(crate) const STANDARD: GeneralPurpose = GeneralPurpose::new( 16 | &alphabet::STANDARD, 17 | GeneralPurposeConfig::new() 18 | .with_encode_padding(true) 19 | .with_decode_padding_mode(DecodePaddingMode::Indifferent), 20 | ); 21 | 22 | pub(crate) const STANDARD_NO_PAD: GeneralPurpose = GeneralPurpose::new( 23 | &alphabet::STANDARD, 24 | GeneralPurposeConfig::new() 25 | .with_encode_padding(false) 26 | .with_decode_padding_mode(DecodePaddingMode::Indifferent), 27 | ); 28 | } 29 | --------------------------------------------------------------------------------