Spaces:
Runtime error
Runtime error
| use std::convert::Infallible; | |
| use std::net::{IpAddr, SocketAddr}; | |
| use std::str::FromStr; | |
| use std::sync::Arc; | |
| use std::time::Duration; | |
| use crate::ferron_res::server_software::SERVER_SOFTWARE; | |
| use crate::ferron_util::combine_config::combine_config; | |
| use crate::ferron_util::error_pages::generate_default_error_page; | |
| use crate::ferron_util::url_sanitizer::sanitize_url; | |
| use crate::ferron_common::{ | |
| ErrorLogger, LogMessage, RequestData, ServerModuleHandlers, SocketData, | |
| }; | |
| use async_channel::Sender; | |
| use chrono::prelude::*; | |
| use futures_util::TryStreamExt; | |
| use http::header::CONTENT_TYPE; | |
| use http_body_util::combinators::BoxBody; | |
| use http_body_util::{BodyExt, Empty, Full, StreamBody}; | |
| use hyper::body::{Body, Bytes, Frame}; | |
| use hyper::header::{self, HeaderName, HeaderValue}; | |
| use hyper::{HeaderMap, Method, Request, Response, StatusCode}; | |
| use hyper_tungstenite::is_upgrade_request; | |
| use rustls_acme::ResolvesServerCertAcme; | |
| use tokio::fs; | |
| use tokio::io::BufReader; | |
| use tokio::time::timeout; | |
| use tokio_util::io::ReaderStream; | |
| use yaml_rust2::Yaml; | |
| async fn generate_error_response( | |
| status_code: StatusCode, | |
| config: &Yaml, | |
| headers: &Option<HeaderMap>, | |
| ) -> Response<BoxBody<Bytes, std::io::Error>> { | |
| let bare_body = | |
| generate_default_error_page(status_code, config["serverAdministratorEmail"].as_str()); | |
| let mut content_length: Option<u64> = bare_body.len().try_into().ok(); | |
| let mut response_body = Full::new(Bytes::from(bare_body)) | |
| .map_err(|e| match e {}) | |
| .boxed(); | |
| if let Some(error_pages) = config["errorPages"].as_vec() { | |
| for error_page_yaml in error_pages { | |
| if let Some(page_status_code) = error_page_yaml["scode"].as_i64() { | |
| let page_status_code = match StatusCode::from_u16(match page_status_code.try_into() { | |
| Ok(status_code) => status_code, | |
| Err(_) => continue, | |
| }) { | |
| Ok(status_code) => status_code, | |
| Err(_) => continue, | |
| }; | |
| if status_code != page_status_code { | |
| continue; | |
| } | |
| if let Some(page_path) = error_page_yaml["path"].as_str() { | |
| let file = fs::File::open(page_path).await; | |
| let file = match file { | |
| Ok(file) => file, | |
| Err(_) => continue, | |
| }; | |
| content_length = match file.metadata().await { | |
| Ok(metadata) => Some(metadata.len()), | |
| Err(_) => None, | |
| }; | |
| // Use BufReader for better performance. | |
| let reader_stream = ReaderStream::new(BufReader::with_capacity(12800, file)); | |
| let stream_body = StreamBody::new(reader_stream.map_ok(Frame::data)); | |
| let boxed_body = stream_body.boxed(); | |
| response_body = boxed_body; | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| let mut response_builder = Response::builder().status(status_code); | |
| if let Some(headers) = headers { | |
| let headers_iter = headers.iter(); | |
| for (name, value) in headers_iter { | |
| if name != header::CONTENT_TYPE && name != header::CONTENT_LENGTH { | |
| response_builder = response_builder.header(name, value); | |
| } | |
| } | |
| } | |
| if let Some(content_length) = content_length { | |
| response_builder = response_builder.header(header::CONTENT_LENGTH, content_length); | |
| } | |
| response_builder = response_builder.header(header::CONTENT_TYPE, "text/html"); | |
| response_builder.body(response_body).unwrap_or_default() | |
| } | |
| async fn log_combined( | |
| logger: &Sender<LogMessage>, | |
| client_ip: IpAddr, | |
| auth_user: Option<String>, | |
| method: String, | |
| request_path: String, | |
| protocol: String, | |
| status_code: u16, | |
| content_length: Option<u64>, | |
| referrer: Option<String>, | |
| user_agent: Option<String>, | |
| ) { | |
| let now: DateTime<Local> = Local::now(); | |
| let formatted_time = now.format("%d/%b/%Y:%H:%M:%S %z").to_string(); | |
| logger | |
| .send(LogMessage::new( | |
| format!( | |
| "{} - {} [{}] \"{} {} {}\" {} {} {} {}", | |
| client_ip, | |
| match auth_user { | |
| Some(auth_user) => auth_user, | |
| None => String::from("-"), | |
| }, | |
| formatted_time, | |
| method, | |
| request_path, | |
| protocol, | |
| status_code, | |
| match content_length { | |
| Some(content_length) => format!("{}", content_length), | |
| None => String::from("-"), | |
| }, | |
| match referrer { | |
| Some(referrer) => format!( | |
| "\"{}\"", | |
| referrer.replace("\\", "\\\\").replace("\"", "\\\"") | |
| ), | |
| None => String::from("-"), | |
| }, | |
| match user_agent { | |
| Some(user_agent) => format!( | |
| "\"{}\"", | |
| user_agent.replace("\\", "\\\\").replace("\"", "\\\"") | |
| ), | |
| None => String::from("-"), | |
| }, | |
| ), | |
| false, | |
| )) | |
| .await | |
| .unwrap_or_default(); | |
| } | |
| async fn request_handler_wrapped( | |
| mut request: Request<BoxBody<Bytes, std::io::Error>>, | |
| remote_address: SocketAddr, | |
| local_address: SocketAddr, | |
| encrypted: bool, | |
| config: Arc<Yaml>, | |
| logger: Sender<LogMessage>, | |
| handlers_vec: Vec<Box<dyn ServerModuleHandlers + Send>>, | |
| acme_http01_resolver_option: Option<Arc<ResolvesServerCertAcme>>, | |
| http3_alt_port: Option<u16>, | |
| ) -> Result<Response<BoxBody<Bytes, std::io::Error>>, Infallible> { | |
| let is_proxy_request = match request.version() { | |
| hyper::Version::HTTP_2 | hyper::Version::HTTP_3 => { | |
| request.method() == hyper::Method::CONNECT && request.uri().host().is_some() | |
| } | |
| _ => request.uri().host().is_some(), | |
| }; | |
| let is_connect_proxy_request = request.method() == hyper::Method::CONNECT; | |
| // Collect request data for logging | |
| let log_method = String::from(request.method().as_str()); | |
| let log_request_path = match is_proxy_request { | |
| true => request.uri().to_string(), | |
| false => format!( | |
| "{}{}", | |
| request.uri().path(), | |
| match request.uri().query() { | |
| Some(query) => format!("?{}", query), | |
| None => String::from(""), | |
| } | |
| ), | |
| }; | |
| let log_protocol = String::from(match request.version() { | |
| hyper::Version::HTTP_09 => "HTTP/0.9", | |
| hyper::Version::HTTP_10 => "HTTP/1.0", | |
| hyper::Version::HTTP_11 => "HTTP/1.1", | |
| hyper::Version::HTTP_2 => "HTTP/2.0", | |
| hyper::Version::HTTP_3 => "HTTP/3.0", | |
| _ => "HTTP/Unknown", | |
| }); | |
| let log_referrer = match request.headers().get(header::REFERER) { | |
| Some(header_value) => match header_value.to_str() { | |
| Ok(header_value) => Some(String::from(header_value)), | |
| Err(_) => None, | |
| }, | |
| None => None, | |
| }; | |
| let log_user_agent = match request.headers().get(header::USER_AGENT) { | |
| Some(header_value) => match header_value.to_str() { | |
| Ok(header_value) => Some(String::from(header_value)), | |
| Err(_) => None, | |
| }, | |
| None => None, | |
| }; | |
| let log_enabled = config["global"]["logFilePath"].as_str().is_some(); | |
| let error_log_enabled = config["global"]["errorLogFilePath"].as_str().is_some(); | |
| // Construct SocketData | |
| let mut socket_data = SocketData::new(remote_address, local_address, encrypted); | |
| match request.version() { | |
| hyper::Version::HTTP_2 | hyper::Version::HTTP_3 => { | |
| // Set "Host" request header for HTTP/2 and HTTP/3 connections | |
| if let Some(authority) = request.uri().authority() { | |
| let authority = authority.to_owned(); | |
| let headers = request.headers_mut(); | |
| if !headers.contains_key(header::HOST) { | |
| if let Ok(authority_value) = HeaderValue::from_bytes(authority.as_str().as_bytes()) { | |
| headers.append(header::HOST, authority_value); | |
| } | |
| } | |
| } | |
| // Normalize the Cookie header for HTTP/2 and HTTP/3 | |
| let mut cookie_normalized = String::new(); | |
| let mut cookie_set = false; | |
| let headers = request.headers_mut(); | |
| for cookie in headers.get_all(header::COOKIE) { | |
| if let Ok(cookie) = cookie.to_str() { | |
| if cookie_set { | |
| cookie_normalized.push_str("; "); | |
| } | |
| cookie_set = true; | |
| cookie_normalized.push_str(cookie); | |
| } | |
| } | |
| if cookie_set { | |
| if let Ok(cookie_value) = HeaderValue::from_bytes(cookie_normalized.as_bytes()) { | |
| headers.insert(header::COOKIE, cookie_value); | |
| } | |
| } | |
| } | |
| _ => (), | |
| } | |
| let host_header_option = request.headers().get(header::HOST); | |
| if let Some(header_data) = host_header_option { | |
| match header_data.to_str() { | |
| Ok(host_header) => { | |
| let host_header_lower_case = host_header.to_lowercase(); | |
| if host_header_lower_case != *host_header { | |
| let host_header_value = match HeaderValue::from_str(&host_header_lower_case) { | |
| Ok(host_header_value) => host_header_value, | |
| Err(err) => { | |
| if error_log_enabled { | |
| logger | |
| .send(LogMessage::new( | |
| format!("Host header sanitation error: {}", err), | |
| true, | |
| )) | |
| .await | |
| .unwrap_or_default(); | |
| } | |
| let response = Response::builder() | |
| .status(StatusCode::BAD_REQUEST) | |
| .header(header::CONTENT_TYPE, "text/html") | |
| .body( | |
| Full::new(Bytes::from(generate_default_error_page( | |
| StatusCode::BAD_REQUEST, | |
| None, | |
| ))) | |
| .map_err(|e| match e {}) | |
| .boxed(), | |
| ) | |
| .unwrap_or_default(); | |
| if log_enabled { | |
| log_combined( | |
| &logger, | |
| socket_data.remote_addr.ip(), | |
| None, | |
| log_method, | |
| log_request_path, | |
| log_protocol, | |
| response.status().as_u16(), | |
| match response.headers().get(header::CONTENT_LENGTH) { | |
| Some(header_value) => match header_value.to_str() { | |
| Ok(header_value) => match header_value.parse::<u64>() { | |
| Ok(content_length) => Some(content_length), | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| None => response.body().size_hint().exact(), | |
| }, | |
| log_referrer, | |
| log_user_agent, | |
| ) | |
| .await; | |
| } | |
| let (mut response_parts, response_body) = response.into_parts(); | |
| if let Some(http3_alt_port) = http3_alt_port { | |
| if let Ok(header_value) = match response_parts.headers.get(header::ALT_SVC) { | |
| Some(value) => HeaderValue::from_bytes( | |
| format!( | |
| "{}, h3=\":{}\", h3-29=\":{}\"", | |
| String::from_utf8_lossy(value.as_bytes()), | |
| http3_alt_port, | |
| http3_alt_port | |
| ) | |
| .as_bytes(), | |
| ), | |
| None => HeaderValue::from_bytes( | |
| format!("h3=\":{}\", h3-29=\":{}\"", http3_alt_port, http3_alt_port).as_bytes(), | |
| ), | |
| } { | |
| response_parts.headers.insert(header::ALT_SVC, header_value); | |
| } | |
| } | |
| response_parts | |
| .headers | |
| .insert(header::SERVER, HeaderValue::from_static(SERVER_SOFTWARE)); | |
| return Ok(Response::from_parts(response_parts, response_body)); | |
| } | |
| }; | |
| request | |
| .headers_mut() | |
| .insert(header::HOST, host_header_value); | |
| } | |
| } | |
| Err(err) => { | |
| if error_log_enabled { | |
| logger | |
| .send(LogMessage::new( | |
| format!("Host header sanitation error: {}", err), | |
| true, | |
| )) | |
| .await | |
| .unwrap_or_default(); | |
| } | |
| let response = Response::builder() | |
| .status(StatusCode::BAD_REQUEST) | |
| .header(header::CONTENT_TYPE, "text/html") | |
| .body( | |
| Full::new(Bytes::from(generate_default_error_page( | |
| StatusCode::BAD_REQUEST, | |
| None, | |
| ))) | |
| .map_err(|e| match e {}) | |
| .boxed(), | |
| ) | |
| .unwrap_or_default(); | |
| if log_enabled { | |
| log_combined( | |
| &logger, | |
| socket_data.remote_addr.ip(), | |
| None, | |
| log_method, | |
| log_request_path, | |
| log_protocol, | |
| response.status().as_u16(), | |
| match response.headers().get(header::CONTENT_LENGTH) { | |
| Some(header_value) => match header_value.to_str() { | |
| Ok(header_value) => match header_value.parse::<u64>() { | |
| Ok(content_length) => Some(content_length), | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| None => response.body().size_hint().exact(), | |
| }, | |
| log_referrer, | |
| log_user_agent, | |
| ) | |
| .await; | |
| } | |
| let (mut response_parts, response_body) = response.into_parts(); | |
| if let Some(http3_alt_port) = http3_alt_port { | |
| if let Ok(header_value) = match response_parts.headers.get(header::ALT_SVC) { | |
| Some(value) => HeaderValue::from_bytes( | |
| format!( | |
| "{}, h3=\":{}\", h3-29=\":{}\"", | |
| String::from_utf8_lossy(value.as_bytes()), | |
| http3_alt_port, | |
| http3_alt_port | |
| ) | |
| .as_bytes(), | |
| ), | |
| None => HeaderValue::from_bytes( | |
| format!("h3=\":{}\", h3-29=\":{}\"", http3_alt_port, http3_alt_port).as_bytes(), | |
| ), | |
| } { | |
| response_parts.headers.insert(header::ALT_SVC, header_value); | |
| } | |
| } | |
| response_parts | |
| .headers | |
| .insert(header::SERVER, HeaderValue::from_static(SERVER_SOFTWARE)); | |
| return Ok(Response::from_parts(response_parts, response_body)); | |
| } | |
| } | |
| }; | |
| // Combine the server configuration | |
| let combined_config = match combine_config( | |
| config, | |
| match is_proxy_request || is_connect_proxy_request { | |
| false => match request.headers().get(header::HOST) { | |
| Some(value) => value.to_str().ok(), | |
| None => None, | |
| }, | |
| true => None, | |
| }, | |
| local_address.ip(), | |
| request.uri().path(), | |
| ) { | |
| Some(config) => config, | |
| None => { | |
| if error_log_enabled { | |
| logger | |
| .send(LogMessage::new( | |
| String::from("Cannot determine server configuration"), | |
| true, | |
| )) | |
| .await | |
| .unwrap_or_default(); | |
| } | |
| let response = Response::builder() | |
| .status(StatusCode::INTERNAL_SERVER_ERROR) | |
| .header(header::CONTENT_TYPE, "text/html") | |
| .body( | |
| Full::new(Bytes::from(generate_default_error_page( | |
| StatusCode::INTERNAL_SERVER_ERROR, | |
| None, | |
| ))) | |
| .map_err(|e| match e {}) | |
| .boxed(), | |
| ) | |
| .unwrap_or_default(); | |
| if log_enabled { | |
| log_combined( | |
| &logger, | |
| socket_data.remote_addr.ip(), | |
| None, | |
| log_method, | |
| log_request_path, | |
| log_protocol, | |
| response.status().as_u16(), | |
| match response.headers().get(header::CONTENT_LENGTH) { | |
| Some(header_value) => match header_value.to_str() { | |
| Ok(header_value) => match header_value.parse::<u64>() { | |
| Ok(content_length) => Some(content_length), | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| None => response.body().size_hint().exact(), | |
| }, | |
| log_referrer, | |
| log_user_agent, | |
| ) | |
| .await; | |
| } | |
| let (mut response_parts, response_body) = response.into_parts(); | |
| if let Some(http3_alt_port) = http3_alt_port { | |
| if let Ok(header_value) = match response_parts.headers.get(header::ALT_SVC) { | |
| Some(value) => HeaderValue::from_bytes( | |
| format!( | |
| "{}, h3=\":{}\", h3-29=\":{}\"", | |
| String::from_utf8_lossy(value.as_bytes()), | |
| http3_alt_port, | |
| http3_alt_port | |
| ) | |
| .as_bytes(), | |
| ), | |
| None => HeaderValue::from_bytes( | |
| format!("h3=\":{}\", h3-29=\":{}\"", http3_alt_port, http3_alt_port).as_bytes(), | |
| ), | |
| } { | |
| response_parts.headers.insert(header::ALT_SVC, header_value); | |
| } | |
| } | |
| response_parts | |
| .headers | |
| .insert(header::SERVER, HeaderValue::from_static(SERVER_SOFTWARE)); | |
| return Ok(Response::from_parts(response_parts, response_body)); | |
| } | |
| }; | |
| let url_pathname = request.uri().path(); | |
| let sanitized_url_pathname = match sanitize_url( | |
| url_pathname, | |
| combined_config["allowDoubleSlashes"] | |
| .as_bool() | |
| .unwrap_or_default(), | |
| ) { | |
| Ok(sanitized_url) => sanitized_url, | |
| Err(err) => { | |
| if error_log_enabled { | |
| logger | |
| .send(LogMessage::new( | |
| format!("URL sanitation error: {}", err), | |
| true, | |
| )) | |
| .await | |
| .unwrap_or_default(); | |
| } | |
| let response = | |
| generate_error_response(StatusCode::BAD_REQUEST, &combined_config, &None).await; | |
| if log_enabled { | |
| log_combined( | |
| &logger, | |
| socket_data.remote_addr.ip(), | |
| None, | |
| log_method, | |
| log_request_path, | |
| log_protocol, | |
| response.status().as_u16(), | |
| match response.headers().get(header::CONTENT_LENGTH) { | |
| Some(header_value) => match header_value.to_str() { | |
| Ok(header_value) => match header_value.parse::<u64>() { | |
| Ok(content_length) => Some(content_length), | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| None => response.body().size_hint().exact(), | |
| }, | |
| log_referrer, | |
| log_user_agent, | |
| ) | |
| .await; | |
| } | |
| let (mut response_parts, response_body) = response.into_parts(); | |
| if let Some(custom_headers_hash) = combined_config["customHeaders"].as_hash() { | |
| let custom_headers_hash_iter = custom_headers_hash.iter(); | |
| for (header_name, header_value) in custom_headers_hash_iter { | |
| if let Some(header_name) = header_name.as_str() { | |
| if let Some(header_value) = header_value.as_str() { | |
| if !response_parts.headers.contains_key(header_name) { | |
| if let Ok(header_value) = | |
| HeaderValue::from_str(&header_value.replace("{path}", url_pathname)) | |
| { | |
| if let Ok(header_name) = HeaderName::from_str(header_name) { | |
| response_parts.headers.insert(header_name, header_value); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if let Some(http3_alt_port) = http3_alt_port { | |
| if let Ok(header_value) = match response_parts.headers.get(header::ALT_SVC) { | |
| Some(value) => HeaderValue::from_bytes( | |
| format!( | |
| "{}, h3=\":{}\", h3-29=\":{}\"", | |
| String::from_utf8_lossy(value.as_bytes()), | |
| http3_alt_port, | |
| http3_alt_port | |
| ) | |
| .as_bytes(), | |
| ), | |
| None => HeaderValue::from_bytes( | |
| format!("h3=\":{}\", h3-29=\":{}\"", http3_alt_port, http3_alt_port).as_bytes(), | |
| ), | |
| } { | |
| response_parts.headers.insert(header::ALT_SVC, header_value); | |
| } | |
| } | |
| response_parts | |
| .headers | |
| .insert(header::SERVER, HeaderValue::from_static(SERVER_SOFTWARE)); | |
| return Ok(Response::from_parts(response_parts, response_body)); | |
| } | |
| }; | |
| if sanitized_url_pathname != url_pathname { | |
| let (mut parts, body) = request.into_parts(); | |
| let mut url_parts = parts.uri.into_parts(); | |
| url_parts.path_and_query = Some( | |
| match format!( | |
| "{}{}", | |
| sanitized_url_pathname, | |
| match url_parts.path_and_query { | |
| Some(path_and_query) => { | |
| match path_and_query.query() { | |
| Some(query) => format!("?{}", query), | |
| None => String::from(""), | |
| } | |
| } | |
| None => String::from(""), | |
| } | |
| ) | |
| .parse() | |
| { | |
| Ok(path_and_query) => path_and_query, | |
| Err(err) => { | |
| if error_log_enabled { | |
| logger | |
| .send(LogMessage::new( | |
| format!("URL sanitation error: {}", err), | |
| true, | |
| )) | |
| .await | |
| .unwrap_or_default(); | |
| } | |
| let response = | |
| generate_error_response(StatusCode::BAD_REQUEST, &combined_config, &None).await; | |
| if log_enabled { | |
| log_combined( | |
| &logger, | |
| socket_data.remote_addr.ip(), | |
| None, | |
| log_method, | |
| log_request_path, | |
| log_protocol, | |
| response.status().as_u16(), | |
| match response.headers().get(header::CONTENT_LENGTH) { | |
| Some(header_value) => match header_value.to_str() { | |
| Ok(header_value) => match header_value.parse::<u64>() { | |
| Ok(content_length) => Some(content_length), | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| None => response.body().size_hint().exact(), | |
| }, | |
| log_referrer, | |
| log_user_agent, | |
| ) | |
| .await; | |
| } | |
| let (mut response_parts, response_body) = response.into_parts(); | |
| if let Some(custom_headers_hash) = combined_config["customHeaders"].as_hash() { | |
| let custom_headers_hash_iter = custom_headers_hash.iter(); | |
| for (header_name, header_value) in custom_headers_hash_iter { | |
| if let Some(header_name) = header_name.as_str() { | |
| if let Some(header_value) = header_value.as_str() { | |
| if !response_parts.headers.contains_key(header_name) { | |
| if let Ok(header_value) = HeaderValue::from_str( | |
| &header_value.replace("{path}", &sanitized_url_pathname), | |
| ) { | |
| if let Ok(header_name) = HeaderName::from_str(header_name) { | |
| response_parts.headers.insert(header_name, header_value); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if let Some(http3_alt_port) = http3_alt_port { | |
| if let Ok(header_value) = match response_parts.headers.get(header::ALT_SVC) { | |
| Some(value) => HeaderValue::from_bytes( | |
| format!( | |
| "{}, h3=\":{}\", h3-29=\":{}\"", | |
| String::from_utf8_lossy(value.as_bytes()), | |
| http3_alt_port, | |
| http3_alt_port | |
| ) | |
| .as_bytes(), | |
| ), | |
| None => HeaderValue::from_bytes( | |
| format!("h3=\":{}\", h3-29=\":{}\"", http3_alt_port, http3_alt_port).as_bytes(), | |
| ), | |
| } { | |
| response_parts.headers.insert(header::ALT_SVC, header_value); | |
| } | |
| } | |
| response_parts | |
| .headers | |
| .insert(header::SERVER, HeaderValue::from_static(SERVER_SOFTWARE)); | |
| return Ok(Response::from_parts(response_parts, response_body)); | |
| } | |
| }, | |
| ); | |
| parts.uri = match hyper::Uri::from_parts(url_parts) { | |
| Ok(uri) => uri, | |
| Err(err) => { | |
| if error_log_enabled { | |
| logger | |
| .send(LogMessage::new( | |
| format!("URL sanitation error: {}", err), | |
| true, | |
| )) | |
| .await | |
| .unwrap_or_default(); | |
| } | |
| let response = | |
| generate_error_response(StatusCode::BAD_REQUEST, &combined_config, &None).await; | |
| if log_enabled { | |
| log_combined( | |
| &logger, | |
| socket_data.remote_addr.ip(), | |
| None, | |
| log_method, | |
| log_request_path, | |
| log_protocol, | |
| response.status().as_u16(), | |
| match response.headers().get(header::CONTENT_LENGTH) { | |
| Some(header_value) => match header_value.to_str() { | |
| Ok(header_value) => match header_value.parse::<u64>() { | |
| Ok(content_length) => Some(content_length), | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| None => response.body().size_hint().exact(), | |
| }, | |
| log_referrer, | |
| log_user_agent, | |
| ) | |
| .await; | |
| } | |
| let (mut response_parts, response_body) = response.into_parts(); | |
| if let Some(custom_headers_hash) = combined_config["customHeaders"].as_hash() { | |
| let custom_headers_hash_iter = custom_headers_hash.iter(); | |
| for (header_name, header_value) in custom_headers_hash_iter { | |
| if let Some(header_name) = header_name.as_str() { | |
| if let Some(header_value) = header_value.as_str() { | |
| if !response_parts.headers.contains_key(header_name) { | |
| if let Ok(header_value) = | |
| HeaderValue::from_str(&header_value.replace("{path}", &sanitized_url_pathname)) | |
| { | |
| if let Ok(header_name) = HeaderName::from_str(header_name) { | |
| response_parts.headers.insert(header_name, header_value); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if let Some(http3_alt_port) = http3_alt_port { | |
| if let Ok(header_value) = match response_parts.headers.get(header::ALT_SVC) { | |
| Some(value) => HeaderValue::from_bytes( | |
| format!( | |
| "{}, h3=\":{}\", h3-29=\":{}\"", | |
| String::from_utf8_lossy(value.as_bytes()), | |
| http3_alt_port, | |
| http3_alt_port | |
| ) | |
| .as_bytes(), | |
| ), | |
| None => HeaderValue::from_bytes( | |
| format!("h3=\":{}\", h3-29=\":{}\"", http3_alt_port, http3_alt_port).as_bytes(), | |
| ), | |
| } { | |
| response_parts.headers.insert(header::ALT_SVC, header_value); | |
| } | |
| } | |
| response_parts | |
| .headers | |
| .insert(header::SERVER, HeaderValue::from_static(SERVER_SOFTWARE)); | |
| return Ok(Response::from_parts(response_parts, response_body)); | |
| } | |
| }; | |
| request = Request::from_parts(parts, body); | |
| } | |
| if request.uri().path() == "*" { | |
| let response = match request.method() { | |
| &Method::OPTIONS => Response::builder() | |
| .status(StatusCode::NO_CONTENT) | |
| .header(header::ALLOW, "GET, POST, HEAD, OPTIONS") | |
| .body(Empty::new().map_err(|e| match e {}).boxed()) | |
| .unwrap_or_default(), | |
| _ => { | |
| let mut header_map = HeaderMap::new(); | |
| if let Ok(header_value) = HeaderValue::from_str("GET, POST, HEAD, OPTIONS") { | |
| header_map.insert(header::ALLOW, header_value); | |
| }; | |
| generate_error_response(StatusCode::BAD_REQUEST, &combined_config, &Some(header_map)).await | |
| } | |
| }; | |
| if log_enabled { | |
| log_combined( | |
| &logger, | |
| socket_data.remote_addr.ip(), | |
| None, | |
| log_method, | |
| log_request_path, | |
| log_protocol, | |
| response.status().as_u16(), | |
| match response.headers().get(header::CONTENT_LENGTH) { | |
| Some(header_value) => match header_value.to_str() { | |
| Ok(header_value) => match header_value.parse::<u64>() { | |
| Ok(content_length) => Some(content_length), | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| None => response.body().size_hint().exact(), | |
| }, | |
| log_referrer, | |
| log_user_agent, | |
| ) | |
| .await; | |
| } | |
| let (mut response_parts, response_body) = response.into_parts(); | |
| if let Some(custom_headers_hash) = combined_config["customHeaders"].as_hash() { | |
| let custom_headers_hash_iter = custom_headers_hash.iter(); | |
| for (header_name, header_value) in custom_headers_hash_iter { | |
| if let Some(header_name) = header_name.as_str() { | |
| if let Some(header_value) = header_value.as_str() { | |
| if !response_parts.headers.contains_key(header_name) { | |
| if let Ok(header_value) = | |
| HeaderValue::from_str(&header_value.replace("{path}", &sanitized_url_pathname)) | |
| { | |
| if let Ok(header_name) = HeaderName::from_str(header_name) { | |
| response_parts.headers.insert(header_name, header_value); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if let Some(http3_alt_port) = http3_alt_port { | |
| if let Ok(header_value) = match response_parts.headers.get(header::ALT_SVC) { | |
| Some(value) => HeaderValue::from_bytes( | |
| format!( | |
| "{}, h3=\":{}\", h3-29=\":{}\"", | |
| String::from_utf8_lossy(value.as_bytes()), | |
| http3_alt_port, | |
| http3_alt_port | |
| ) | |
| .as_bytes(), | |
| ), | |
| None => HeaderValue::from_bytes( | |
| format!("h3=\":{}\", h3-29=\":{}\"", http3_alt_port, http3_alt_port).as_bytes(), | |
| ), | |
| } { | |
| response_parts.headers.insert(header::ALT_SVC, header_value); | |
| } | |
| } | |
| response_parts | |
| .headers | |
| .insert(header::SERVER, HeaderValue::from_static(SERVER_SOFTWARE)); | |
| return Ok(Response::from_parts(response_parts, response_body)); | |
| } | |
| // HTTP-01 ACME challenge for automatic TLS | |
| if let Some(acme_http01_resolver) = acme_http01_resolver_option { | |
| if let Some(challenge_token) = request | |
| .uri() | |
| .path() | |
| .strip_prefix("/.well-known/acme-challenge/") | |
| { | |
| if let Some(acme_response) = acme_http01_resolver.get_http_01_key_auth(challenge_token) { | |
| let response = Response::builder() | |
| .status(StatusCode::OK) | |
| .header( | |
| CONTENT_TYPE, | |
| HeaderValue::from_static("application/octet-stream"), | |
| ) | |
| .body( | |
| Full::new(Bytes::from(acme_response)) | |
| .map_err(|e| match e {}) | |
| .boxed(), | |
| ) | |
| .unwrap_or_default(); | |
| let (mut response_parts, response_body) = response.into_parts(); | |
| if let Some(custom_headers_hash) = combined_config["customHeaders"].as_hash() { | |
| let custom_headers_hash_iter = custom_headers_hash.iter(); | |
| for (header_name, header_value) in custom_headers_hash_iter { | |
| if let Some(header_name) = header_name.as_str() { | |
| if let Some(header_value) = header_value.as_str() { | |
| if !response_parts.headers.contains_key(header_name) { | |
| if let Ok(header_value) = | |
| HeaderValue::from_str(&header_value.replace("{path}", &sanitized_url_pathname)) | |
| { | |
| if let Ok(header_name) = HeaderName::from_str(header_name) { | |
| response_parts.headers.insert(header_name, header_value); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if let Some(http3_alt_port) = http3_alt_port { | |
| if let Ok(header_value) = match response_parts.headers.get(header::ALT_SVC) { | |
| Some(value) => HeaderValue::from_bytes( | |
| format!( | |
| "{}, h3=\":{}\", h3-29=\":{}\"", | |
| String::from_utf8_lossy(value.as_bytes()), | |
| http3_alt_port, | |
| http3_alt_port | |
| ) | |
| .as_bytes(), | |
| ), | |
| None => HeaderValue::from_bytes( | |
| format!("h3=\":{}\", h3-29=\":{}\"", http3_alt_port, http3_alt_port).as_bytes(), | |
| ), | |
| } { | |
| response_parts.headers.insert(header::ALT_SVC, header_value); | |
| } | |
| } | |
| response_parts | |
| .headers | |
| .insert(header::SERVER, HeaderValue::from_static(SERVER_SOFTWARE)); | |
| let response = Response::from_parts(response_parts, response_body); | |
| if log_enabled { | |
| log_combined( | |
| &logger, | |
| socket_data.remote_addr.ip(), | |
| None, | |
| log_method, | |
| log_request_path, | |
| log_protocol, | |
| response.status().as_u16(), | |
| match response.headers().get(header::CONTENT_LENGTH) { | |
| Some(header_value) => match header_value.to_str() { | |
| Ok(header_value) => match header_value.parse::<u64>() { | |
| Ok(content_length) => Some(content_length), | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| None => response.body().size_hint().exact(), | |
| }, | |
| log_referrer, | |
| log_user_agent, | |
| ) | |
| .await; | |
| } | |
| return Ok(response); | |
| } | |
| } | |
| }; | |
| let cloned_logger = logger.clone(); | |
| let error_logger = match error_log_enabled { | |
| true => ErrorLogger::new(cloned_logger), | |
| false => ErrorLogger::without_logger(), | |
| }; | |
| if is_connect_proxy_request { | |
| let mut connect_proxy_handlers = None; | |
| for mut handlers in handlers_vec { | |
| if handlers.does_connect_proxy_requests() { | |
| connect_proxy_handlers = Some(handlers); | |
| break; | |
| } | |
| } | |
| if let Some(mut connect_proxy_handlers) = connect_proxy_handlers { | |
| if let Some(connect_address) = request.uri().authority().map(|auth| auth.to_string()) { | |
| // Variables moved to before "tokio::spawn" to avoid issues with moved values | |
| let client_ip = socket_data.remote_addr.ip(); | |
| let custom_headers_yaml = combined_config["customHeaders"].clone(); | |
| tokio::spawn(async move { | |
| match hyper::upgrade::on(request).await { | |
| Ok(upgraded_request) => { | |
| let result = connect_proxy_handlers | |
| .connect_proxy_request_handler( | |
| upgraded_request, | |
| &connect_address, | |
| &combined_config, | |
| &socket_data, | |
| &error_logger, | |
| ) | |
| .await; | |
| match result { | |
| Ok(_) => (), | |
| Err(err) => { | |
| error_logger | |
| .log(&format!("Unexpected error for CONNECT request: {}", err)) | |
| .await; | |
| } | |
| } | |
| } | |
| Err(err) => { | |
| error_logger | |
| .log(&format!( | |
| "Error while upgrading HTTP CONNECT request: {}", | |
| err | |
| )) | |
| .await | |
| } | |
| } | |
| }); | |
| let response = Response::builder() | |
| .body(Empty::new().map_err(|e| match e {}).boxed()) | |
| .unwrap_or_default(); | |
| if log_enabled { | |
| log_combined( | |
| &logger, | |
| client_ip, | |
| None, | |
| log_method, | |
| log_request_path, | |
| log_protocol, | |
| response.status().as_u16(), | |
| match response.headers().get(header::CONTENT_LENGTH) { | |
| Some(header_value) => match header_value.to_str() { | |
| Ok(header_value) => match header_value.parse::<u64>() { | |
| Ok(content_length) => Some(content_length), | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| None => response.body().size_hint().exact(), | |
| }, | |
| log_referrer, | |
| log_user_agent, | |
| ) | |
| .await; | |
| } | |
| let (mut response_parts, response_body) = response.into_parts(); | |
| if let Some(custom_headers_hash) = custom_headers_yaml.as_hash() { | |
| let custom_headers_hash_iter = custom_headers_hash.iter(); | |
| for (header_name, header_value) in custom_headers_hash_iter { | |
| if let Some(header_name) = header_name.as_str() { | |
| if let Some(header_value) = header_value.as_str() { | |
| if !response_parts.headers.contains_key(header_name) { | |
| if let Ok(header_value) = | |
| HeaderValue::from_str(&header_value.replace("{path}", &sanitized_url_pathname)) | |
| { | |
| if let Ok(header_name) = HeaderName::from_str(header_name) { | |
| response_parts.headers.insert(header_name, header_value); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if let Some(http3_alt_port) = http3_alt_port { | |
| if let Ok(header_value) = match response_parts.headers.get(header::ALT_SVC) { | |
| Some(value) => HeaderValue::from_bytes( | |
| format!( | |
| "{}, h3=\":{}\", h3-29=\":{}\"", | |
| String::from_utf8_lossy(value.as_bytes()), | |
| http3_alt_port, | |
| http3_alt_port | |
| ) | |
| .as_bytes(), | |
| ), | |
| None => HeaderValue::from_bytes( | |
| format!("h3=\":{}\", h3-29=\":{}\"", http3_alt_port, http3_alt_port).as_bytes(), | |
| ), | |
| } { | |
| response_parts.headers.insert(header::ALT_SVC, header_value); | |
| } | |
| } | |
| response_parts | |
| .headers | |
| .insert(header::SERVER, HeaderValue::from_static(SERVER_SOFTWARE)); | |
| Ok(Response::from_parts(response_parts, response_body)) | |
| } else { | |
| let response = Response::builder() | |
| .status(StatusCode::BAD_REQUEST) | |
| .body(Empty::new().map_err(|e| match e {}).boxed()) | |
| .unwrap_or_default(); | |
| if log_enabled { | |
| log_combined( | |
| &logger, | |
| socket_data.remote_addr.ip(), | |
| None, | |
| log_method, | |
| log_request_path, | |
| log_protocol, | |
| response.status().as_u16(), | |
| match response.headers().get(header::CONTENT_LENGTH) { | |
| Some(header_value) => match header_value.to_str() { | |
| Ok(header_value) => match header_value.parse::<u64>() { | |
| Ok(content_length) => Some(content_length), | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| None => response.body().size_hint().exact(), | |
| }, | |
| log_referrer, | |
| log_user_agent, | |
| ) | |
| .await; | |
| } | |
| let (mut response_parts, response_body) = response.into_parts(); | |
| if let Some(custom_headers_hash) = combined_config["customHeaders"].as_hash() { | |
| let custom_headers_hash_iter = custom_headers_hash.iter(); | |
| for (header_name, header_value) in custom_headers_hash_iter { | |
| if let Some(header_name) = header_name.as_str() { | |
| if let Some(header_value) = header_value.as_str() { | |
| if !response_parts.headers.contains_key(header_name) { | |
| if let Ok(header_value) = | |
| HeaderValue::from_str(&header_value.replace("{path}", &sanitized_url_pathname)) | |
| { | |
| if let Ok(header_name) = HeaderName::from_str(header_name) { | |
| response_parts.headers.insert(header_name, header_value); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if let Some(http3_alt_port) = http3_alt_port { | |
| if let Ok(header_value) = match response_parts.headers.get(header::ALT_SVC) { | |
| Some(value) => HeaderValue::from_bytes( | |
| format!( | |
| "{}, h3=\":{}\", h3-29=\":{}\"", | |
| String::from_utf8_lossy(value.as_bytes()), | |
| http3_alt_port, | |
| http3_alt_port | |
| ) | |
| .as_bytes(), | |
| ), | |
| None => HeaderValue::from_bytes( | |
| format!("h3=\":{}\", h3-29=\":{}\"", http3_alt_port, http3_alt_port).as_bytes(), | |
| ), | |
| } { | |
| response_parts.headers.insert(header::ALT_SVC, header_value); | |
| } | |
| } | |
| response_parts | |
| .headers | |
| .insert(header::SERVER, HeaderValue::from_static(SERVER_SOFTWARE)); | |
| Ok(Response::from_parts(response_parts, response_body)) | |
| } | |
| } else { | |
| let response = Response::builder() | |
| .status(StatusCode::NOT_IMPLEMENTED) | |
| .body(Empty::new().map_err(|e| match e {}).boxed()) | |
| .unwrap_or_default(); | |
| if log_enabled { | |
| log_combined( | |
| &logger, | |
| socket_data.remote_addr.ip(), | |
| None, | |
| log_method, | |
| log_request_path, | |
| log_protocol, | |
| response.status().as_u16(), | |
| match response.headers().get(header::CONTENT_LENGTH) { | |
| Some(header_value) => match header_value.to_str() { | |
| Ok(header_value) => match header_value.parse::<u64>() { | |
| Ok(content_length) => Some(content_length), | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| None => response.body().size_hint().exact(), | |
| }, | |
| log_referrer, | |
| log_user_agent, | |
| ) | |
| .await; | |
| } | |
| let (mut response_parts, response_body) = response.into_parts(); | |
| if let Some(custom_headers_hash) = combined_config["customHeaders"].as_hash() { | |
| let custom_headers_hash_iter = custom_headers_hash.iter(); | |
| for (header_name, header_value) in custom_headers_hash_iter { | |
| if let Some(header_name) = header_name.as_str() { | |
| if let Some(header_value) = header_value.as_str() { | |
| if !response_parts.headers.contains_key(header_name) { | |
| if let Ok(header_value) = | |
| HeaderValue::from_str(&header_value.replace("{path}", &sanitized_url_pathname)) | |
| { | |
| if let Ok(header_name) = HeaderName::from_str(header_name) { | |
| response_parts.headers.insert(header_name, header_value); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if let Some(http3_alt_port) = http3_alt_port { | |
| if let Ok(header_value) = match response_parts.headers.get(header::ALT_SVC) { | |
| Some(value) => HeaderValue::from_bytes( | |
| format!( | |
| "{}, h3=\":{}\", h3-29=\":{}\"", | |
| String::from_utf8_lossy(value.as_bytes()), | |
| http3_alt_port, | |
| http3_alt_port | |
| ) | |
| .as_bytes(), | |
| ), | |
| None => HeaderValue::from_bytes( | |
| format!("h3=\":{}\", h3-29=\":{}\"", http3_alt_port, http3_alt_port).as_bytes(), | |
| ), | |
| } { | |
| response_parts.headers.insert(header::ALT_SVC, header_value); | |
| } | |
| } | |
| response_parts | |
| .headers | |
| .insert(header::SERVER, HeaderValue::from_static(SERVER_SOFTWARE)); | |
| Ok(Response::from_parts(response_parts, response_body)) | |
| } | |
| } else { | |
| let is_websocket_request = is_upgrade_request(&request); | |
| let mut request_data = RequestData::new(request, None, None); | |
| let mut latest_auth_data = None; | |
| let mut executed_handlers = Vec::new(); | |
| for mut handlers in handlers_vec { | |
| if is_websocket_request && handlers.does_websocket_requests(&combined_config, &socket_data) { | |
| let (request, _, _) = request_data.into_parts(); | |
| // Variables moved to before "tokio::spawn" to avoid issues with moved values | |
| let client_ip = socket_data.remote_addr.ip(); | |
| let custom_headers_yaml = combined_config["customHeaders"].clone(); | |
| let request_uri = request.uri().to_owned(); | |
| let (original_response, websocket) = match hyper_tungstenite::upgrade(request, None) { | |
| Ok(data) => data, | |
| Err(err) => { | |
| error_logger | |
| .log(&format!("Error while upgrading WebSocket request: {}", err)) | |
| .await; | |
| let response = Response::builder() | |
| .status(StatusCode::INTERNAL_SERVER_ERROR) | |
| .body( | |
| Full::new(Bytes::from(generate_default_error_page( | |
| StatusCode::INTERNAL_SERVER_ERROR, | |
| None, | |
| ))) | |
| .map_err(|e| match e {}) | |
| .boxed(), | |
| ) | |
| .unwrap_or_default(); | |
| if log_enabled { | |
| log_combined( | |
| &logger, | |
| socket_data.remote_addr.ip(), | |
| None, | |
| log_method, | |
| log_request_path, | |
| log_protocol, | |
| response.status().as_u16(), | |
| match response.headers().get(header::CONTENT_LENGTH) { | |
| Some(header_value) => match header_value.to_str() { | |
| Ok(header_value) => match header_value.parse::<u64>() { | |
| Ok(content_length) => Some(content_length), | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| None => response.body().size_hint().exact(), | |
| }, | |
| log_referrer, | |
| log_user_agent, | |
| ) | |
| .await; | |
| } | |
| let (mut response_parts, response_body) = response.into_parts(); | |
| if let Some(custom_headers_hash) = combined_config["customHeaders"].as_hash() { | |
| let custom_headers_hash_iter = custom_headers_hash.iter(); | |
| for (header_name, header_value) in custom_headers_hash_iter { | |
| if let Some(header_name) = header_name.as_str() { | |
| if let Some(header_value) = header_value.as_str() { | |
| if !response_parts.headers.contains_key(header_name) { | |
| if let Ok(header_value) = HeaderValue::from_str( | |
| &header_value.replace("{path}", &sanitized_url_pathname), | |
| ) { | |
| if let Ok(header_name) = HeaderName::from_str(header_name) { | |
| response_parts.headers.insert(header_name, header_value); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if let Some(http3_alt_port) = http3_alt_port { | |
| if let Ok(header_value) = match response_parts.headers.get(header::ALT_SVC) { | |
| Some(value) => HeaderValue::from_bytes( | |
| format!( | |
| "{}, h3=\":{}\", h3-29=\":{}\"", | |
| String::from_utf8_lossy(value.as_bytes()), | |
| http3_alt_port, | |
| http3_alt_port | |
| ) | |
| .as_bytes(), | |
| ), | |
| None => HeaderValue::from_bytes( | |
| format!("h3=\":{}\", h3-29=\":{}\"", http3_alt_port, http3_alt_port).as_bytes(), | |
| ), | |
| } { | |
| response_parts.headers.insert(header::ALT_SVC, header_value); | |
| } | |
| } | |
| response_parts | |
| .headers | |
| .insert(header::SERVER, HeaderValue::from_static(SERVER_SOFTWARE)); | |
| return Ok(Response::from_parts(response_parts, response_body)); | |
| } | |
| }; | |
| tokio::spawn(async move { | |
| let result = handlers | |
| .websocket_request_handler( | |
| websocket, | |
| &request_uri, | |
| &combined_config, | |
| &socket_data, | |
| &error_logger, | |
| ) | |
| .await; | |
| match result { | |
| Ok(_) => (), | |
| Err(err) => { | |
| error_logger | |
| .log(&format!("Unexpected error for WebSocket request: {}", err)) | |
| .await; | |
| } | |
| } | |
| }); | |
| let response = original_response.map(|body| body.map_err(|err| match err {}).boxed()); | |
| if log_enabled { | |
| log_combined( | |
| &logger, | |
| client_ip, | |
| None, | |
| log_method, | |
| log_request_path, | |
| log_protocol, | |
| response.status().as_u16(), | |
| match response.headers().get(header::CONTENT_LENGTH) { | |
| Some(header_value) => match header_value.to_str() { | |
| Ok(header_value) => match header_value.parse::<u64>() { | |
| Ok(content_length) => Some(content_length), | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| None => response.body().size_hint().exact(), | |
| }, | |
| log_referrer, | |
| log_user_agent, | |
| ) | |
| .await; | |
| } | |
| let (mut response_parts, response_body) = response.into_parts(); | |
| if let Some(custom_headers_hash) = custom_headers_yaml.as_hash() { | |
| let custom_headers_hash_iter = custom_headers_hash.iter(); | |
| for (header_name, header_value) in custom_headers_hash_iter { | |
| if let Some(header_name) = header_name.as_str() { | |
| if let Some(header_value) = header_value.as_str() { | |
| if !response_parts.headers.contains_key(header_name) { | |
| if let Ok(header_value) = | |
| HeaderValue::from_str(&header_value.replace("{path}", &sanitized_url_pathname)) | |
| { | |
| if let Ok(header_name) = HeaderName::from_str(header_name) { | |
| response_parts.headers.insert(header_name, header_value); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if let Some(http3_alt_port) = http3_alt_port { | |
| if let Ok(header_value) = match response_parts.headers.get(header::ALT_SVC) { | |
| Some(value) => HeaderValue::from_bytes( | |
| format!( | |
| "{}, h3=\":{}\", h3-29=\":{}\"", | |
| String::from_utf8_lossy(value.as_bytes()), | |
| http3_alt_port, | |
| http3_alt_port | |
| ) | |
| .as_bytes(), | |
| ), | |
| None => HeaderValue::from_bytes( | |
| format!("h3=\":{}\", h3-29=\":{}\"", http3_alt_port, http3_alt_port).as_bytes(), | |
| ), | |
| } { | |
| response_parts.headers.insert(header::ALT_SVC, header_value); | |
| } | |
| } | |
| response_parts | |
| .headers | |
| .insert(header::SERVER, HeaderValue::from_static(SERVER_SOFTWARE)); | |
| return Ok(Response::from_parts(response_parts, response_body)); | |
| } | |
| let response_result = match is_proxy_request { | |
| true => { | |
| handlers | |
| .proxy_request_handler(request_data, &combined_config, &socket_data, &error_logger) | |
| .await | |
| } | |
| false => { | |
| handlers | |
| .request_handler(request_data, &combined_config, &socket_data, &error_logger) | |
| .await | |
| } | |
| }; | |
| executed_handlers.push(handlers); | |
| match response_result { | |
| Ok(response) => { | |
| let ( | |
| request_option, | |
| auth_data, | |
| original_url, | |
| response, | |
| status, | |
| headers, | |
| new_remote_address, | |
| parallel_fn, | |
| ) = response.into_parts(); | |
| latest_auth_data = auth_data.clone(); | |
| if let Some(new_remote_address) = new_remote_address { | |
| socket_data.remote_addr = new_remote_address; | |
| }; | |
| if let Some(parallel_fn) = parallel_fn { | |
| // Spawn the function in the web server's Tokio runtime. | |
| // We have implemented parallel_fn parameter in the ResponseData | |
| // because tokio::spawn doesn't work on dynamic libraries, | |
| // see https://github.com/tokio-rs/tokio/issues/6927 | |
| tokio::spawn(parallel_fn); | |
| } | |
| match response { | |
| Some(response) => { | |
| let (mut response_parts, response_body) = response.into_parts(); | |
| if let Some(custom_headers_hash) = combined_config["customHeaders"].as_hash() { | |
| let custom_headers_hash_iter = custom_headers_hash.iter(); | |
| for (header_name, header_value) in custom_headers_hash_iter { | |
| if let Some(header_name) = header_name.as_str() { | |
| if let Some(header_value) = header_value.as_str() { | |
| if !response_parts.headers.contains_key(header_name) { | |
| if let Ok(header_value) = HeaderValue::from_str( | |
| &header_value.replace("{path}", &sanitized_url_pathname), | |
| ) { | |
| if let Ok(header_name) = HeaderName::from_str(header_name) { | |
| response_parts.headers.insert(header_name, header_value); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if let Some(http3_alt_port) = http3_alt_port { | |
| if let Ok(header_value) = match response_parts.headers.get(header::ALT_SVC) { | |
| Some(value) => HeaderValue::from_bytes( | |
| format!( | |
| "{}, h3=\":{}\", h3-29=\":{}\"", | |
| String::from_utf8_lossy(value.as_bytes()), | |
| http3_alt_port, | |
| http3_alt_port | |
| ) | |
| .as_bytes(), | |
| ), | |
| None => HeaderValue::from_bytes( | |
| format!("h3=\":{}\", h3-29=\":{}\"", http3_alt_port, http3_alt_port).as_bytes(), | |
| ), | |
| } { | |
| response_parts.headers.insert(header::ALT_SVC, header_value); | |
| } | |
| } | |
| response_parts | |
| .headers | |
| .insert(header::SERVER, HeaderValue::from_static(SERVER_SOFTWARE)); | |
| let mut response = Response::from_parts(response_parts, response_body); | |
| while let Some(mut executed_handler) = executed_handlers.pop() { | |
| let response_status = match is_proxy_request { | |
| true => { | |
| executed_handler | |
| .proxy_response_modifying_handler(response) | |
| .await | |
| } | |
| false => executed_handler.response_modifying_handler(response).await, | |
| }; | |
| response = match response_status { | |
| Ok(response) => response, | |
| Err(err) => { | |
| if error_log_enabled { | |
| logger | |
| .send(LogMessage::new( | |
| format!("Unexpected error while serving a request: {}", err), | |
| true, | |
| )) | |
| .await | |
| .unwrap_or_default(); | |
| } | |
| let response = generate_error_response( | |
| StatusCode::INTERNAL_SERVER_ERROR, | |
| &combined_config, | |
| &headers, | |
| ) | |
| .await; | |
| if log_enabled { | |
| log_combined( | |
| &logger, | |
| socket_data.remote_addr.ip(), | |
| auth_data, | |
| log_method, | |
| log_request_path, | |
| log_protocol, | |
| response.status().as_u16(), | |
| match response.headers().get(header::CONTENT_LENGTH) { | |
| Some(header_value) => match header_value.to_str() { | |
| Ok(header_value) => match header_value.parse::<u64>() { | |
| Ok(content_length) => Some(content_length), | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| None => response.body().size_hint().exact(), | |
| }, | |
| log_referrer, | |
| log_user_agent, | |
| ) | |
| .await; | |
| } | |
| let (mut response_parts, response_body) = response.into_parts(); | |
| if let Some(custom_headers_hash) = combined_config["customHeaders"].as_hash() { | |
| let custom_headers_hash_iter = custom_headers_hash.iter(); | |
| for (header_name, header_value) in custom_headers_hash_iter { | |
| if let Some(header_name) = header_name.as_str() { | |
| if let Some(header_value) = header_value.as_str() { | |
| if !response_parts.headers.contains_key(header_name) { | |
| if let Ok(header_value) = HeaderValue::from_str( | |
| &header_value.replace("{path}", &sanitized_url_pathname), | |
| ) { | |
| if let Ok(header_name) = HeaderName::from_str(header_name) { | |
| response_parts.headers.insert(header_name, header_value); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if let Some(http3_alt_port) = http3_alt_port { | |
| if let Ok(header_value) = match response_parts.headers.get(header::ALT_SVC) { | |
| Some(value) => HeaderValue::from_bytes( | |
| format!( | |
| "{}, h3=\":{}\", h3-29=\":{}\"", | |
| String::from_utf8_lossy(value.as_bytes()), | |
| http3_alt_port, | |
| http3_alt_port | |
| ) | |
| .as_bytes(), | |
| ), | |
| None => HeaderValue::from_bytes( | |
| format!("h3=\":{}\", h3-29=\":{}\"", http3_alt_port, http3_alt_port) | |
| .as_bytes(), | |
| ), | |
| } { | |
| response_parts.headers.insert(header::ALT_SVC, header_value); | |
| } | |
| } | |
| response_parts | |
| .headers | |
| .insert(header::SERVER, HeaderValue::from_static(SERVER_SOFTWARE)); | |
| return Ok(Response::from_parts(response_parts, response_body)); | |
| } | |
| }; | |
| } | |
| if log_enabled { | |
| log_combined( | |
| &logger, | |
| socket_data.remote_addr.ip(), | |
| auth_data, | |
| log_method, | |
| log_request_path, | |
| log_protocol, | |
| response.status().as_u16(), | |
| match response.headers().get(header::CONTENT_LENGTH) { | |
| Some(header_value) => match header_value.to_str() { | |
| Ok(header_value) => match header_value.parse::<u64>() { | |
| Ok(content_length) => Some(content_length), | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| None => response.body().size_hint().exact(), | |
| }, | |
| log_referrer, | |
| log_user_agent, | |
| ) | |
| .await; | |
| } | |
| return Ok(response); | |
| } | |
| None => match status { | |
| Some(status) => { | |
| let response = generate_error_response(status, &combined_config, &headers).await; | |
| let (mut response_parts, response_body) = response.into_parts(); | |
| if let Some(custom_headers_hash) = combined_config["customHeaders"].as_hash() { | |
| let custom_headers_hash_iter = custom_headers_hash.iter(); | |
| for (header_name, header_value) in custom_headers_hash_iter { | |
| if let Some(header_name) = header_name.as_str() { | |
| if let Some(header_value) = header_value.as_str() { | |
| if !response_parts.headers.contains_key(header_name) { | |
| if let Ok(header_value) = HeaderValue::from_str( | |
| &header_value.replace("{path}", &sanitized_url_pathname), | |
| ) { | |
| if let Ok(header_name) = HeaderName::from_str(header_name) { | |
| response_parts.headers.insert(header_name, header_value); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if let Some(http3_alt_port) = http3_alt_port { | |
| if let Ok(header_value) = match response_parts.headers.get(header::ALT_SVC) { | |
| Some(value) => HeaderValue::from_bytes( | |
| format!( | |
| "{}, h3=\":{}\", h3-29=\":{}\"", | |
| String::from_utf8_lossy(value.as_bytes()), | |
| http3_alt_port, | |
| http3_alt_port | |
| ) | |
| .as_bytes(), | |
| ), | |
| None => HeaderValue::from_bytes( | |
| format!("h3=\":{}\", h3-29=\":{}\"", http3_alt_port, http3_alt_port) | |
| .as_bytes(), | |
| ), | |
| } { | |
| response_parts.headers.insert(header::ALT_SVC, header_value); | |
| } | |
| } | |
| response_parts | |
| .headers | |
| .insert(header::SERVER, HeaderValue::from_static(SERVER_SOFTWARE)); | |
| let mut response = Response::from_parts(response_parts, response_body); | |
| while let Some(mut executed_handler) = executed_handlers.pop() { | |
| let response_status = match is_proxy_request { | |
| true => { | |
| executed_handler | |
| .proxy_response_modifying_handler(response) | |
| .await | |
| } | |
| false => executed_handler.response_modifying_handler(response).await, | |
| }; | |
| response = match response_status { | |
| Ok(response) => response, | |
| Err(err) => { | |
| if error_log_enabled { | |
| logger | |
| .send(LogMessage::new( | |
| format!("Unexpected error while serving a request: {}", err), | |
| true, | |
| )) | |
| .await | |
| .unwrap_or_default(); | |
| } | |
| let response = generate_error_response( | |
| StatusCode::INTERNAL_SERVER_ERROR, | |
| &combined_config, | |
| &headers, | |
| ) | |
| .await; | |
| if log_enabled { | |
| log_combined( | |
| &logger, | |
| socket_data.remote_addr.ip(), | |
| auth_data, | |
| log_method, | |
| log_request_path, | |
| log_protocol, | |
| response.status().as_u16(), | |
| match response.headers().get(header::CONTENT_LENGTH) { | |
| Some(header_value) => match header_value.to_str() { | |
| Ok(header_value) => match header_value.parse::<u64>() { | |
| Ok(content_length) => Some(content_length), | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| None => response.body().size_hint().exact(), | |
| }, | |
| log_referrer, | |
| log_user_agent, | |
| ) | |
| .await; | |
| } | |
| let (mut response_parts, response_body) = response.into_parts(); | |
| if let Some(custom_headers_hash) = combined_config["customHeaders"].as_hash() | |
| { | |
| let custom_headers_hash_iter = custom_headers_hash.iter(); | |
| for (header_name, header_value) in custom_headers_hash_iter { | |
| if let Some(header_name) = header_name.as_str() { | |
| if let Some(header_value) = header_value.as_str() { | |
| if !response_parts.headers.contains_key(header_name) { | |
| if let Ok(header_value) = HeaderValue::from_str( | |
| &header_value.replace("{path}", &sanitized_url_pathname), | |
| ) { | |
| if let Ok(header_name) = HeaderName::from_str(header_name) { | |
| response_parts.headers.insert(header_name, header_value); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if let Some(http3_alt_port) = http3_alt_port { | |
| if let Ok(header_value) = match response_parts.headers.get(header::ALT_SVC) | |
| { | |
| Some(value) => HeaderValue::from_bytes( | |
| format!( | |
| "{}, h3=\":{}\", h3-29=\":{}\"", | |
| String::from_utf8_lossy(value.as_bytes()), | |
| http3_alt_port, | |
| http3_alt_port | |
| ) | |
| .as_bytes(), | |
| ), | |
| None => HeaderValue::from_bytes( | |
| format!("h3=\":{}\", h3-29=\":{}\"", http3_alt_port, http3_alt_port) | |
| .as_bytes(), | |
| ), | |
| } { | |
| response_parts.headers.insert(header::ALT_SVC, header_value); | |
| } | |
| } | |
| response_parts | |
| .headers | |
| .insert(header::SERVER, HeaderValue::from_static(SERVER_SOFTWARE)); | |
| return Ok(Response::from_parts(response_parts, response_body)); | |
| } | |
| }; | |
| } | |
| if log_enabled { | |
| log_combined( | |
| &logger, | |
| socket_data.remote_addr.ip(), | |
| auth_data, | |
| log_method, | |
| log_request_path, | |
| log_protocol, | |
| response.status().as_u16(), | |
| match response.headers().get(header::CONTENT_LENGTH) { | |
| Some(header_value) => match header_value.to_str() { | |
| Ok(header_value) => match header_value.parse::<u64>() { | |
| Ok(content_length) => Some(content_length), | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| None => response.body().size_hint().exact(), | |
| }, | |
| log_referrer, | |
| log_user_agent, | |
| ) | |
| .await; | |
| } | |
| return Ok(response); | |
| } | |
| None => match request_option { | |
| Some(request) => { | |
| request_data = RequestData::new(request, auth_data, original_url); | |
| continue; | |
| } | |
| None => { | |
| break; | |
| } | |
| }, | |
| }, | |
| } | |
| } | |
| Err(err) => { | |
| let response = | |
| generate_error_response(StatusCode::INTERNAL_SERVER_ERROR, &combined_config, &None) | |
| .await; | |
| let (mut response_parts, response_body) = response.into_parts(); | |
| if let Some(custom_headers_hash) = combined_config["customHeaders"].as_hash() { | |
| let custom_headers_hash_iter = custom_headers_hash.iter(); | |
| for (header_name, header_value) in custom_headers_hash_iter { | |
| if let Some(header_name) = header_name.as_str() { | |
| if let Some(header_value) = header_value.as_str() { | |
| if !response_parts.headers.contains_key(header_name) { | |
| if let Ok(header_value) = HeaderValue::from_str( | |
| &header_value.replace("{path}", &sanitized_url_pathname), | |
| ) { | |
| if let Ok(header_name) = HeaderName::from_str(header_name) { | |
| response_parts.headers.insert(header_name, header_value); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if let Some(http3_alt_port) = http3_alt_port { | |
| if let Ok(header_value) = match response_parts.headers.get(header::ALT_SVC) { | |
| Some(value) => HeaderValue::from_bytes( | |
| format!( | |
| "{}, h3=\":{}\", h3-29=\":{}\"", | |
| String::from_utf8_lossy(value.as_bytes()), | |
| http3_alt_port, | |
| http3_alt_port | |
| ) | |
| .as_bytes(), | |
| ), | |
| None => HeaderValue::from_bytes( | |
| format!("h3=\":{}\", h3-29=\":{}\"", http3_alt_port, http3_alt_port).as_bytes(), | |
| ), | |
| } { | |
| response_parts.headers.insert(header::ALT_SVC, header_value); | |
| } | |
| } | |
| response_parts | |
| .headers | |
| .insert(header::SERVER, HeaderValue::from_static(SERVER_SOFTWARE)); | |
| let mut response = Response::from_parts(response_parts, response_body); | |
| while let Some(mut executed_handler) = executed_handlers.pop() { | |
| let response_status = match is_proxy_request { | |
| true => { | |
| executed_handler | |
| .proxy_response_modifying_handler(response) | |
| .await | |
| } | |
| false => executed_handler.response_modifying_handler(response).await, | |
| }; | |
| response = match response_status { | |
| Ok(response) => response, | |
| Err(err) => { | |
| if error_log_enabled { | |
| logger | |
| .send(LogMessage::new( | |
| format!("Unexpected error while serving a request: {}", err), | |
| true, | |
| )) | |
| .await | |
| .unwrap_or_default(); | |
| } | |
| let response = generate_error_response( | |
| StatusCode::INTERNAL_SERVER_ERROR, | |
| &combined_config, | |
| &None, | |
| ) | |
| .await; | |
| if log_enabled { | |
| log_combined( | |
| &logger, | |
| socket_data.remote_addr.ip(), | |
| latest_auth_data, | |
| log_method, | |
| log_request_path, | |
| log_protocol, | |
| response.status().as_u16(), | |
| match response.headers().get(header::CONTENT_LENGTH) { | |
| Some(header_value) => match header_value.to_str() { | |
| Ok(header_value) => match header_value.parse::<u64>() { | |
| Ok(content_length) => Some(content_length), | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| None => response.body().size_hint().exact(), | |
| }, | |
| log_referrer, | |
| log_user_agent, | |
| ) | |
| .await; | |
| } | |
| let (mut response_parts, response_body) = response.into_parts(); | |
| if let Some(custom_headers_hash) = combined_config["customHeaders"].as_hash() { | |
| let custom_headers_hash_iter = custom_headers_hash.iter(); | |
| for (header_name, header_value) in custom_headers_hash_iter { | |
| if let Some(header_name) = header_name.as_str() { | |
| if let Some(header_value) = header_value.as_str() { | |
| if !response_parts.headers.contains_key(header_name) { | |
| if let Ok(header_value) = HeaderValue::from_str( | |
| &header_value.replace("{path}", &sanitized_url_pathname), | |
| ) { | |
| if let Ok(header_name) = HeaderName::from_str(header_name) { | |
| response_parts.headers.insert(header_name, header_value); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if let Some(http3_alt_port) = http3_alt_port { | |
| if let Ok(header_value) = match response_parts.headers.get(header::ALT_SVC) { | |
| Some(value) => HeaderValue::from_bytes( | |
| format!( | |
| "{}, h3=\":{}\", h3-29=\":{}\"", | |
| String::from_utf8_lossy(value.as_bytes()), | |
| http3_alt_port, | |
| http3_alt_port | |
| ) | |
| .as_bytes(), | |
| ), | |
| None => HeaderValue::from_bytes( | |
| format!("h3=\":{}\", h3-29=\":{}\"", http3_alt_port, http3_alt_port) | |
| .as_bytes(), | |
| ), | |
| } { | |
| response_parts.headers.insert(header::ALT_SVC, header_value); | |
| } | |
| } | |
| response_parts | |
| .headers | |
| .insert(header::SERVER, HeaderValue::from_static(SERVER_SOFTWARE)); | |
| return Ok(Response::from_parts(response_parts, response_body)); | |
| } | |
| }; | |
| } | |
| if error_log_enabled { | |
| logger | |
| .send(LogMessage::new( | |
| format!("Unexpected error while serving a request: {}", err), | |
| true, | |
| )) | |
| .await | |
| .unwrap_or_default(); | |
| } | |
| if log_enabled { | |
| log_combined( | |
| &logger, | |
| socket_data.remote_addr.ip(), | |
| latest_auth_data, | |
| log_method, | |
| log_request_path, | |
| log_protocol, | |
| response.status().as_u16(), | |
| match response.headers().get(header::CONTENT_LENGTH) { | |
| Some(header_value) => match header_value.to_str() { | |
| Ok(header_value) => match header_value.parse::<u64>() { | |
| Ok(content_length) => Some(content_length), | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| None => response.body().size_hint().exact(), | |
| }, | |
| log_referrer, | |
| log_user_agent, | |
| ) | |
| .await; | |
| } | |
| return Ok(response); | |
| } | |
| } | |
| } | |
| let response = generate_error_response(StatusCode::NOT_FOUND, &combined_config, &None).await; | |
| let (mut response_parts, response_body) = response.into_parts(); | |
| if let Some(custom_headers_hash) = combined_config["customHeaders"].as_hash() { | |
| let custom_headers_hash_iter = custom_headers_hash.iter(); | |
| for (header_name, header_value) in custom_headers_hash_iter { | |
| if let Some(header_name) = header_name.as_str() { | |
| if let Some(header_value) = header_value.as_str() { | |
| if !response_parts.headers.contains_key(header_name) { | |
| if let Ok(header_value) = | |
| HeaderValue::from_str(&header_value.replace("{path}", &sanitized_url_pathname)) | |
| { | |
| if let Ok(header_name) = HeaderName::from_str(header_name) { | |
| response_parts.headers.insert(header_name, header_value); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if let Some(http3_alt_port) = http3_alt_port { | |
| if let Ok(header_value) = match response_parts.headers.get(header::ALT_SVC) { | |
| Some(value) => HeaderValue::from_bytes( | |
| format!( | |
| "{}, h3=\":{}\", h3-29=\":{}\"", | |
| String::from_utf8_lossy(value.as_bytes()), | |
| http3_alt_port, | |
| http3_alt_port | |
| ) | |
| .as_bytes(), | |
| ), | |
| None => HeaderValue::from_bytes( | |
| format!("h3=\":{}\", h3-29=\":{}\"", http3_alt_port, http3_alt_port).as_bytes(), | |
| ), | |
| } { | |
| response_parts.headers.insert(header::ALT_SVC, header_value); | |
| } | |
| } | |
| response_parts | |
| .headers | |
| .insert(header::SERVER, HeaderValue::from_static(SERVER_SOFTWARE)); | |
| let mut response = Response::from_parts(response_parts, response_body); | |
| while let Some(mut executed_handler) = executed_handlers.pop() { | |
| let response_status = match is_proxy_request { | |
| true => { | |
| executed_handler | |
| .proxy_response_modifying_handler(response) | |
| .await | |
| } | |
| false => executed_handler.response_modifying_handler(response).await, | |
| }; | |
| response = match response_status { | |
| Ok(response) => response, | |
| Err(err) => { | |
| if error_log_enabled { | |
| logger | |
| .send(LogMessage::new( | |
| format!("Unexpected error while serving a request: {}", err), | |
| true, | |
| )) | |
| .await | |
| .unwrap_or_default(); | |
| } | |
| let response = | |
| generate_error_response(StatusCode::INTERNAL_SERVER_ERROR, &combined_config, &None) | |
| .await; | |
| if log_enabled { | |
| log_combined( | |
| &logger, | |
| socket_data.remote_addr.ip(), | |
| latest_auth_data, | |
| log_method, | |
| log_request_path, | |
| log_protocol, | |
| response.status().as_u16(), | |
| match response.headers().get(header::CONTENT_LENGTH) { | |
| Some(header_value) => match header_value.to_str() { | |
| Ok(header_value) => match header_value.parse::<u64>() { | |
| Ok(content_length) => Some(content_length), | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| None => response.body().size_hint().exact(), | |
| }, | |
| log_referrer, | |
| log_user_agent, | |
| ) | |
| .await; | |
| } | |
| let (mut response_parts, response_body) = response.into_parts(); | |
| if let Some(custom_headers_hash) = combined_config["customHeaders"].as_hash() { | |
| let custom_headers_hash_iter = custom_headers_hash.iter(); | |
| for (header_name, header_value) in custom_headers_hash_iter { | |
| if let Some(header_name) = header_name.as_str() { | |
| if let Some(header_value) = header_value.as_str() { | |
| if !response_parts.headers.contains_key(header_name) { | |
| if let Ok(header_value) = HeaderValue::from_str( | |
| &header_value.replace("{path}", &sanitized_url_pathname), | |
| ) { | |
| if let Ok(header_name) = HeaderName::from_str(header_name) { | |
| response_parts.headers.insert(header_name, header_value); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if let Some(http3_alt_port) = http3_alt_port { | |
| if let Ok(header_value) = match response_parts.headers.get(header::ALT_SVC) { | |
| Some(value) => HeaderValue::from_bytes( | |
| format!( | |
| "{}, h3=\":{}\", h3-29=\":{}\"", | |
| String::from_utf8_lossy(value.as_bytes()), | |
| http3_alt_port, | |
| http3_alt_port | |
| ) | |
| .as_bytes(), | |
| ), | |
| None => HeaderValue::from_bytes( | |
| format!("h3=\":{}\", h3-29=\":{}\"", http3_alt_port, http3_alt_port).as_bytes(), | |
| ), | |
| } { | |
| response_parts.headers.insert(header::ALT_SVC, header_value); | |
| } | |
| } | |
| response_parts | |
| .headers | |
| .insert(header::SERVER, HeaderValue::from_static(SERVER_SOFTWARE)); | |
| return Ok(Response::from_parts(response_parts, response_body)); | |
| } | |
| }; | |
| } | |
| if log_enabled { | |
| log_combined( | |
| &logger, | |
| socket_data.remote_addr.ip(), | |
| latest_auth_data, | |
| log_method, | |
| log_request_path, | |
| log_protocol, | |
| response.status().as_u16(), | |
| match response.headers().get(header::CONTENT_LENGTH) { | |
| Some(header_value) => match header_value.to_str() { | |
| Ok(header_value) => match header_value.parse::<u64>() { | |
| Ok(content_length) => Some(content_length), | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| Err(_) => response.body().size_hint().exact(), | |
| }, | |
| None => response.body().size_hint().exact(), | |
| }, | |
| log_referrer, | |
| log_user_agent, | |
| ) | |
| .await; | |
| } | |
| Ok(response) | |
| } | |
| } | |
| pub async fn request_handler( | |
| request: Request<BoxBody<Bytes, std::io::Error>>, | |
| remote_address: SocketAddr, | |
| local_address: SocketAddr, | |
| encrypted: bool, | |
| config: Arc<Yaml>, | |
| logger: Sender<LogMessage>, | |
| handlers_vec: Vec<Box<dyn ServerModuleHandlers + Send>>, | |
| acme_http01_resolver_option: Option<Arc<ResolvesServerCertAcme>>, | |
| http3_alt_port: Option<u16>, | |
| ) -> Result<Response<BoxBody<Bytes, std::io::Error>>, anyhow::Error> { | |
| let timeout_yaml = &config["global"]["timeout"]; | |
| if timeout_yaml.is_null() { | |
| request_handler_wrapped( | |
| request, | |
| remote_address, | |
| local_address, | |
| encrypted, | |
| config, | |
| logger, | |
| handlers_vec, | |
| acme_http01_resolver_option, | |
| http3_alt_port, | |
| ) | |
| .await | |
| .map_err(|e| anyhow::anyhow!(e)) | |
| } else { | |
| let timeout_millis = timeout_yaml.as_i64().unwrap_or(300000) as u64; | |
| match timeout( | |
| Duration::from_millis(timeout_millis), | |
| request_handler_wrapped( | |
| request, | |
| remote_address, | |
| local_address, | |
| encrypted, | |
| config, | |
| logger, | |
| handlers_vec, | |
| acme_http01_resolver_option, | |
| http3_alt_port, | |
| ), | |
| ) | |
| .await | |
| { | |
| Ok(response) => response.map_err(|e| anyhow::anyhow!(e)), | |
| Err(_) => Err(anyhow::anyhow!("The client or server has timed out")), | |
| } | |
| } | |
| } | |