-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
examples: add an example of using a custom json codec
- Loading branch information
Showing
5 changed files
with
191 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
//! A HelloWorld example that uses JSON instead of protobuf as the message serialization format. | ||
//! | ||
//! Generated code is the output of codegen as defined in the `build_json_codec_service` function | ||
//! in the `examples/build.rs` file. As defined there, the generated code assumes that a module | ||
//! `crate::common` exists which defines `HelloRequest`, `HelloResponse`, and `JsonCodec`. | ||
|
||
pub mod common; | ||
use common::HelloRequest; | ||
|
||
pub mod hello_world { | ||
include!(concat!(env!("OUT_DIR"), "/json.helloworld.Greeter.rs")); | ||
} | ||
use hello_world::greeter_client::GreeterClient; | ||
|
||
#[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(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
//! This module defines common request/response types as well as the JsonCodec that is used by the | ||
//! json.helloworld.Greeter service which is defined manually (instead of via proto files) by the | ||
//! `build_json_codec_service` function in the `examples/build.rs` file. | ||
|
||
use bytes::{Buf, BufMut}; | ||
use serde::{Deserialize, Serialize}; | ||
use std::marker::PhantomData; | ||
use tonic::{ | ||
codec::{Codec, DecodeBuf, Decoder, EncodeBuf, Encoder}, | ||
Status, | ||
}; | ||
|
||
#[derive(Debug, Deserialize, Serialize)] | ||
pub struct HelloRequest { | ||
pub name: String, | ||
} | ||
|
||
#[derive(Debug, Deserialize, Serialize)] | ||
pub struct HelloResponse { | ||
pub message: String, | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct JsonEncoder<T>(PhantomData<T>); | ||
|
||
impl<T: serde::Serialize> Encoder for JsonEncoder<T> { | ||
type Item = T; | ||
type Error = Status; | ||
|
||
fn encode(&mut self, item: Self::Item, buf: &mut EncodeBuf<'_>) -> Result<(), Self::Error> { | ||
serde_json::to_writer(buf.writer(), &item).map_err(|e| Status::internal(e.to_string())) | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct JsonDecoder<U>(PhantomData<U>); | ||
|
||
impl<U: serde::de::DeserializeOwned> Decoder for JsonDecoder<U> { | ||
type Item = U; | ||
type Error = Status; | ||
|
||
fn decode(&mut self, buf: &mut DecodeBuf<'_>) -> Result<Option<Self::Item>, Self::Error> { | ||
if !buf.has_remaining() { | ||
return Ok(None); | ||
} | ||
|
||
let item: Self::Item = | ||
serde_json::from_reader(buf.reader()).map_err(|e| Status::internal(e.to_string()))?; | ||
Ok(Some(item)) | ||
} | ||
} | ||
|
||
/// A [`Codec`] that implements `application/grpc+json` via the serde library. | ||
#[derive(Debug, Clone)] | ||
pub struct JsonCodec<T, U>(PhantomData<(T, U)>); | ||
|
||
impl<T, U> Default for JsonCodec<T, U> { | ||
fn default() -> Self { | ||
Self(PhantomData) | ||
} | ||
} | ||
|
||
impl<T, U> Codec for JsonCodec<T, U> | ||
where | ||
T: serde::Serialize + Send + 'static, | ||
U: serde::de::DeserializeOwned + Send + 'static, | ||
{ | ||
type Encode = T; | ||
type Decode = U; | ||
type Encoder = JsonEncoder<T>; | ||
type Decoder = JsonDecoder<U>; | ||
|
||
fn encoder(&mut self) -> Self::Encoder { | ||
JsonEncoder(PhantomData) | ||
} | ||
|
||
fn decoder(&mut self) -> Self::Decoder { | ||
JsonDecoder(PhantomData) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
//! A HelloWorld example that uses JSON instead of protobuf as the message serialization format. | ||
//! | ||
//! Generated code is the output of codegen as defined in the `build_json_codec_service` function | ||
//! in the `examples/build.rs` file. As defined there, the generated code assumes that a module | ||
//! `crate::common` exists which defines `HelloRequest`, `HelloResponse`, and `JsonCodec`. | ||
|
||
use tonic::{transport::Server, Request, Response, Status}; | ||
|
||
pub mod common; | ||
use common::{HelloRequest, HelloResponse}; | ||
|
||
pub mod hello_world { | ||
include!(concat!(env!("OUT_DIR"), "/json.helloworld.Greeter.rs")); | ||
} | ||
use hello_world::greeter_server::{Greeter, GreeterServer}; | ||
|
||
#[derive(Default)] | ||
pub struct MyGreeter {} | ||
|
||
#[tonic::async_trait] | ||
impl Greeter for MyGreeter { | ||
async fn say_hello( | ||
&self, | ||
request: Request<HelloRequest>, | ||
) -> Result<Response<HelloResponse>, Status> { | ||
println!("Got a request from {:?}", request.remote_addr()); | ||
|
||
let reply = HelloResponse { | ||
message: format!("Hello {}!", request.into_inner().name), | ||
}; | ||
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(); | ||
|
||
println!("GreeterServer listening on {}", addr); | ||
|
||
Server::builder() | ||
.add_service(GreeterServer::new(greeter)) | ||
.serve(addr) | ||
.await?; | ||
|
||
Ok(()) | ||
} |