Skip to content

Commit 8909021

Browse files
authored
feat: Add tower integration (#356)
1 parent 78f1a72 commit 8909021

File tree

7 files changed

+621
-0
lines changed

7 files changed

+621
-0
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ members = [
1010
"sentry-log",
1111
"sentry-panic",
1212
"sentry-slog",
13+
"sentry-tower",
1314
"sentry-tracing",
1415
"sentry-types",
1516
]

sentry-tower/Cargo.toml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[package]
2+
name = "sentry-tower"
3+
version = "0.23.0"
4+
authors = ["Sentry <[email protected]>"]
5+
license = "Apache-2.0"
6+
readme = "README.md"
7+
repository = "https://github.com/getsentry/sentry-rust"
8+
homepage = "https://sentry.io/welcome/"
9+
description = """
10+
Sentry integration for tower-based crates.
11+
"""
12+
edition = "2018"
13+
14+
[dependencies]
15+
tower-layer = "0.3"
16+
tower-service = "0.3"
17+
sentry-core = { version = "0.23.0", path = "../sentry-core", default-features = false, features = ["client"] }
18+
19+
[dev-dependencies]
20+
anyhow = "1"
21+
prost = "0.8"
22+
sentry = { version = "0.23.0", path = "../sentry", default-features = false, features = ["test"] }
23+
sentry-anyhow = { version = "0.23.0", path = "../sentry-anyhow" }
24+
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
25+
tonic = { version = "0.5", features = ["transport"] }
26+
tower = { version = "0.4", features = ["util", "timeout"] }

sentry-tower/src/helloworld.rs

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
// Autogenerated from tonic's example proto files using `tonic-build`.
2+
//
3+
// Source proto files can be found at
4+
// https://github.com/hyperium/tonic/blob/master/examples/proto/helloworld/helloworld.proto
5+
6+
/// The request message containing the user's name.
7+
#[derive(Clone, PartialEq, ::prost::Message)]
8+
pub struct HelloRequest {
9+
#[prost(string, tag = "1")]
10+
pub name: ::prost::alloc::string::String,
11+
}
12+
/// The response message containing the greetings
13+
#[derive(Clone, PartialEq, ::prost::Message)]
14+
pub struct HelloReply {
15+
#[prost(string, tag = "1")]
16+
pub message: ::prost::alloc::string::String,
17+
}
18+
#[doc = r" Generated client implementations."]
19+
pub mod greeter_client {
20+
#![allow(unused_variables, dead_code, missing_docs)]
21+
use tonic::codegen::*;
22+
#[doc = " The greeting service definition."]
23+
#[derive(Debug, Clone)]
24+
pub struct GreeterClient<T> {
25+
inner: tonic::client::Grpc<T>,
26+
}
27+
impl GreeterClient<tonic::transport::Channel> {
28+
#[doc = r" Attempt to create a new client by connecting to a given endpoint."]
29+
pub async fn connect<D>(dst: D) -> Result<Self, tonic::transport::Error>
30+
where
31+
D: std::convert::TryInto<tonic::transport::Endpoint>,
32+
D::Error: Into<StdError>,
33+
{
34+
let conn = tonic::transport::Endpoint::new(dst)?.connect().await?;
35+
Ok(Self::new(conn))
36+
}
37+
}
38+
impl<T> GreeterClient<T>
39+
where
40+
T: tonic::client::GrpcService<tonic::body::BoxBody>,
41+
T::ResponseBody: Body + Send + Sync + 'static,
42+
T::Error: Into<StdError>,
43+
<T::ResponseBody as Body>::Error: Into<StdError> + Send,
44+
{
45+
pub fn new(inner: T) -> Self {
46+
let inner = tonic::client::Grpc::new(inner);
47+
Self { inner }
48+
}
49+
pub fn with_interceptor<F>(
50+
inner: T,
51+
interceptor: F,
52+
) -> GreeterClient<InterceptedService<T, F>>
53+
where
54+
F: FnMut(tonic::Request<()>) -> Result<tonic::Request<()>, tonic::Status>,
55+
T: Service<
56+
http::Request<tonic::body::BoxBody>,
57+
Response = http::Response<
58+
<T as tonic::client::GrpcService<tonic::body::BoxBody>>::ResponseBody,
59+
>,
60+
>,
61+
<T as Service<http::Request<tonic::body::BoxBody>>>::Error:
62+
Into<StdError> + Send + Sync,
63+
{
64+
GreeterClient::new(InterceptedService::new(inner, interceptor))
65+
}
66+
#[doc = r" Compress requests with `gzip`."]
67+
#[doc = r""]
68+
#[doc = r" This requires the server to support it otherwise it might respond with an"]
69+
#[doc = r" error."]
70+
pub fn send_gzip(mut self) -> Self {
71+
self.inner = self.inner.send_gzip();
72+
self
73+
}
74+
#[doc = r" Enable decompressing responses with `gzip`."]
75+
pub fn accept_gzip(mut self) -> Self {
76+
self.inner = self.inner.accept_gzip();
77+
self
78+
}
79+
#[doc = " Sends a greeting"]
80+
pub async fn say_hello(
81+
&mut self,
82+
request: impl tonic::IntoRequest<super::HelloRequest>,
83+
) -> Result<tonic::Response<super::HelloReply>, tonic::Status> {
84+
self.inner.ready().await.map_err(|e| {
85+
tonic::Status::new(
86+
tonic::Code::Unknown,
87+
format!("Service was not ready: {}", e.into()),
88+
)
89+
})?;
90+
let codec = tonic::codec::ProstCodec::default();
91+
let path = http::uri::PathAndQuery::from_static("/helloworld.Greeter/SayHello");
92+
self.inner.unary(request.into_request(), path, codec).await
93+
}
94+
}
95+
}
96+
#[doc = r" Generated server implementations."]
97+
pub mod greeter_server {
98+
#![allow(unused_variables, dead_code, missing_docs)]
99+
use tonic::codegen::*;
100+
#[doc = "Generated trait containing gRPC methods that should be implemented for use with GreeterServer."]
101+
#[async_trait]
102+
pub trait Greeter: Send + Sync + 'static {
103+
#[doc = " Sends a greeting"]
104+
async fn say_hello(
105+
&self,
106+
request: tonic::Request<super::HelloRequest>,
107+
) -> Result<tonic::Response<super::HelloReply>, tonic::Status>;
108+
}
109+
#[doc = " The greeting service definition."]
110+
#[derive(Debug)]
111+
pub struct GreeterServer<T: Greeter> {
112+
inner: _Inner<T>,
113+
accept_compression_encodings: (),
114+
send_compression_encodings: (),
115+
}
116+
struct _Inner<T>(Arc<T>);
117+
impl<T: Greeter> GreeterServer<T> {
118+
pub fn new(inner: T) -> Self {
119+
let inner = Arc::new(inner);
120+
let inner = _Inner(inner);
121+
Self {
122+
inner,
123+
accept_compression_encodings: Default::default(),
124+
send_compression_encodings: Default::default(),
125+
}
126+
}
127+
pub fn with_interceptor<F>(inner: T, interceptor: F) -> InterceptedService<Self, F>
128+
where
129+
F: FnMut(tonic::Request<()>) -> Result<tonic::Request<()>, tonic::Status>,
130+
{
131+
InterceptedService::new(Self::new(inner), interceptor)
132+
}
133+
}
134+
impl<T, B> Service<http::Request<B>> for GreeterServer<T>
135+
where
136+
T: Greeter,
137+
B: Body + Send + Sync + 'static,
138+
B::Error: Into<StdError> + Send + 'static,
139+
{
140+
type Response = http::Response<tonic::body::BoxBody>;
141+
type Error = Never;
142+
type Future = BoxFuture<Self::Response, Self::Error>;
143+
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
144+
Poll::Ready(Ok(()))
145+
}
146+
fn call(&mut self, req: http::Request<B>) -> Self::Future {
147+
let inner = self.inner.clone();
148+
match req.uri().path() {
149+
"/helloworld.Greeter/SayHello" => {
150+
#[allow(non_camel_case_types)]
151+
struct SayHelloSvc<T: Greeter>(pub Arc<T>);
152+
impl<T: Greeter> tonic::server::UnaryService<super::HelloRequest> for SayHelloSvc<T> {
153+
type Response = super::HelloReply;
154+
type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>;
155+
fn call(
156+
&mut self,
157+
request: tonic::Request<super::HelloRequest>,
158+
) -> Self::Future {
159+
let inner = self.0.clone();
160+
let fut = async move { (*inner).say_hello(request).await };
161+
Box::pin(fut)
162+
}
163+
}
164+
let accept_compression_encodings = self.accept_compression_encodings;
165+
let send_compression_encodings = self.send_compression_encodings;
166+
let inner = self.inner.clone();
167+
let fut = async move {
168+
let inner = inner.0;
169+
let method = SayHelloSvc(inner);
170+
let codec = tonic::codec::ProstCodec::default();
171+
let mut grpc = tonic::server::Grpc::new(codec).apply_compression_config(
172+
accept_compression_encodings,
173+
send_compression_encodings,
174+
);
175+
let res = grpc.unary(method, req).await;
176+
Ok(res)
177+
};
178+
Box::pin(fut)
179+
}
180+
_ => Box::pin(async move {
181+
Ok(http::Response::builder()
182+
.status(200)
183+
.header("grpc-status", "12")
184+
.header("content-type", "application/grpc")
185+
.body(empty_body())
186+
.unwrap())
187+
}),
188+
}
189+
}
190+
}
191+
impl<T: Greeter> Clone for GreeterServer<T> {
192+
fn clone(&self) -> Self {
193+
let inner = self.inner.clone();
194+
Self {
195+
inner,
196+
accept_compression_encodings: self.accept_compression_encodings,
197+
send_compression_encodings: self.send_compression_encodings,
198+
}
199+
}
200+
}
201+
impl<T: Greeter> Clone for _Inner<T> {
202+
fn clone(&self) -> Self {
203+
Self(self.0.clone())
204+
}
205+
}
206+
impl<T: std::fmt::Debug> std::fmt::Debug for _Inner<T> {
207+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208+
write!(f, "{:?}", self.0)
209+
}
210+
}
211+
impl<T: Greeter> tonic::transport::NamedService for GreeterServer<T> {
212+
const NAME: &'static str = "helloworld.Greeter";
213+
}
214+
}

0 commit comments

Comments
 (0)