product_os_router/
dual_protocol.rs1use std::prelude::v1::*;
6
7use std::fmt::{self, Debug, Formatter};
8use std::future::Future;
9use std::pin::Pin;
10use std::task::{Context, Poll};
11
12use pin_project::pin_project;
13use product_os_http::header::{HOST, LOCATION, UPGRADE};
14use product_os_http::uri::{Authority, Scheme};
15use product_os_http::{HeaderValue, Request, Response, StatusCode, Uri};
16use product_os_http_body::Bytes;
17use product_os_http_body::{Either, Empty};
18use tower_layer::Layer;
19use tower_service::Service as TowerService;
20
21#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
25pub enum Protocol {
26 Tls,
28 Plain,
30}
31
32#[derive(Clone, Copy, Debug)]
47pub struct UpgradeHttpLayer;
48
49impl<Service> Layer<Service> for UpgradeHttpLayer {
50 type Service = UpgradeHttp<Service>;
51
52 fn layer(&self, inner: Service) -> Self::Service {
53 UpgradeHttp::new(inner)
54 }
55}
56
57#[derive(Clone, Debug)]
66pub struct UpgradeHttp<Service> {
67 service: Service,
69}
70
71impl<Service> UpgradeHttp<Service> {
72 pub const fn new(service: Service) -> Self {
74 Self { service }
75 }
76
77 pub fn into_inner(self) -> Service {
80 self.service
81 }
82
83 pub const fn get_ref(&self) -> &Service {
85 &self.service
86 }
87
88 pub fn get_mut(&mut self) -> &mut Service {
90 &mut self.service
91 }
92}
93
94impl<Service, RequestBody, ResponseBody> TowerService<Request<RequestBody>> for UpgradeHttp<Service>
95where
96 Service: TowerService<Request<RequestBody>, Response = Response<ResponseBody>>,
97{
98 type Response = Response<Either<ResponseBody, Empty<Bytes>>>;
99 type Error = Service::Error;
100 type Future = UpgradeHttpFuture<Service, Request<RequestBody>>;
101
102 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
103 self.service.poll_ready(cx)
104 }
105
106 fn call(&mut self, req: Request<RequestBody>) -> Self::Future {
107 let protocol = req.extensions().get::<Protocol>().copied();
108
109 match protocol {
110 None => {
111 let response = Response::builder()
115 .status(StatusCode::INTERNAL_SERVER_ERROR)
116 .body(Empty::new())
117 .expect("building empty 500 response");
118 UpgradeHttpFuture::new_upgrade(response)
119 }
120 Some(Protocol::Tls) => UpgradeHttpFuture::new_service(self.service.call(req)),
121 Some(Protocol::Plain) => {
122 let response = Response::builder();
123
124 let response = if let Some((authority, scheme)) =
125 extract_authority(&req).and_then(|authority| {
126 let uri = req.uri();
127
128 if uri.scheme_str() == Some("ws")
133 || req.headers().get(UPGRADE)
134 == Some(&HeaderValue::from_static("websocket"))
135 {
136 Some((
137 authority,
138 Scheme::try_from("wss").expect("ASCII string is valid"),
139 ))
140 }
141 else if uri.scheme() == Some(&Scheme::HTTP) || uri.scheme_str().is_none()
143 {
144 Some((authority, Scheme::HTTPS))
145 }
146 else {
148 None
149 }
150 }) {
151 let mut uri = Uri::builder().scheme(scheme).authority(authority);
153
154 if let Some(path_and_query) = req.uri().path_and_query() {
155 uri = uri.path_and_query(path_and_query.clone());
156 }
157
158 let uri = uri.build().expect("invalid path and query");
159
160 response
161 .status(StatusCode::MOVED_PERMANENTLY)
162 .header(LOCATION, uri.to_string())
163 } else {
164 response.status(StatusCode::BAD_REQUEST)
167 }
168 .body(Empty::new())
169 .expect("invalid header or body");
170
171 UpgradeHttpFuture::new_upgrade(response)
172 }
173 }
174 }
175}
176
177#[pin_project]
179pub struct UpgradeHttpFuture<Service, Request>(#[pin] FutureServe<Service, Request>)
180where
181 Service: TowerService<Request>;
182
183#[derive(Debug)]
185#[pin_project(project = UpgradeHttpFutureProj)]
186enum FutureServe<Service, Request>
187where
188 Service: TowerService<Request>,
189{
190 Service(#[pin] Service::Future),
193 Upgrade(Option<Response<Empty<Bytes>>>),
196}
197
198impl<Service, Request> Debug for UpgradeHttpFuture<Service, Request>
200where
201 Service: TowerService<Request>,
202 FutureServe<Service, Request>: Debug,
203{
204 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
205 formatter
206 .debug_tuple("UpgradeHttpFuture")
207 .field(&self.0)
208 .finish()
209 }
210}
211
212impl<Service, Request> UpgradeHttpFuture<Service, Request>
213where
214 Service: TowerService<Request>,
215{
216 const fn new_service(future: Service::Future) -> Self {
219 Self(FutureServe::Service(future))
220 }
221
222 const fn new_upgrade(response: Response<Empty<Bytes>>) -> Self {
225 Self(FutureServe::Upgrade(Some(response)))
226 }
227}
228
229impl<Service, Request, ResponseBody> Future for UpgradeHttpFuture<Service, Request>
230where
231 Service: TowerService<Request, Response = Response<ResponseBody>>,
232{
233 type Output = Result<Response<Either<ResponseBody, Empty<Bytes>>>, Service::Error>;
234
235 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
236 match self.project().0.project() {
237 UpgradeHttpFutureProj::Service(future) => {
238 future.poll(cx).map_ok(|result| result.map(Either::Left))
239 }
240 UpgradeHttpFutureProj::Upgrade(response) => Poll::Ready(Ok(response
241 .take()
242 .expect("polled again after `Poll::Ready`")
243 .map(Either::Right))),
244 }
245 }
246}
247
248fn extract_authority<Body>(request: &Request<Body>) -> Option<Authority> {
250 const X_FORWARDED_HOST: &str = "x-forwarded-host";
252
253 let headers = request.headers();
254
255 headers
256 .get(X_FORWARDED_HOST)
257 .or_else(|| headers.get(HOST))
258 .and_then(|header| header.to_str().ok())
259 .or_else(|| request.uri().host())
260 .and_then(|host| Authority::try_from(host).ok())
261}