chore: Reorganize examples and interop crates (#180)
* chore: Reorganize examples and interop crates * fix interop tests
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
pub mod pb {
|
||||
tonic::include_proto!("grpc.examples.echo");
|
||||
}
|
||||
|
||||
use http::header::HeaderValue;
|
||||
use pb::{echo_client::EchoClient, EchoRequest};
|
||||
use tonic::transport::Channel;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let channel = Channel::from_static("http://[::1]:50051")
|
||||
.intercept_headers(|headers| {
|
||||
headers.insert(
|
||||
"authorization",
|
||||
HeaderValue::from_static("Bearer some-secret-token"),
|
||||
);
|
||||
})
|
||||
.connect()
|
||||
.await?;
|
||||
|
||||
let mut client = EchoClient::new(channel);
|
||||
|
||||
let request = tonic::Request::new(EchoRequest {
|
||||
message: "hello".into(),
|
||||
});
|
||||
|
||||
let response = client.unary_echo(request).await?;
|
||||
|
||||
println!("RESPONSE={:?}", response);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
pub mod pb {
|
||||
tonic::include_proto!("grpc.examples.echo");
|
||||
}
|
||||
|
||||
use futures::Stream;
|
||||
use pb::{EchoRequest, EchoResponse};
|
||||
use std::pin::Pin;
|
||||
use tonic::{body::BoxBody, transport::Server, Request, Response, Status, Streaming};
|
||||
use tower::Service;
|
||||
|
||||
type EchoResult<T> = Result<Response<T>, Status>;
|
||||
type ResponseStream = Pin<Box<dyn Stream<Item = Result<EchoResponse, Status>> + Send + Sync>>;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct EchoServer;
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl pb::echo_server::Echo for EchoServer {
|
||||
async fn unary_echo(&self, request: Request<EchoRequest>) -> EchoResult<EchoResponse> {
|
||||
let message = request.into_inner().message;
|
||||
Ok(Response::new(EchoResponse { message }))
|
||||
}
|
||||
|
||||
type ServerStreamingEchoStream = ResponseStream;
|
||||
|
||||
async fn server_streaming_echo(
|
||||
&self,
|
||||
_: Request<EchoRequest>,
|
||||
) -> EchoResult<Self::ServerStreamingEchoStream> {
|
||||
Err(Status::unimplemented("not implemented"))
|
||||
}
|
||||
|
||||
async fn client_streaming_echo(
|
||||
&self,
|
||||
_: Request<Streaming<EchoRequest>>,
|
||||
) -> EchoResult<EchoResponse> {
|
||||
Err(Status::unimplemented("not implemented"))
|
||||
}
|
||||
|
||||
type BidirectionalStreamingEchoStream = ResponseStream;
|
||||
|
||||
async fn bidirectional_streaming_echo(
|
||||
&self,
|
||||
_: Request<Streaming<EchoRequest>>,
|
||||
) -> EchoResult<Self::BidirectionalStreamingEchoStream> {
|
||||
Err(Status::unimplemented("not implemented"))
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let addr = "[::1]:50051".parse().unwrap();
|
||||
let server = EchoServer::default();
|
||||
|
||||
Server::builder()
|
||||
.interceptor_fn(move |svc, req| {
|
||||
let auth_header = req.headers().get("authorization").clone();
|
||||
|
||||
let authed = if let Some(auth_header) = auth_header {
|
||||
auth_header == "Bearer some-secret-token"
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let fut = svc.call(req);
|
||||
|
||||
async move {
|
||||
if authed {
|
||||
fut.await
|
||||
} else {
|
||||
// Cancel the inner future since we never await it
|
||||
// the IO never gets registered.
|
||||
drop(fut);
|
||||
let res = http::Response::builder()
|
||||
.header("grpc-status", "16")
|
||||
.body(BoxBody::empty())
|
||||
.unwrap();
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
})
|
||||
.add_service(pb::echo_server::EchoServer::new(server))
|
||||
.serve(addr)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
use tokio::runtime::{Builder, Runtime};
|
||||
|
||||
pub mod hello_world {
|
||||
tonic::include_proto!("helloworld");
|
||||
}
|
||||
|
||||
use hello_world::{greeter_client::GreeterClient, HelloReply, HelloRequest};
|
||||
|
||||
type StdError = Box<dyn std::error::Error + Send + Sync + 'static>;
|
||||
type Result<T, E = StdError> = ::std::result::Result<T, E>;
|
||||
|
||||
// The order of the fields in this struct is important. The runtime must be the first field and the
|
||||
// client must be the last field so that when `BlockingClient` is dropped the client is dropped
|
||||
// before the runtime. Not doing this will result in a deadlock when dropped.
|
||||
struct BlockingClient {
|
||||
rt: Runtime,
|
||||
client: GreeterClient<tonic::transport::Channel>,
|
||||
}
|
||||
|
||||
impl BlockingClient {
|
||||
pub fn connect<D>(dst: D) -> Result<Self, tonic::transport::Error>
|
||||
where
|
||||
D: std::convert::TryInto<tonic::transport::Endpoint>,
|
||||
D::Error: Into<StdError>,
|
||||
{
|
||||
let mut rt = Builder::new()
|
||||
.basic_scheduler()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
let client = rt.block_on(GreeterClient::connect(dst))?;
|
||||
|
||||
Ok(Self { rt, client })
|
||||
}
|
||||
|
||||
pub fn say_hello(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<HelloRequest>,
|
||||
) -> Result<tonic::Response<HelloReply>, tonic::Status> {
|
||||
self.rt.block_on(self.client.say_hello(request))
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let mut client = BlockingClient::connect("http://[::1]:50051")?;
|
||||
|
||||
let request = tonic::Request::new(HelloRequest {
|
||||
name: "Tonic".into(),
|
||||
});
|
||||
|
||||
let response = client.say_hello(request)?;
|
||||
|
||||
println!("RESPONSE={:?}", response);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
# Google Cloud Pubsub example
|
||||
|
||||
This example will attempt to fetch a list of topics using the google
|
||||
gRPC protobuf specification. This will use an OAuth token and TLS to
|
||||
fetch the list of topics.
|
||||
|
||||
First, you must generate a access token via the [OAuth playground]. From here
|
||||
select the `Cloud Pub/Sub API v1` and its urls as the scope. This will start
|
||||
the OAuth flow. Then you must hit the `Exchange authorization code for tokens`
|
||||
button to generate an `access_token` which will show up in the HTTP response
|
||||
to the right under the `access_token` field in the response json.
|
||||
|
||||
Once, you have this token you must fetch your GCP project id which can be found
|
||||
from the main page on the dashboard. When you have both of these items you can
|
||||
run the example like so:
|
||||
|
||||
```shell
|
||||
GCP_AUTH_TOKEN="<access-token>" cargo run --bin gcp-client -- <project-id>
|
||||
```
|
||||
|
||||
[OAuth playground]: https://developers.google.com/oauthplayground/
|
||||
@@ -0,0 +1,55 @@
|
||||
pub mod api {
|
||||
tonic::include_proto!("google.pubsub.v1");
|
||||
}
|
||||
|
||||
use api::{publisher_client::PublisherClient, ListTopicsRequest};
|
||||
use http::header::HeaderValue;
|
||||
use tonic::{
|
||||
transport::{Certificate, Channel, ClientTlsConfig},
|
||||
Request,
|
||||
};
|
||||
|
||||
const ENDPOINT: &str = "https://pubsub.googleapis.com";
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let token = std::env::var("GCP_AUTH_TOKEN").map_err(|_| {
|
||||
"Pass a valid 0Auth bearer token via `GCP_AUTH_TOKEN` environment variable.".to_string()
|
||||
})?;
|
||||
|
||||
let project = std::env::args()
|
||||
.skip(1)
|
||||
.next()
|
||||
.ok_or("Expected a project name as the first argument.".to_string())?;
|
||||
|
||||
let bearer_token = format!("Bearer {}", token);
|
||||
let header_value = HeaderValue::from_str(&bearer_token)?;
|
||||
|
||||
let certs = tokio::fs::read("tonic-examples/data/gcp/roots.pem").await?;
|
||||
|
||||
let tls_config = ClientTlsConfig::with_rustls()
|
||||
.ca_certificate(Certificate::from_pem(certs.as_slice()))
|
||||
.domain_name("pubsub.googleapis.com");
|
||||
|
||||
let channel = Channel::from_static(ENDPOINT)
|
||||
.intercept_headers(move |headers| {
|
||||
headers.insert("authorization", header_value.clone());
|
||||
})
|
||||
.tls_config(tls_config)
|
||||
.connect()
|
||||
.await?;
|
||||
|
||||
let mut service = PublisherClient::new(channel);
|
||||
|
||||
let response = service
|
||||
.list_topics(Request::new(ListTopicsRequest {
|
||||
project: format!("projects/{0}", project),
|
||||
page_size: 10,
|
||||
..Default::default()
|
||||
}))
|
||||
.await?;
|
||||
|
||||
println!("RESPONSE={:?}", response);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
pub mod hello_world {
|
||||
tonic::include_proto!("helloworld");
|
||||
}
|
||||
|
||||
use hello_world::{greeter_client::GreeterClient, HelloRequest};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut client = GreeterClient::connect("http://[::1]:50051").await?;
|
||||
|
||||
let request = tonic::Request::new(HelloRequest {
|
||||
name: "Tonic".into(),
|
||||
});
|
||||
|
||||
let response = client.say_hello(request).await?;
|
||||
|
||||
println!("RESPONSE={:?}", response);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
use tonic::{transport::Server, Request, Response, Status};
|
||||
|
||||
pub mod hello_world {
|
||||
tonic::include_proto!("helloworld");
|
||||
}
|
||||
|
||||
use hello_world::{
|
||||
greeter_server::{Greeter, GreeterServer},
|
||||
HelloReply, HelloRequest,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MyGreeter {}
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl Greeter for MyGreeter {
|
||||
async fn say_hello(
|
||||
&self,
|
||||
request: Request<HelloRequest>,
|
||||
) -> Result<Response<HelloReply>, Status> {
|
||||
println!("Got a request: {:?}", request);
|
||||
|
||||
let reply = hello_world::HelloReply {
|
||||
message: format!("Hello {}!", request.into_inner().name).into(),
|
||||
};
|
||||
Ok(Response::new(reply))
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let addr = "[::1]:50051".parse().unwrap();
|
||||
let greeter = MyGreeter::default();
|
||||
|
||||
Server::builder()
|
||||
.add_service(GreeterServer::new(greeter))
|
||||
.serve(addr)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
pub mod pb {
|
||||
tonic::include_proto!("grpc.examples.echo");
|
||||
}
|
||||
|
||||
use pb::{echo_client::EchoClient, EchoRequest};
|
||||
use tonic::transport::Channel;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let endpoints = ["http://[::1]:50051", "http://[::1]:50052"]
|
||||
.into_iter()
|
||||
.map(|a| Channel::from_static(a));
|
||||
|
||||
let channel = Channel::balance_list(endpoints);
|
||||
|
||||
let mut client = EchoClient::new(channel);
|
||||
|
||||
for _ in 0..12usize {
|
||||
let request = tonic::Request::new(EchoRequest {
|
||||
message: "hello".into(),
|
||||
});
|
||||
|
||||
let response = client.unary_echo(request).await?;
|
||||
|
||||
println!("RESPONSE={:?}", response);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
pub mod pb {
|
||||
tonic::include_proto!("grpc.examples.echo");
|
||||
}
|
||||
|
||||
use futures::Stream;
|
||||
use std::net::SocketAddr;
|
||||
use std::pin::Pin;
|
||||
use tokio::sync::mpsc;
|
||||
use tonic::{transport::Server, Request, Response, Status, Streaming};
|
||||
|
||||
use pb::{EchoRequest, EchoResponse};
|
||||
|
||||
type EchoResult<T> = Result<Response<T>, Status>;
|
||||
type ResponseStream = Pin<Box<dyn Stream<Item = Result<EchoResponse, Status>> + Send + Sync>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EchoServer {
|
||||
addr: SocketAddr,
|
||||
}
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl pb::echo_server::Echo for EchoServer {
|
||||
async fn unary_echo(&self, request: Request<EchoRequest>) -> EchoResult<EchoResponse> {
|
||||
let message = format!("{} (from {})", request.into_inner().message, self.addr);
|
||||
|
||||
Ok(Response::new(EchoResponse { message }))
|
||||
}
|
||||
|
||||
type ServerStreamingEchoStream = ResponseStream;
|
||||
|
||||
async fn server_streaming_echo(
|
||||
&self,
|
||||
_: Request<EchoRequest>,
|
||||
) -> EchoResult<Self::ServerStreamingEchoStream> {
|
||||
Err(Status::unimplemented("not implemented"))
|
||||
}
|
||||
|
||||
async fn client_streaming_echo(
|
||||
&self,
|
||||
_: Request<Streaming<EchoRequest>>,
|
||||
) -> EchoResult<EchoResponse> {
|
||||
Err(Status::unimplemented("not implemented"))
|
||||
}
|
||||
|
||||
type BidirectionalStreamingEchoStream = ResponseStream;
|
||||
|
||||
async fn bidirectional_streaming_echo(
|
||||
&self,
|
||||
_: Request<Streaming<EchoRequest>>,
|
||||
) -> EchoResult<Self::BidirectionalStreamingEchoStream> {
|
||||
Err(Status::unimplemented("not implemented"))
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let addrs = ["[::1]:50051", "[::1]:50052"];
|
||||
|
||||
let (tx, mut rx) = mpsc::unbounded_channel();
|
||||
|
||||
for addr in &addrs {
|
||||
let addr = addr.parse()?;
|
||||
let tx = tx.clone();
|
||||
|
||||
let server = EchoServer { addr };
|
||||
let serve = Server::builder()
|
||||
.add_service(pb::echo_server::EchoServer::new(server))
|
||||
.serve(addr);
|
||||
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = serve.await {
|
||||
eprintln!("Error = {:?}", e);
|
||||
}
|
||||
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
rx.recv().await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
pub mod hello_world {
|
||||
tonic::include_proto!("helloworld");
|
||||
}
|
||||
|
||||
pub mod echo {
|
||||
tonic::include_proto!("grpc.examples.echo");
|
||||
}
|
||||
|
||||
use echo::{echo_client::EchoClient, EchoRequest};
|
||||
use hello_world::{greeter_client::GreeterClient, HelloRequest};
|
||||
use tonic::transport::Endpoint;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let channel = Endpoint::from_static("http://[::1]:50051")
|
||||
.connect()
|
||||
.await?;
|
||||
|
||||
let mut greeter_client = GreeterClient::new(channel.clone());
|
||||
let mut echo_client = EchoClient::new(channel);
|
||||
|
||||
let request = tonic::Request::new(HelloRequest {
|
||||
name: "Tonic".into(),
|
||||
});
|
||||
|
||||
let response = greeter_client.say_hello(request).await?;
|
||||
|
||||
println!("GREETER RESPONSE={:?}", response);
|
||||
|
||||
let request = tonic::Request::new(EchoRequest {
|
||||
message: "hello".into(),
|
||||
});
|
||||
|
||||
let response = echo_client.unary_echo(request).await?;
|
||||
|
||||
println!("ECHO RESPONSE={:?}", response);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
use futures::Stream;
|
||||
use std::pin::Pin;
|
||||
use tonic::{transport::Server, Request, Response, Status};
|
||||
|
||||
pub mod hello_world {
|
||||
tonic::include_proto!("helloworld");
|
||||
}
|
||||
|
||||
pub mod echo {
|
||||
tonic::include_proto!("grpc.examples.echo");
|
||||
}
|
||||
|
||||
use hello_world::{
|
||||
greeter_server::{Greeter, GreeterServer},
|
||||
HelloReply, HelloRequest,
|
||||
};
|
||||
|
||||
use echo::{
|
||||
echo_server::{Echo, EchoServer},
|
||||
EchoRequest, EchoResponse,
|
||||
};
|
||||
|
||||
type ResponseStream = Pin<Box<dyn Stream<Item = Result<EchoResponse, Status>> + Send + Sync>>;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let addr = "[::1]:50051".parse().unwrap();
|
||||
|
||||
let greeter = GreeterServer::new(MyGreeter::default());
|
||||
let echo = EchoServer::new(MyEcho::default());
|
||||
|
||||
Server::builder()
|
||||
.add_service(greeter)
|
||||
.add_service(echo)
|
||||
.serve(addr)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MyGreeter {}
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl Greeter for MyGreeter {
|
||||
async fn say_hello(
|
||||
&self,
|
||||
request: Request<HelloRequest>,
|
||||
) -> Result<Response<HelloReply>, Status> {
|
||||
let reply = hello_world::HelloReply {
|
||||
message: format!("Hello {}!", request.into_inner().name).into(),
|
||||
};
|
||||
Ok(Response::new(reply))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MyEcho;
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl Echo for MyEcho {
|
||||
async fn unary_echo(
|
||||
&self,
|
||||
request: Request<EchoRequest>,
|
||||
) -> Result<Response<EchoResponse>, Status> {
|
||||
let message = request.into_inner().message;
|
||||
Ok(Response::new(EchoResponse { message }))
|
||||
}
|
||||
|
||||
type ServerStreamingEchoStream = ResponseStream;
|
||||
type BidirectionalStreamingEchoStream = ResponseStream;
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
use futures::stream;
|
||||
use rand::rngs::ThreadRng;
|
||||
use rand::Rng;
|
||||
use route_guide::{Point, Rectangle, RouteNote};
|
||||
use std::error::Error;
|
||||
use std::time::Duration;
|
||||
use tokio::time::Instant;
|
||||
use tonic::transport::Channel;
|
||||
use tonic::Request;
|
||||
|
||||
pub mod route_guide {
|
||||
tonic::include_proto!("routeguide");
|
||||
}
|
||||
|
||||
use route_guide::routeguide_client::RouteGuideClient;
|
||||
|
||||
async fn print_features(client: &mut RouteGuideClient<Channel>) -> Result<(), Box<dyn Error>> {
|
||||
let rectangle = Rectangle {
|
||||
lo: Some(Point {
|
||||
latitude: 400000000,
|
||||
longitude: -750000000,
|
||||
}),
|
||||
hi: Some(Point {
|
||||
latitude: 420000000,
|
||||
longitude: -730000000,
|
||||
}),
|
||||
};
|
||||
|
||||
let mut stream = client
|
||||
.list_features(Request::new(rectangle))
|
||||
.await?
|
||||
.into_inner();
|
||||
|
||||
while let Some(feature) = stream.message().await? {
|
||||
println!("NOTE = {:?}", feature);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn run_record_route(client: &mut RouteGuideClient<Channel>) -> Result<(), Box<dyn Error>> {
|
||||
let mut rng = rand::thread_rng();
|
||||
let point_count: i32 = rng.gen_range(2, 100);
|
||||
|
||||
let mut points = vec![];
|
||||
for _ in 0..=point_count {
|
||||
points.push(random_point(&mut rng))
|
||||
}
|
||||
|
||||
println!("Traversing {} points", points.len());
|
||||
let request = Request::new(stream::iter(points));
|
||||
|
||||
match client.record_route(request).await {
|
||||
Ok(response) => println!("SUMMARY: {:?}", response.into_inner()),
|
||||
Err(e) => println!("something went wrong: {:?}", e),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn run_route_chat(client: &mut RouteGuideClient<Channel>) -> Result<(), Box<dyn Error>> {
|
||||
let start = Instant::now();
|
||||
|
||||
let outbound = async_stream::stream! {
|
||||
let mut interval = tokio::time::interval(Duration::from_secs(1));
|
||||
|
||||
while let time = interval.tick().await {
|
||||
let elapsed = time.duration_since(start);
|
||||
let note = RouteNote {
|
||||
location: Some(Point {
|
||||
latitude: 409146138 + elapsed.as_secs() as i32,
|
||||
longitude: -746188906,
|
||||
}),
|
||||
message: format!("at {:?}", elapsed),
|
||||
};
|
||||
|
||||
yield note;
|
||||
}
|
||||
};
|
||||
|
||||
let request = Request::new(outbound);
|
||||
let response = client.route_chat(request).await?;
|
||||
let mut inbound = response.into_inner();
|
||||
|
||||
while let Some(note) = inbound.message().await? {
|
||||
println!("NOTE = {:?}", note);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut client = RouteGuideClient::connect("http://[::1]:10000").await?;
|
||||
|
||||
println!("*** SIMPLE RPC ***");
|
||||
let response = client
|
||||
.get_feature(Request::new(Point {
|
||||
latitude: 409146138,
|
||||
longitude: -746188906,
|
||||
}))
|
||||
.await?;
|
||||
println!("RESPONSE = {:?}", response);
|
||||
|
||||
println!("\n*** SERVER STREAMING ***");
|
||||
print_features(&mut client).await?;
|
||||
|
||||
println!("\n*** CLIENT STREAMING ***");
|
||||
run_record_route(&mut client).await?;
|
||||
|
||||
println!("\n*** BIDIRECTIONAL STREAMING ***");
|
||||
run_route_chat(&mut client).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn random_point(rng: &mut ThreadRng) -> Point {
|
||||
let latitude = (rng.gen_range(0, 180) - 90) * 10_000_000;
|
||||
let longitude = (rng.gen_range(0, 360) - 180) * 10_000_000;
|
||||
Point {
|
||||
latitude,
|
||||
longitude,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
use serde::Deserialize;
|
||||
use std::fs::File;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Feature {
|
||||
location: Location,
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Location {
|
||||
latitude: i32,
|
||||
longitude: i32,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn load() -> Vec<crate::routeguide::Feature> {
|
||||
let file =
|
||||
File::open("tonic-examples/data/route_guide_db.json").expect("failed to open data file");
|
||||
|
||||
let decoded: Vec<Feature> =
|
||||
serde_json::from_reader(&file).expect("failed to deserialize features");
|
||||
|
||||
decoded
|
||||
.into_iter()
|
||||
.map(|feature| crate::routeguide::Feature {
|
||||
name: feature.name,
|
||||
location: Some(crate::routeguide::Point {
|
||||
longitude: feature.location.longitude,
|
||||
latitude: feature.location.latitude,
|
||||
}),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
mod data;
|
||||
|
||||
use futures::{Stream, StreamExt};
|
||||
use std::collections::HashMap;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
use tokio::sync::mpsc;
|
||||
use tonic::transport::Server;
|
||||
use tonic::{Request, Response, Status};
|
||||
|
||||
pub mod routeguide {
|
||||
tonic::include_proto!("routeguide");
|
||||
}
|
||||
|
||||
use routeguide::{routeguide_server, Feature, Point, Rectangle, RouteNote, RouteSummary};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RouteGuide {
|
||||
features: Arc<Vec<Feature>>,
|
||||
}
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl routeguide_server::RouteGuide for RouteGuide {
|
||||
async fn get_feature(&self, request: Request<Point>) -> Result<Response<Feature>, Status> {
|
||||
println!("GetFeature = {:?}", request);
|
||||
|
||||
for feature in &self.features[..] {
|
||||
if feature.location.as_ref() == Some(request.get_ref()) {
|
||||
return Ok(Response::new(feature.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
let response = Response::new(Feature {
|
||||
name: "".to_string(),
|
||||
location: None,
|
||||
});
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
type ListFeaturesStream = mpsc::Receiver<Result<Feature, Status>>;
|
||||
|
||||
async fn list_features(
|
||||
&self,
|
||||
request: Request<Rectangle>,
|
||||
) -> Result<Response<Self::ListFeaturesStream>, Status> {
|
||||
println!("ListFeatures = {:?}", request);
|
||||
|
||||
let (mut tx, rx) = mpsc::channel(4);
|
||||
let features = self.features.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
for feature in &features[..] {
|
||||
if in_range(feature.location.as_ref().unwrap(), request.get_ref()) {
|
||||
println!(" => send {:?}", feature);
|
||||
tx.send(Ok(feature.clone())).await.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
println!(" /// done sending");
|
||||
});
|
||||
|
||||
Ok(Response::new(rx))
|
||||
}
|
||||
|
||||
async fn record_route(
|
||||
&self,
|
||||
request: Request<tonic::Streaming<Point>>,
|
||||
) -> Result<Response<RouteSummary>, Status> {
|
||||
println!("RecordRoute");
|
||||
|
||||
let stream = request.into_inner();
|
||||
|
||||
// Pin the inbound stream to the stack so that we can call next on it
|
||||
futures::pin_mut!(stream);
|
||||
|
||||
let mut summary = RouteSummary::default();
|
||||
let mut last_point = None;
|
||||
let now = Instant::now();
|
||||
|
||||
while let Some(point) = stream.next().await {
|
||||
let point = point?;
|
||||
|
||||
println!(" ==> Point = {:?}", point);
|
||||
|
||||
// Increment the point count
|
||||
summary.point_count += 1;
|
||||
|
||||
// Find features
|
||||
for feature in &self.features[..] {
|
||||
if feature.location.as_ref() == Some(&point) {
|
||||
summary.feature_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the distance
|
||||
if let Some(ref last_point) = last_point {
|
||||
summary.distance += calc_distance(last_point, &point);
|
||||
}
|
||||
|
||||
last_point = Some(point);
|
||||
}
|
||||
|
||||
summary.elapsed_time = now.elapsed().as_secs() as i32;
|
||||
|
||||
Ok(Response::new(summary))
|
||||
}
|
||||
|
||||
type RouteChatStream =
|
||||
Pin<Box<dyn Stream<Item = Result<RouteNote, Status>> + Send + Sync + 'static>>;
|
||||
|
||||
async fn route_chat(
|
||||
&self,
|
||||
request: Request<tonic::Streaming<RouteNote>>,
|
||||
) -> Result<Response<Self::RouteChatStream>, Status> {
|
||||
println!("RouteChat");
|
||||
|
||||
let mut notes = HashMap::new();
|
||||
let stream = request.into_inner();
|
||||
|
||||
let output = async_stream::try_stream! {
|
||||
futures::pin_mut!(stream);
|
||||
|
||||
while let Some(note) = stream.next().await {
|
||||
let note = note?;
|
||||
|
||||
let location = note.location.clone().unwrap();
|
||||
|
||||
let location_notes = notes.entry(location).or_insert(vec![]);
|
||||
location_notes.push(note);
|
||||
|
||||
for note in location_notes {
|
||||
yield note.clone();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Response::new(Box::pin(output)
|
||||
as Pin<
|
||||
Box<dyn Stream<Item = Result<RouteNote, Status>> + Send + Sync + 'static>,
|
||||
>))
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let addr = "[::1]:10000".parse().unwrap();
|
||||
|
||||
println!("Listening on: {}", addr);
|
||||
|
||||
let route_guide = RouteGuide {
|
||||
features: Arc::new(data::load()),
|
||||
};
|
||||
|
||||
let svc = routeguide_server::RouteGuideServer::new(route_guide);
|
||||
|
||||
Server::builder().add_service(svc).serve(addr).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Implement hash for Point
|
||||
impl Hash for Point {
|
||||
fn hash<H>(&self, state: &mut H)
|
||||
where
|
||||
H: Hasher,
|
||||
{
|
||||
self.latitude.hash(state);
|
||||
self.longitude.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Point {}
|
||||
|
||||
fn in_range(point: &Point, rect: &Rectangle) -> bool {
|
||||
use std::cmp;
|
||||
|
||||
let lo = rect.lo.as_ref().unwrap();
|
||||
let hi = rect.hi.as_ref().unwrap();
|
||||
|
||||
let left = cmp::min(lo.longitude, hi.longitude);
|
||||
let right = cmp::max(lo.longitude, hi.longitude);
|
||||
let top = cmp::max(lo.latitude, hi.latitude);
|
||||
let bottom = cmp::min(lo.latitude, hi.latitude);
|
||||
|
||||
point.longitude >= left
|
||||
&& point.longitude <= right
|
||||
&& point.latitude >= bottom
|
||||
&& point.latitude <= top
|
||||
}
|
||||
|
||||
/// Calculates the distance between two points using the "haversine" formula.
|
||||
/// This code was taken from http://www.movable-type.co.uk/scripts/latlong.html.
|
||||
fn calc_distance(p1: &Point, p2: &Point) -> i32 {
|
||||
const CORD_FACTOR: f64 = 1e7;
|
||||
const R: f64 = 6371000.0; // meters
|
||||
|
||||
let lat1 = p1.latitude as f64 / CORD_FACTOR;
|
||||
let lat2 = p2.latitude as f64 / CORD_FACTOR;
|
||||
let lng1 = p1.longitude as f64 / CORD_FACTOR;
|
||||
let lng2 = p2.longitude as f64 / CORD_FACTOR;
|
||||
|
||||
let lat_rad1 = lat1.to_radians();
|
||||
let lat_rad2 = lat2.to_radians();
|
||||
|
||||
let delta_lat = (lat2 - lat1).to_radians();
|
||||
let delta_lng = (lng2 - lng1).to_radians();
|
||||
|
||||
let a = (delta_lat / 2f64).sin() * (delta_lat / 2f64).sin()
|
||||
+ (lat_rad1).cos() * (lat_rad2).cos() * (delta_lng / 2f64).sin() * (delta_lng / 2f64).sin();
|
||||
|
||||
let c = 2f64 * a.sqrt().atan2((1f64 - a).sqrt());
|
||||
|
||||
(R * c) as i32
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
pub mod pb {
|
||||
tonic::include_proto!("/grpc.examples.echo");
|
||||
}
|
||||
|
||||
use pb::{echo_client::EchoClient, EchoRequest};
|
||||
use tonic::transport::{Certificate, Channel, ClientTlsConfig};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let pem = tokio::fs::read("tonic-examples/data/tls/ca.pem").await?;
|
||||
let ca = Certificate::from_pem(pem);
|
||||
|
||||
let tls = ClientTlsConfig::with_rustls()
|
||||
.ca_certificate(ca)
|
||||
.domain_name("example.com");
|
||||
|
||||
let channel = Channel::from_static("http://[::1]:50051")
|
||||
.tls_config(tls)
|
||||
.connect()
|
||||
.await?;
|
||||
|
||||
let mut client = EchoClient::new(channel);
|
||||
let request = tonic::Request::new(EchoRequest {
|
||||
message: "hello".into(),
|
||||
});
|
||||
|
||||
let response = client.unary_echo(request).await?;
|
||||
|
||||
println!("RESPONSE={:?}", response);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
pub mod pb {
|
||||
tonic::include_proto!("/grpc.examples.echo");
|
||||
}
|
||||
|
||||
use futures::Stream;
|
||||
use pb::{EchoRequest, EchoResponse};
|
||||
use std::pin::Pin;
|
||||
use tonic::{
|
||||
transport::{Identity, Server, ServerTlsConfig},
|
||||
Request, Response, Status, Streaming,
|
||||
};
|
||||
|
||||
type EchoResult<T> = Result<Response<T>, Status>;
|
||||
type ResponseStream = Pin<Box<dyn Stream<Item = Result<EchoResponse, Status>> + Send + Sync>>;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct EchoServer;
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl pb::echo_server::Echo for EchoServer {
|
||||
async fn unary_echo(&self, request: Request<EchoRequest>) -> EchoResult<EchoResponse> {
|
||||
let message = request.into_inner().message;
|
||||
Ok(Response::new(EchoResponse { message }))
|
||||
}
|
||||
|
||||
type ServerStreamingEchoStream = ResponseStream;
|
||||
|
||||
async fn server_streaming_echo(
|
||||
&self,
|
||||
_: Request<EchoRequest>,
|
||||
) -> EchoResult<Self::ServerStreamingEchoStream> {
|
||||
Err(Status::unimplemented("not implemented"))
|
||||
}
|
||||
|
||||
async fn client_streaming_echo(
|
||||
&self,
|
||||
_: Request<Streaming<EchoRequest>>,
|
||||
) -> EchoResult<EchoResponse> {
|
||||
Err(Status::unimplemented("not implemented"))
|
||||
}
|
||||
|
||||
type BidirectionalStreamingEchoStream = ResponseStream;
|
||||
|
||||
async fn bidirectional_streaming_echo(
|
||||
&self,
|
||||
_: Request<Streaming<EchoRequest>>,
|
||||
) -> EchoResult<Self::BidirectionalStreamingEchoStream> {
|
||||
Err(Status::unimplemented("not implemented"))
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let cert = tokio::fs::read("tonic-examples/data/tls/server.pem").await?;
|
||||
let key = tokio::fs::read("tonic-examples/data/tls/server.key").await?;
|
||||
|
||||
let identity = Identity::from_pem(cert, key);
|
||||
|
||||
let addr = "[::1]:50051".parse().unwrap();
|
||||
let server = EchoServer::default();
|
||||
|
||||
Server::builder()
|
||||
.tls_config(ServerTlsConfig::with_rustls().identity(identity))
|
||||
.add_service(pb::echo_server::EchoServer::new(server))
|
||||
.serve(addr)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
pub mod pb {
|
||||
tonic::include_proto!("grpc.examples.echo");
|
||||
}
|
||||
|
||||
use pb::{echo_client::EchoClient, EchoRequest};
|
||||
use tonic::transport::{Certificate, Channel, ClientTlsConfig, Identity};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let server_root_ca_cert = tokio::fs::read("tonic-examples/data/tls/ca.pem").await?;
|
||||
let server_root_ca_cert = Certificate::from_pem(server_root_ca_cert);
|
||||
let client_cert = tokio::fs::read("tonic-examples/data/tls/client1.pem").await?;
|
||||
let client_key = tokio::fs::read("tonic-examples/data/tls/client1.key").await?;
|
||||
let client_identity = Identity::from_pem(client_cert, client_key);
|
||||
|
||||
let tls = ClientTlsConfig::with_rustls()
|
||||
.domain_name("localhost")
|
||||
.ca_certificate(server_root_ca_cert)
|
||||
.identity(client_identity);
|
||||
|
||||
let channel = Channel::from_static("http://[::1]:50051")
|
||||
.tls_config(tls)
|
||||
.connect()
|
||||
.await?;
|
||||
|
||||
let mut client = EchoClient::new(channel);
|
||||
|
||||
let request = tonic::Request::new(EchoRequest {
|
||||
message: "hello".into(),
|
||||
});
|
||||
|
||||
let response = client.unary_echo(request).await?;
|
||||
|
||||
println!("RESPONSE={:?}", response);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
pub mod pb {
|
||||
tonic::include_proto!("grpc.examples.echo");
|
||||
}
|
||||
|
||||
use futures::Stream;
|
||||
use pb::{EchoRequest, EchoResponse};
|
||||
use std::pin::Pin;
|
||||
use tonic::transport::{Certificate, Identity, Server, ServerTlsConfig};
|
||||
use tonic::{Request, Response, Status};
|
||||
|
||||
type EchoResult<T> = Result<Response<T>, Status>;
|
||||
type ResponseStream = Pin<Box<dyn Stream<Item = Result<EchoResponse, Status>> + Send + Sync>>;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct EchoServer;
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl pb::echo_server::Echo for EchoServer {
|
||||
async fn unary_echo(&self, request: Request<EchoRequest>) -> EchoResult<EchoResponse> {
|
||||
let message = request.into_inner().message;
|
||||
Ok(Response::new(EchoResponse { message }))
|
||||
}
|
||||
|
||||
type ServerStreamingEchoStream = ResponseStream;
|
||||
type BidirectionalStreamingEchoStream = ResponseStream;
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let cert = tokio::fs::read("tonic-examples/data/tls/server.pem").await?;
|
||||
let key = tokio::fs::read("tonic-examples/data/tls/server.key").await?;
|
||||
let server_identity = Identity::from_pem(cert, key);
|
||||
|
||||
let client_ca_cert = tokio::fs::read("tonic-examples/data/tls/client_ca.pem").await?;
|
||||
let client_ca_cert = Certificate::from_pem(client_ca_cert);
|
||||
|
||||
let addr = "[::1]:50051".parse().unwrap();
|
||||
let server = EchoServer::default();
|
||||
|
||||
let tls = ServerTlsConfig::with_rustls()
|
||||
.identity(server_identity)
|
||||
.client_ca_root(client_ca_cert);
|
||||
|
||||
Server::builder()
|
||||
.tls_config(tls)
|
||||
.add_service(pb::echo_server::EchoServer::new(server))
|
||||
.serve(addr)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
pub mod hello_world {
|
||||
tonic::include_proto!("helloworld");
|
||||
}
|
||||
|
||||
use hello_world::{greeter_client::GreeterClient, HelloRequest};
|
||||
use tracing_attributes::instrument;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
tracing_subscriber::FmtSubscriber::builder()
|
||||
.with_max_level(tracing::Level::DEBUG)
|
||||
.init();
|
||||
|
||||
say_hi("Bob".into()).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
async fn say_hi(name: String) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut client = GreeterClient::connect("http://[::1]:50051").await?;
|
||||
|
||||
let request = tonic::Request::new(HelloRequest { name });
|
||||
|
||||
tracing::info!(
|
||||
message = "Sending request.",
|
||||
request = %request.get_ref().name
|
||||
);
|
||||
|
||||
let response = client.say_hello(request).await?;
|
||||
|
||||
tracing::info!(
|
||||
message = "Got a response.",
|
||||
response = %response.get_ref().message
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
use tonic::{transport::Server, Request, Response, Status};
|
||||
|
||||
pub mod hello_world {
|
||||
tonic::include_proto!("helloworld");
|
||||
}
|
||||
|
||||
use hello_world::{
|
||||
greeter_server::{Greeter, GreeterServer},
|
||||
HelloReply, HelloRequest,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MyGreeter {}
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl Greeter for MyGreeter {
|
||||
async fn say_hello(
|
||||
&self,
|
||||
request: Request<HelloRequest>,
|
||||
) -> Result<Response<HelloReply>, Status> {
|
||||
tracing::info!(message = "Inbound request.", metadata = ?request.metadata());
|
||||
|
||||
let reply = hello_world::HelloReply {
|
||||
message: format!("Hello {}!", request.into_inner().name).into(),
|
||||
};
|
||||
|
||||
tracing::debug!(message = "Sending reply.", response = %reply.message);
|
||||
|
||||
Ok(Response::new(reply))
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
tracing_subscriber::FmtSubscriber::builder()
|
||||
.with_max_level(tracing::Level::DEBUG)
|
||||
.init();
|
||||
|
||||
let addr = "[::1]:50051".parse().unwrap();
|
||||
let greeter = MyGreeter::default();
|
||||
|
||||
tracing::info!(message = "Starting server.", %addr);
|
||||
|
||||
Server::builder()
|
||||
.trace_fn(|_| tracing::info_span!("helloworld_server"))
|
||||
.add_service(GreeterServer::new(greeter))
|
||||
.serve(addr)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user