├── .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 |
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