Skip to main content

apollo_router/
http_ext.rs

1//! Wrapper types for [`http::Request`] and [`http::Response`] from the http crate.
2//!
3//! To improve their usability.
4
5use std::cmp::PartialEq;
6use std::hash::Hash;
7use std::ops::Deref;
8use std::ops::DerefMut;
9
10use axum::response::IntoResponse;
11use bytes::Bytes;
12use http::HeaderValue;
13use http::header;
14use http::header::HeaderName;
15use multimap::MultiMap;
16
17use crate::graphql;
18use crate::services::APPLICATION_JSON_HEADER_VALUE;
19
20/// Delayed-fallibility wrapper for conversion to [`http::header::HeaderName`].
21///
22/// `buildstructor` builders allow doing implicit conversions for convenience,
23/// but only infallible ones.
24/// `HeaderName` can be converted from various types but the conversions is often fallible,
25/// with `TryFrom` or `TryInto` instead of `From` or `Into`.
26/// This types splits conversion in two steps:
27/// it implements infallible conversion from various types like `&str` (that builders can rely on)
28/// and fallible conversion to `HeaderName` (called later where we can handle errors).
29///
30/// See for example [`supergraph::Request::builder`][crate::services::supergraph::Request::builder]
31/// which can be used like this:
32///
33/// ```
34/// # fn main() -> Result<(), tower::BoxError> {
35/// use apollo_router::services::supergraph;
36/// let request = supergraph::Request::fake_builder()
37///     .header("accept-encoding", "gzip")
38///     // Other parameters
39///     .build()?;
40/// # Ok(()) }
41/// ```
42pub struct TryIntoHeaderName {
43    /// The fallible conversion result
44    result: Result<header::HeaderName, header::InvalidHeaderName>,
45}
46
47/// Delayed-fallibility wrapper for conversion to [`http::header::HeaderValue`].
48///
49/// `buildstructor` builders allow doing implicit conversions for convenience,
50/// but only infallible ones.
51/// `HeaderValue` can be converted from various types but the conversions is often fallible,
52/// with `TryFrom` or `TryInto` instead of `From` or `Into`.
53/// This types splits conversion in two steps:
54/// it implements infallible conversion from various types like `&str` (that builders can rely on)
55/// and fallible conversion to `HeaderValue` (called later where we can handle errors).
56///
57/// See for example [`supergraph::Request::builder`][crate::services::supergraph::Request::builder]
58/// which can be used like this:
59///
60/// ```
61/// # fn main() -> Result<(), tower::BoxError> {
62/// use apollo_router::services::supergraph;
63/// let request = supergraph::Request::fake_builder()
64///     .header("accept-encoding", "gzip")
65///     // Other parameters
66///     .build()?;
67/// # Ok(()) }
68/// ```
69pub struct TryIntoHeaderValue {
70    /// The fallible conversion result
71    result: Result<header::HeaderValue, header::InvalidHeaderValue>,
72}
73
74impl TryFrom<TryIntoHeaderName> for header::HeaderName {
75    type Error = header::InvalidHeaderName;
76
77    fn try_from(value: TryIntoHeaderName) -> Result<Self, Self::Error> {
78        value.result
79    }
80}
81
82impl TryFrom<TryIntoHeaderValue> for header::HeaderValue {
83    type Error = header::InvalidHeaderValue;
84
85    fn try_from(value: TryIntoHeaderValue) -> Result<Self, Self::Error> {
86        value.result
87    }
88}
89
90impl From<header::HeaderName> for TryIntoHeaderName {
91    fn from(value: header::HeaderName) -> Self {
92        Self { result: Ok(value) }
93    }
94}
95
96impl From<&'_ header::HeaderName> for TryIntoHeaderName {
97    fn from(value: &'_ header::HeaderName) -> Self {
98        Self {
99            result: Ok(value.clone()),
100        }
101    }
102}
103
104impl From<&'_ [u8]> for TryIntoHeaderName {
105    fn from(value: &'_ [u8]) -> Self {
106        Self {
107            result: value.try_into(),
108        }
109    }
110}
111
112impl From<&'_ str> for TryIntoHeaderName {
113    fn from(value: &'_ str) -> Self {
114        Self {
115            result: value.try_into(),
116        }
117    }
118}
119
120impl From<Vec<u8>> for TryIntoHeaderName {
121    fn from(value: Vec<u8>) -> Self {
122        Self {
123            result: value.try_into(),
124        }
125    }
126}
127
128impl From<String> for TryIntoHeaderName {
129    fn from(value: String) -> Self {
130        Self {
131            result: value.try_into(),
132        }
133    }
134}
135
136impl From<header::HeaderValue> for TryIntoHeaderValue {
137    fn from(value: header::HeaderValue) -> Self {
138        Self { result: Ok(value) }
139    }
140}
141
142impl From<&'_ header::HeaderValue> for TryIntoHeaderValue {
143    fn from(value: &'_ header::HeaderValue) -> Self {
144        Self {
145            result: Ok(value.clone()),
146        }
147    }
148}
149
150impl From<&'_ [u8]> for TryIntoHeaderValue {
151    fn from(value: &'_ [u8]) -> Self {
152        Self {
153            result: value.try_into(),
154        }
155    }
156}
157
158impl From<&'_ str> for TryIntoHeaderValue {
159    fn from(value: &'_ str) -> Self {
160        Self {
161            result: value.try_into(),
162        }
163    }
164}
165
166impl From<Vec<u8>> for TryIntoHeaderValue {
167    fn from(value: Vec<u8>) -> Self {
168        Self {
169            result: value.try_into(),
170        }
171    }
172}
173
174impl From<String> for TryIntoHeaderValue {
175    fn from(value: String) -> Self {
176        Self {
177            result: value.try_into(),
178        }
179    }
180}
181
182impl Eq for TryIntoHeaderName {}
183
184impl PartialEq for TryIntoHeaderName {
185    fn eq(&self, other: &Self) -> bool {
186        match (&self.result, &other.result) {
187            (Ok(a), Ok(b)) => a == b,
188            (Err(_), Err(_)) => true,
189            _ => false,
190        }
191    }
192}
193
194impl Hash for TryIntoHeaderName {
195    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
196        if let Ok(value) = &self.result {
197            value.hash(state)
198        }
199    }
200}
201
202pub(crate) fn header_map(
203    from: MultiMap<TryIntoHeaderName, TryIntoHeaderValue>,
204) -> Result<http::HeaderMap<http::HeaderValue>, http::Error> {
205    let mut http = http::HeaderMap::new();
206    for (key, values) in from {
207        let name = key.result?;
208        let mut values = values.into_iter();
209        if let Some(last) = values.next_back() {
210            for value in values {
211                http.append(name.clone(), value.result?);
212            }
213            http.append(name, last.result?);
214        }
215    }
216    Ok(http)
217}
218
219// In an earlier version of the http crate, `Request` didn't implement `Clone`, so we
220// implemented partial support (excluding Extensions) for `Clone`. `Request` does now implement
221// `Clone`, so this function does now clone Requests fully.
222//  - https://github.com/hyperium/http/pull/634
223//  - https://github.com/hyperium/http/releases/tag/v1.0.0
224pub(crate) fn clone_http_request<B: Clone>(request: &http::Request<B>) -> http::Request<B> {
225    request.clone()
226}
227
228// In an earlier version of the http crate, `Response` didn't implement `Clone`, so we
229// implemented partial support (excluding Extensions) for `Clone`. `Response` does now implement
230// `Clone`, so this function does now clone Responses fully.
231//  - https://github.com/hyperium/http/pull/634
232//  - https://github.com/hyperium/http/releases/tag/v1.0.0
233pub(crate) fn clone_http_response<B: Clone>(response: &http::Response<B>) -> http::Response<B> {
234    response.clone()
235}
236
237/// Wrap an http Request.
238#[derive(Debug)]
239pub(crate) struct Request<T> {
240    pub(crate) inner: http::Request<T>,
241}
242
243// Most of the required functionality is provided by our Deref and DerefMut implementations.
244#[buildstructor::buildstructor]
245impl<T> Request<T> {
246    /// This is the constructor (or builder) to use when constructing a real Request.
247    ///
248    /// Required parameters are required in non-testing code to create a Request.
249    #[builder]
250    pub(crate) fn new(
251        headers: MultiMap<TryIntoHeaderName, TryIntoHeaderValue>,
252        uri: http::Uri,
253        method: http::Method,
254        body: T,
255    ) -> http::Result<Request<T>> {
256        let mut builder = http::request::Builder::new().method(method).uri(uri);
257        for (key, values) in headers {
258            let header_name: HeaderName = key.try_into()?;
259            for value in values {
260                let header_value: HeaderValue = value.try_into()?;
261                builder = builder.header(header_name.clone(), header_value);
262            }
263        }
264        let req = builder.body(body)?;
265        Ok(Self { inner: req })
266    }
267}
268
269#[cfg(test)]
270#[buildstructor::buildstructor]
271impl<T> Request<T> {
272    /// This is the constructor (or builder) to use when constructing a "fake" Request.
273    ///
274    /// This does not enforce the provision of the uri and method that is required for a fully functional
275    /// Request. It's usually enough for testing, when a fully constructed Request is
276    /// difficult to construct and not required for the purposes of the test.
277    ///
278    /// In addition, fake requests are expected to be valid, and will panic if given invalid values.
279    #[builder]
280    pub(crate) fn fake_new(
281        headers: MultiMap<TryIntoHeaderName, TryIntoHeaderValue>,
282        uri: Option<http::Uri>,
283        method: Option<http::Method>,
284        body: T,
285    ) -> http::Result<Request<T>> {
286        Self::new(
287            headers,
288            uri.unwrap_or_default(),
289            method.unwrap_or(http::Method::GET),
290            body,
291        )
292    }
293}
294
295impl<T> Deref for Request<T> {
296    type Target = http::Request<T>;
297
298    fn deref(&self) -> &Self::Target {
299        &self.inner
300    }
301}
302
303impl<T> DerefMut for Request<T> {
304    fn deref_mut(&mut self) -> &mut Self::Target {
305        &mut self.inner
306    }
307}
308
309impl<T> From<http::Request<T>> for Request<T> {
310    fn from(inner: http::Request<T>) -> Self {
311        Request { inner }
312    }
313}
314
315impl<T: Clone> From<&'_ http::Request<T>> for Request<T> {
316    fn from(req: &'_ http::Request<T>) -> Self {
317        Request {
318            inner: clone_http_request(req),
319        }
320    }
321}
322
323impl<T> From<Request<T>> for http::Request<T> {
324    fn from(request: Request<T>) -> http::Request<T> {
325        request.inner
326    }
327}
328
329impl<T: Clone> Clone for Request<T> {
330    fn clone(&self) -> Self {
331        Self {
332            inner: clone_http_request(&self.inner),
333        }
334    }
335}
336
337impl<T: Hash> Hash for Request<T> {
338    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
339        self.inner.method().hash(state);
340        self.inner.version().hash(state);
341        self.inner.uri().hash(state);
342        // this assumes headers are in the same order
343        for (name, value) in self.inner.headers() {
344            name.hash(state);
345            value.hash(state);
346        }
347        self.inner.body().hash(state);
348    }
349}
350
351impl<T: PartialEq> PartialEq for Request<T> {
352    fn eq(&self, other: &Self) -> bool {
353        let mut res = self.inner.method().eq(other.inner.method())
354            && self.inner.version().eq(&other.inner.version())
355            && self.inner.uri().eq(other.inner.uri());
356
357        if !res {
358            return false;
359        }
360        if self.inner.headers().len() != other.inner.headers().len() {
361            return false;
362        }
363
364        // this assumes headers are in the same order
365        for ((name, value), (other_name, other_value)) in self
366            .inner
367            .headers()
368            .iter()
369            .zip(other.inner.headers().iter())
370        {
371            res = name.eq(other_name) && value.eq(other_value);
372            if !res {
373                return false;
374            }
375        }
376
377        self.inner.body().eq(other.inner.body())
378    }
379}
380
381impl<T: Eq> Eq for Request<T> {}
382
383/// Wrap an http Response.
384#[derive(Debug, Default)]
385pub(crate) struct Response<T> {
386    pub(crate) inner: http::Response<T>,
387}
388
389impl<T> Deref for Response<T> {
390    type Target = http::Response<T>;
391
392    fn deref(&self) -> &Self::Target {
393        &self.inner
394    }
395}
396
397impl<T> DerefMut for Response<T> {
398    fn deref_mut(&mut self) -> &mut Self::Target {
399        &mut self.inner
400    }
401}
402
403impl<T> From<http::Response<T>> for Response<T> {
404    fn from(inner: http::Response<T>) -> Self {
405        Response { inner }
406    }
407}
408
409impl<T: Clone> From<&'_ http::Response<T>> for Response<T> {
410    fn from(req: &'_ http::Response<T>) -> Self {
411        Response {
412            inner: clone_http_response(req),
413        }
414    }
415}
416
417impl<T> From<Response<T>> for http::Response<T> {
418    fn from(response: Response<T>) -> http::Response<T> {
419        response.inner
420    }
421}
422
423impl<T: Clone> Clone for Response<T> {
424    fn clone(&self) -> Self {
425        Self {
426            inner: clone_http_response(&self.inner),
427        }
428    }
429}
430
431impl IntoResponse for Response<graphql::Response> {
432    fn into_response(self) -> axum::response::Response {
433        // todo: chunks?
434        let (mut parts, body) = http::Response::from(self).into_parts();
435        let json_body_bytes =
436            Bytes::from(serde_json::to_vec(&body).expect("body should be serializable; qed"));
437        parts
438            .headers
439            .insert(header::CONTENT_TYPE, APPLICATION_JSON_HEADER_VALUE.clone());
440
441        axum::response::Response::from_parts(parts, axum::body::Body::from(json_body_bytes))
442    }
443}
444
445impl IntoResponse for Response<Bytes> {
446    fn into_response(self) -> axum::response::Response {
447        // todo: chunks?
448        let (parts, body) = http::Response::from(self).into_parts();
449
450        axum::response::Response::from_parts(parts, axum::body::Body::from(body))
451    }
452}
453
454#[cfg(test)]
455mod test {
456    use http::HeaderValue;
457    use http::Method;
458    use http::Uri;
459
460    use super::clone_http_request;
461    use super::clone_http_response;
462    use crate::http_ext::Request;
463
464    #[test]
465    fn builder() {
466        let request = Request::builder()
467            .header("a", "b")
468            .header("a", "c")
469            .uri(Uri::from_static("http://example.com"))
470            .method(Method::POST)
471            .body("test")
472            .build()
473            .unwrap();
474        assert_eq!(
475            request
476                .headers()
477                .get_all("a")
478                .into_iter()
479                .collect::<Vec<_>>(),
480            vec![HeaderValue::from_static("b"), HeaderValue::from_static("c")]
481        );
482        assert_eq!(request.uri(), &Uri::from_static("http://example.com"));
483        assert_eq!(request.method(), Method::POST);
484        assert_eq!(request.body(), &"test");
485    }
486
487    #[test]
488    fn cloning_an_http_request_includes_extensions() {
489        let mut request = http::Request::new("body".to_string());
490        request.extensions_mut().insert::<usize>(6);
491        let cloned_request = clone_http_request(&request);
492
493        assert_eq!(
494            *cloned_request
495                .extensions()
496                .get::<usize>()
497                .expect("it has the usize extension"),
498            6
499        );
500    }
501
502    #[test]
503    fn cloning_an_http_response_includes_extensions() {
504        let mut response = http::Response::new("body".to_string());
505        response.extensions_mut().insert::<usize>(6);
506        let cloned_response = clone_http_response(&response);
507
508        assert_eq!(
509            *cloned_response
510                .extensions()
511                .get::<usize>()
512                .expect("it has the usize extension"),
513            6
514        );
515    }
516}