]>
Commit | Line | Data |
---|---|---|
bd1507c4 | 1 | use failure::*; |
bd1507c4 DM |
2 | |
3 | use std::collections::HashMap; | |
4 | use std::sync::Arc; | |
b9b7f7ec | 5 | use std::task::{Context, Poll}; |
bd1507c4 DM |
6 | |
7 | use futures::*; | |
8 | use hyper::{Body, Request, Response, StatusCode}; | |
9 | ||
9ea4bce4 WB |
10 | use proxmox::api::{ApiResponseFuture, HttpError, Router, RpcEnvironment}; |
11 | use proxmox::http_err; | |
3d482025 | 12 | |
bd1507c4 | 13 | use crate::tools; |
bd1507c4 DM |
14 | use crate::server::formatter::*; |
15 | use crate::server::WorkerTask; | |
16 | ||
42a87f7b DM |
17 | /// Hyper Service implementation to handle stateful H2 connections. |
18 | /// | |
19 | /// We use this kind of service to handle backup protocol | |
20 | /// connections. State is stored inside the generic ``rpcenv``. Logs | |
21 | /// goes into the ``WorkerTask`` log. | |
22 | pub struct H2Service<E> { | |
23 | router: &'static Router, | |
24 | rpcenv: E, | |
bd1507c4 | 25 | worker: Arc<WorkerTask>, |
a42d1f55 | 26 | debug: bool, |
bd1507c4 DM |
27 | } |
28 | ||
42a87f7b | 29 | impl <E: RpcEnvironment + Clone> H2Service<E> { |
bd1507c4 | 30 | |
42a87f7b DM |
31 | pub fn new(rpcenv: E, worker: Arc<WorkerTask>, router: &'static Router, debug: bool) -> Self { |
32 | Self { rpcenv, worker, router, debug } | |
a42d1f55 DM |
33 | } |
34 | ||
35 | pub fn debug<S: AsRef<str>>(&self, msg: S) { | |
36 | if self.debug { self.worker.log(msg); } | |
bd1507c4 DM |
37 | } |
38 | ||
bb084b9c | 39 | fn handle_request(&self, req: Request<Body>) -> ApiResponseFuture { |
bd1507c4 DM |
40 | |
41 | let (parts, body) = req.into_parts(); | |
42 | ||
43 | let method = parts.method.clone(); | |
44 | ||
45 | let (path, components) = match tools::normalize_uri_path(parts.uri.path()) { | |
46 | Ok((p,c)) => (p, c), | |
ad51d02a | 47 | Err(err) => return future::err(http_err!(BAD_REQUEST, err.to_string())).boxed(), |
bd1507c4 DM |
48 | }; |
49 | ||
39e60bd6 | 50 | self.debug(format!("{} {}", method, path)); |
bd1507c4 DM |
51 | |
52 | let mut uri_param = HashMap::new(); | |
53 | ||
42a87f7b DM |
54 | let formatter = &JSON_FORMATTER; |
55 | ||
56 | match self.router.find_method(&components, method, &mut uri_param) { | |
255f378a | 57 | None => { |
bd1507c4 | 58 | let err = http_err!(NOT_FOUND, "Path not found.".to_string()); |
ad51d02a | 59 | future::ok((formatter.format_error)(err)).boxed() |
bd1507c4 | 60 | } |
255f378a | 61 | Some(api_method) => { |
70fbac84 DM |
62 | crate::server::rest::handle_api_request( |
63 | self.rpcenv.clone(), api_method, formatter, parts, body, uri_param).boxed() | |
bd1507c4 DM |
64 | } |
65 | } | |
66 | } | |
67 | ||
68 | fn log_response(worker: Arc<WorkerTask>, method: hyper::Method, path: &str, resp: &Response<Body>) { | |
69 | ||
70 | let status = resp.status(); | |
71 | ||
72 | if !status.is_success() { | |
73 | let reason = status.canonical_reason().unwrap_or("unknown reason"); | |
74 | ||
75 | let mut message = "request failed"; | |
76 | if let Some(data) = resp.extensions().get::<ErrorMessageExtension>() { | |
77 | message = &data.0; | |
78 | } | |
79 | ||
80 | worker.log(format!("{} {}: {} {}: {}", method.as_str(), path, status.as_str(), reason, message)); | |
81 | } | |
82 | } | |
83 | } | |
84 | ||
b9b7f7ec WB |
85 | impl <E: RpcEnvironment + Clone> tower_service::Service<Request<Body>> for H2Service<E> { |
86 | type Response = Response<Body>; | |
642322b4 | 87 | type Error = Error; |
b9b7f7ec WB |
88 | type Future = |
89 | std::pin::Pin<Box<dyn Future<Output = Result<Response<Body>, Self::Error>> + Send>>; | |
bd1507c4 | 90 | |
b9b7f7ec WB |
91 | fn poll_ready(&mut self, _cx: &mut Context) -> Poll<Result<(), Self::Error>> { |
92 | Poll::Ready(Ok(())) | |
93 | } | |
94 | ||
95 | fn call(&mut self, req: Request<Body>) -> Self::Future { | |
bd1507c4 DM |
96 | let path = req.uri().path().to_owned(); |
97 | let method = req.method().clone(); | |
98 | let worker = self.worker.clone(); | |
99 | ||
b9b7f7ec WB |
100 | std::pin::Pin::from(self.handle_request(req)) |
101 | .map(move |result| match result { | |
bd1507c4 DM |
102 | Ok(res) => { |
103 | Self::log_response(worker, method, &path, &res); | |
642322b4 | 104 | Ok::<_, Error>(res) |
bd1507c4 DM |
105 | } |
106 | Err(err) => { | |
642322b4 | 107 | if let Some(apierr) = err.downcast_ref::<HttpError>() { |
bd1507c4 | 108 | let mut resp = Response::new(Body::from(apierr.message.clone())); |
907f5bb0 | 109 | resp.extensions_mut().insert(ErrorMessageExtension(apierr.message.clone())); |
bd1507c4 DM |
110 | *resp.status_mut() = apierr.code; |
111 | Self::log_response(worker, method, &path, &resp); | |
112 | Ok(resp) | |
113 | } else { | |
114 | let mut resp = Response::new(Body::from(err.to_string())); | |
907f5bb0 | 115 | resp.extensions_mut().insert(ErrorMessageExtension(err.to_string())); |
bd1507c4 DM |
116 | *resp.status_mut() = StatusCode::BAD_REQUEST; |
117 | Self::log_response(worker, method, &path, &resp); | |
118 | Ok(resp) | |
119 | } | |
120 | } | |
b9b7f7ec WB |
121 | }) |
122 | .boxed() | |
bd1507c4 DM |
123 | } |
124 | } |