[Add] TCP server to core MTP (HTTP/1.1 & HTTP/2) compatibility
All checks were successful
CI / checks (push) Successful in 5m29s
All checks were successful
CI / checks (push) Successful in 5m29s
This commit is contained in:
parent
04760fd88d
commit
00f0aaeeff
21 changed files with 1627 additions and 677 deletions
100
mtp-webserver/src/http.rs
Normal file
100
mtp-webserver/src/http.rs
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
use crate::{HttpRequest, HttpResponse, Router, WebServerError, WebServerMetrics};
|
||||
use http::StatusCode;
|
||||
use std::{future::Future, sync::Arc, time::Duration};
|
||||
|
||||
pub(crate) async fn dispatch_request(request: HttpRequest, router: &Router) -> HttpResponse {
|
||||
let path = request.uri.path();
|
||||
if let Some(handler) = router.handler(&request.method, path) {
|
||||
return handler(request, HttpResponse::default()).await;
|
||||
}
|
||||
if let Some((handler, params)) = router.pattern_handler(&request.method, path) {
|
||||
return handler(request, HttpResponse::default(), params).await;
|
||||
}
|
||||
if let Some(handler) = router.fallback_handler() {
|
||||
return handler(request, HttpResponse::default()).await;
|
||||
}
|
||||
HttpResponse::new(StatusCode::NOT_FOUND)
|
||||
}
|
||||
|
||||
pub(crate) async fn run_request<F>(
|
||||
path: &str,
|
||||
timeout: Duration,
|
||||
metrics: Option<&Arc<dyn WebServerMetrics>>,
|
||||
future: F,
|
||||
) -> HttpResponse
|
||||
where
|
||||
F: Future<Output = Result<HttpResponse, WebServerError>>,
|
||||
{
|
||||
if let Some(metrics) = metrics {
|
||||
metrics.request_started(path);
|
||||
}
|
||||
let started = std::time::Instant::now();
|
||||
let response = match tokio::time::timeout(timeout, future).await {
|
||||
Ok(Ok(response)) => response,
|
||||
Ok(Err(error)) => {
|
||||
let status = if matches!(error, WebServerError::PayloadTooLarge) {
|
||||
StatusCode::PAYLOAD_TOO_LARGE
|
||||
} else {
|
||||
StatusCode::BAD_REQUEST
|
||||
};
|
||||
if let Some(metrics) = metrics {
|
||||
metrics.error_occurred(&error);
|
||||
}
|
||||
HttpResponse::new(status)
|
||||
}
|
||||
Err(_) => {
|
||||
let error = WebServerError::Http("request handler timed out".into());
|
||||
if let Some(metrics) = metrics {
|
||||
metrics.error_occurred(&error);
|
||||
}
|
||||
HttpResponse::new(StatusCode::REQUEST_TIMEOUT)
|
||||
}
|
||||
};
|
||||
if let Some(metrics) = metrics {
|
||||
metrics.request_completed(path, response.status.as_u16(), started.elapsed());
|
||||
}
|
||||
response
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use http::{Method, Uri};
|
||||
|
||||
fn request(method: Method, uri: &'static str) -> HttpRequest {
|
||||
HttpRequest {
|
||||
method,
|
||||
uri: Uri::from_static(uri),
|
||||
headers: Default::default(),
|
||||
body: None,
|
||||
remote_addr: "127.0.0.1:1".parse().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn shared_dispatch_preserves_precedence_and_ignores_query() {
|
||||
let router = Router::new()
|
||||
.route("/items", |_, response| async move {
|
||||
response.status(StatusCode::ACCEPTED)
|
||||
})
|
||||
.unwrap()
|
||||
.route_pattern("/{name}", |_, response, _| async move {
|
||||
response.status(StatusCode::CREATED)
|
||||
})
|
||||
.unwrap()
|
||||
.fallback(|_, response| async move { response.status(StatusCode::IM_A_TEAPOT) })
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
dispatch_request(request(Method::GET, "/items?q=1"), &router)
|
||||
.await
|
||||
.status,
|
||||
StatusCode::ACCEPTED
|
||||
);
|
||||
assert_eq!(
|
||||
dispatch_request(request(Method::GET, "/other"), &router)
|
||||
.await
|
||||
.status,
|
||||
StatusCode::CREATED
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue