aws_smithy_http_server/instrumentation/
service.rs1use std::{
9 future::Future,
10 pin::Pin,
11 task::{Context, Poll},
12};
13
14use futures_util::{ready, TryFuture};
15use http::{HeaderMap, Request, Response, StatusCode, Uri};
16use tower::Service;
17use tracing::{debug, debug_span, instrument::Instrumented, Instrument};
18
19use crate::shape_id::ShapeId;
20
21use super::{MakeDebug, MakeDisplay, MakeIdentity};
22
23pin_project_lite::pin_project! {
24 struct InnerFuture<Fut, ResponseMakeFmt> {
26 #[pin]
27 inner: Fut,
28 make: ResponseMakeFmt
29 }
30}
31
32impl<Fut, ResponseMakeFmt, T> Future for InnerFuture<Fut, ResponseMakeFmt>
33where
34 Fut: TryFuture<Ok = Response<T>>,
35 Fut: Future<Output = Result<Fut::Ok, Fut::Error>>,
36
37 for<'a> ResponseMakeFmt: MakeDebug<&'a HeaderMap>,
38 for<'a> ResponseMakeFmt: MakeDisplay<StatusCode>,
39{
40 type Output = Fut::Output;
41
42 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
43 let this = self.project();
44 let response = ready!(this.inner.poll(cx))?;
45
46 {
47 let headers = this.make.make_debug(response.headers());
48 let status_code = this.make.make_display(response.status());
49 debug!(?headers, %status_code, "response");
50 }
51
52 Poll::Ready(Ok(response))
53 }
54}
55
56pin_project_lite::pin_project! {
58 pub struct InstrumentedFuture<Fut, ResponseMakeFmt> {
60 #[pin]
61 inner: Instrumented<InnerFuture<Fut, ResponseMakeFmt>>
62 }
63}
64
65impl<Fut, ResponseMakeFmt, T> Future for InstrumentedFuture<Fut, ResponseMakeFmt>
66where
67 Fut: TryFuture<Ok = Response<T>>,
68 Fut: Future<Output = Result<Fut::Ok, Fut::Error>>,
69
70 for<'a> ResponseMakeFmt: MakeDebug<&'a HeaderMap>,
71 for<'a> ResponseMakeFmt: MakeDisplay<StatusCode>,
72{
73 type Output = Fut::Output;
74
75 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
76 self.project().inner.poll(cx)
77 }
78}
79
80#[derive(Debug, Clone)]
108pub struct InstrumentOperation<S, RequestMakeFmt = MakeIdentity, ResponseMakeFmt = MakeIdentity> {
109 inner: S,
110 operation_id: ShapeId,
111 make_request: RequestMakeFmt,
112 make_response: ResponseMakeFmt,
113}
114
115impl<S> InstrumentOperation<S> {
116 pub fn new(inner: S, operation_id: ShapeId) -> Self {
118 Self {
119 inner,
120 operation_id,
121 make_request: MakeIdentity,
122 make_response: MakeIdentity,
123 }
124 }
125}
126
127impl<S, RequestMakeFmt, ResponseMakeFmt> InstrumentOperation<S, RequestMakeFmt, ResponseMakeFmt> {
128 pub fn request_fmt<R>(self, make_request: R) -> InstrumentOperation<S, R, ResponseMakeFmt> {
132 InstrumentOperation {
133 inner: self.inner,
134 operation_id: self.operation_id,
135 make_request,
136 make_response: self.make_response,
137 }
138 }
139
140 pub fn response_fmt<R>(self, make_response: R) -> InstrumentOperation<S, RequestMakeFmt, R> {
144 InstrumentOperation {
145 inner: self.inner,
146 operation_id: self.operation_id,
147 make_request: self.make_request,
148 make_response,
149 }
150 }
151}
152
153impl<S, U, V, RequestMakeFmt, ResponseMakeFmt> Service<Request<U>>
154 for InstrumentOperation<S, RequestMakeFmt, ResponseMakeFmt>
155where
156 S: Service<Request<U>, Response = Response<V>>,
157
158 for<'a> RequestMakeFmt: MakeDebug<&'a HeaderMap>,
159 for<'a> RequestMakeFmt: MakeDisplay<&'a Uri>,
160
161 ResponseMakeFmt: Clone,
162 for<'a> ResponseMakeFmt: MakeDebug<&'a HeaderMap>,
163 for<'a> ResponseMakeFmt: MakeDisplay<StatusCode>,
164{
165 type Response = S::Response;
166 type Error = S::Error;
167 type Future = InstrumentedFuture<S::Future, ResponseMakeFmt>;
168
169 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
170 self.inner.poll_ready(cx)
171 }
172
173 fn call(&mut self, request: Request<U>) -> Self::Future {
174 let span = {
175 let headers = self.make_request.make_debug(request.headers());
176 let uri = self.make_request.make_display(request.uri());
177 debug_span!("request", operation = %self.operation_id.absolute(), method = %request.method(), %uri, ?headers)
178 };
179
180 InstrumentedFuture {
181 inner: InnerFuture {
182 inner: self.inner.call(request),
183 make: self.make_response.clone(),
184 }
185 .instrument(span),
186 }
187 }
188}