3 use std
::collections
::HashMap
;
6 use std
::task
::{Context, Poll}
;
9 use hyper
::{Body, Request, Response, StatusCode}
;
11 use proxmox
::api
::{ApiResponseFuture, HttpError, Router, RpcEnvironment}
;
12 use proxmox
::http_err
;
15 use crate::server
::formatter
::*;
16 use crate::server
::WorkerTask
;
18 /// Hyper Service implementation to handle stateful H2 connections.
20 /// We use this kind of service to handle backup protocol
21 /// connections. State is stored inside the generic ``rpcenv``. Logs
22 /// goes into the ``WorkerTask`` log.
23 pub struct H2Service
<E
> {
24 router
: &'
static Router
,
26 worker
: Arc
<WorkerTask
>,
30 impl <E
: RpcEnvironment
+ Clone
> H2Service
<E
> {
32 pub fn new(rpcenv
: E
, worker
: Arc
<WorkerTask
>, router
: &'
static Router
, debug
: bool
) -> Self {
33 Self { rpcenv, worker, router, debug }
36 pub fn debug
<S
: AsRef
<str>>(&self, msg
: S
) {
37 if self.debug { self.worker.log(msg); }
40 fn handle_request(&self, req
: Request
<Body
>) -> ApiResponseFuture
{
42 let (parts
, body
) = req
.into_parts();
44 let method
= parts
.method
.clone();
46 let (path
, components
) = match tools
::normalize_uri_path(parts
.uri
.path()) {
48 Err(err
) => return future
::err(http_err
!(BAD_REQUEST
, "{}", err
)).boxed(),
51 self.debug(format
!("{} {}", method
, path
));
53 let mut uri_param
= HashMap
::new();
55 let formatter
= &JSON_FORMATTER
;
57 match self.router
.find_method(&components
, method
, &mut uri_param
) {
59 let err
= http_err
!(NOT_FOUND
, "Path '{}' not found.", path
);
60 future
::ok((formatter
.format_error
)(err
)).boxed()
63 crate::server
::rest
::handle_api_request(
64 self.rpcenv
.clone(), api_method
, formatter
, parts
, body
, uri_param
).boxed()
69 fn log_response(worker
: Arc
<WorkerTask
>, method
: hyper
::Method
, path
: &str, resp
: &Response
<Body
>) {
71 let status
= resp
.status();
73 if !status
.is_success() {
74 let reason
= status
.canonical_reason().unwrap_or("unknown reason");
76 let mut message
= "request failed";
77 if let Some(data
) = resp
.extensions().get
::<ErrorMessageExtension
>() {
81 worker
.log(format
!("{} {}: {} {}: {}", method
.as_str(), path
, status
.as_str(), reason
, message
));
86 impl <E
: RpcEnvironment
+ Clone
> tower_service
::Service
<Request
<Body
>> for H2Service
<E
> {
87 type Response
= Response
<Body
>;
89 #[allow(clippy::type_complexity)]
90 type Future
= Pin
<Box
<dyn Future
<Output
= Result
<Self::Response
, Self::Error
>> + Send
>>;
92 fn poll_ready(&mut self, _cx
: &mut Context
) -> Poll
<Result
<(), Self::Error
>> {
96 fn call(&mut self, req
: Request
<Body
>) -> Self::Future
{
97 let path
= req
.uri().path().to_owned();
98 let method
= req
.method().clone();
99 let worker
= self.worker
.clone();
101 self.handle_request(req
)
102 .map(move |result
| match result
{
104 Self::log_response(worker
, method
, &path
, &res
);
108 if let Some(apierr
) = err
.downcast_ref
::<HttpError
>() {
109 let mut resp
= Response
::new(Body
::from(apierr
.message
.clone()));
110 resp
.extensions_mut().insert(ErrorMessageExtension(apierr
.message
.clone()));
111 *resp
.status_mut() = apierr
.code
;
112 Self::log_response(worker
, method
, &path
, &resp
);
115 let mut resp
= Response
::new(Body
::from(err
.to_string()));
116 resp
.extensions_mut().insert(ErrorMessageExtension(err
.to_string()));
117 *resp
.status_mut() = StatusCode
::BAD_REQUEST
;
118 Self::log_response(worker
, method
, &path
, &resp
);