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}