linode_api_proxy/http_handlers/
fallback_handler.rs

1use core::{future::Future, pin::Pin};
2
3use axum::{
4    body::Body,
5    handler::Handler,
6    http::{
7        uri::{Builder as UriBuilder, Parts as UriParts},
8        Request, StatusCode,
9    },
10    response::{IntoResponse as _, Json, Response},
11};
12use linode_api::types::Version;
13
14use crate::context::LinodeApiHttpClient;
15
16//
17#[derive(Debug, Clone)]
18#[non_exhaustive]
19pub struct FallbackHandler {
20    pub linode_api_http_client: LinodeApiHttpClient,
21    pub version: Version,
22}
23
24impl FallbackHandler {
25    pub fn new(linode_api_http_client: LinodeApiHttpClient, version: Version) -> Self {
26        Self {
27            linode_api_http_client,
28            version,
29        }
30    }
31}
32
33impl<T, S> Handler<T, S, Body> for FallbackHandler {
34    type Future = Pin<Box<dyn Future<Output = Response> + Send + 'static>>;
35
36    fn call(self, mut req: Request<Body>, _state: S) -> Self::Future {
37        let req_uri_path = req.uri().path();
38        let req_uri_query = req.uri().query();
39
40        //
41        let base_uri = match self.version {
42            Version::V4 => {
43                use linode_api::endpoints::v4::BASE_URI;
44
45                BASE_URI.to_owned()
46            }
47        };
48
49        let req_uri = {
50            let UriParts {
51                scheme,
52                authority,
53                path_and_query,
54                ..
55            } = base_uri.into_parts();
56
57            let mut path_and_query_str = path_and_query
58                .map(|x| x.path().to_owned())
59                .unwrap_or_default();
60
61            if !req_uri_path.starts_with('/') {
62                path_and_query_str.push('/');
63            }
64            path_and_query_str.push_str(req_uri_path);
65
66            if let Some(req_uri_query) = req_uri_query {
67                path_and_query_str.push('?');
68                path_and_query_str.push_str(req_uri_query)
69            }
70
71            let uri_builder = UriBuilder::new();
72            let uri_builder = if let Some(scheme) = scheme {
73                uri_builder.scheme(scheme)
74            } else {
75                uri_builder
76            };
77            let uri_builder = if let Some(authority) = authority {
78                uri_builder.authority(authority)
79            } else {
80                uri_builder
81            };
82            let uri_builder = uri_builder.path_and_query(path_and_query_str);
83
84            match uri_builder.build() {
85                Ok(x) => x,
86                Err(err) => {
87                    return Box::pin(async move {
88                        let mut resp = match self.version {
89                            Version::V4 => {
90                                use linode_api::{
91                                    endpoints::v4::ErrorResponseBody,
92                                    objects::v4::{Error, ErrorReason},
93                                };
94
95                                Json(ErrorResponseBody {
96                                    errors: vec![Error {
97                                        field: None,
98                                        reason: ErrorReason::Other(format!(
99                                            "request uri change failed, err:{err}"
100                                        )),
101                                    }],
102                                })
103                                .into_response()
104                            }
105                        };
106
107                        *resp.status_mut() = StatusCode::from_u16(599).expect("Never");
108                        resp
109                    })
110                }
111            }
112        };
113
114        *req.uri_mut() = req_uri;
115
116        Box::pin(async move {
117            match axum_request_send::impl_reqwest::send(self.linode_api_http_client.inner(), req)
118                .await
119            {
120                Ok(resp) => resp,
121                Err(err) => match self.version {
122                    Version::V4 => {
123                        use linode_api::{
124                            endpoints::v4::ErrorResponseBody,
125                            objects::v4::{Error, ErrorReason},
126                        };
127
128                        let mut resp = Json(ErrorResponseBody {
129                            errors: vec![Error {
130                                field: None,
131                                reason: ErrorReason::Other(format!("respond failed, err:{err}")),
132                            }],
133                        })
134                        .into_response();
135
136                        *resp.status_mut() = StatusCode::from_u16(599).expect("Never");
137                        resp
138                    }
139                },
140            }
141        })
142    }
143}