use super::*;
pub fn build_protocol_request(
config: &RequestConfig,
head: ProtocolRequestHead,
) -> Result<Request, RequestError> {
let ProtocolRequestHead {
version,
method,
parts,
body,
} = head;
let uri: &http::Uri = &parts.uri;
let target: String = request_target(uri.path(), uri.query());
let max_path_size: usize = config.get_max_path_size();
if target.len() > max_path_size && max_path_size != DEFAULT_LOW_SECURITY_MAX_PATH_SIZE {
return Err(RequestError::PathTooLong(HttpStatus::URITooLong));
}
let max_body_size: usize = config.get_max_body_size();
if body.len() > max_body_size && max_body_size != DEFAULT_LOW_SECURITY_MAX_BODY_SIZE {
return Err(RequestError::ContentLengthTooLarge(
HttpStatus::PayloadTooLarge,
));
}
let mut request: Request = Request::default();
let query_index: Option<usize> = target.find(QUESTION_MARK);
let hash_index: Option<usize> = target.find(HASH);
fill_request_querys(
split_target_query(&target, query_index, hash_index),
request.get_mut_querys(),
);
request
.get_mut_path()
.push_str(split_target_path(&target, query_index, hash_index));
request.set_method(protocol_method(method));
request.set_version(version);
fill_request_headers(&parts.headers, config, &mut request)?;
request.set_host(resolve_request_host(&parts.headers, uri));
request.get_mut_body().clear();
request.get_mut_body().extend_from_slice(&body);
Ok(request)
}
pub fn resolve_request_host(headers: &ProtocolHeaders, uri: &http::Uri) -> String {
if let Some(authority) = uri.authority() {
return authority.as_str().to_owned();
}
for name in [HOST, COLON_AUTHORITY] {
if let Some(value) = headers.get(name)
&& let Ok(value) = value.to_str()
{
return value.to_owned();
}
}
EMPTY_STR.to_owned()
}
pub fn split_target_query(
target: &str,
query_index: Option<usize>,
hash_index: Option<usize>,
) -> &str {
query_index.map_or(EMPTY_STR, |query_index: usize| {
let rest: &str = &target[query_index + 1..];
match hash_index {
None => rest,
Some(hash_index) if hash_index <= query_index => rest,
Some(hash_index) => &rest[..hash_index - query_index - 1],
}
})
}
pub fn split_target_path(
target: &str,
query_index: Option<usize>,
hash_index: Option<usize>,
) -> &str {
match query_index.or(hash_index) {
Some(separator_index) => &target[..separator_index],
None => target,
}
}
pub fn fill_request_querys(query: &str, querys: &mut RequestQuerys) {
if query.is_empty() {
return;
}
for pair in query.split(AND) {
if let Some((key, value)) = pair.split_once(EQUAL) {
if !key.is_empty() {
querys
.entry(key.to_string())
.or_default()
.push_back(value.to_string());
}
} else if !pair.is_empty() {
querys.entry(pair.to_string()).or_default();
}
}
}
pub fn protocol_method(method: ProtocolMethod) -> RequestMethod {
method
.as_str()
.parse::<RequestMethod>()
.unwrap_or(Method::Unknown(method.as_str().to_owned()))
}
pub fn fill_request_headers(
headers: &ProtocolHeaders,
config: &RequestConfig,
request: &mut Request,
) -> Result<(), RequestError> {
let mut header_count: usize = 0;
for (name, value) in headers.iter() {
let key: &str = name.as_str();
if key.starts_with(PSEUDO_HEADER_PREFIX) || is_hop_by_hop(key) {
continue;
}
let Ok(value) = value.to_str() else {
continue;
};
header_count += 1;
if header_count > config.get_max_header_count()
&& config.get_max_header_count() != DEFAULT_LOW_SECURITY_MAX_HEADER_COUNT
{
return Err(RequestError::TooManyHeaders(
HttpStatus::RequestHeaderFieldsTooLarge,
));
}
if key.len() > config.get_max_header_key_size()
&& config.get_max_header_key_size() != DEFAULT_LOW_SECURITY_MAX_HEADER_KEY_SIZE
{
return Err(RequestError::HeaderKeyTooLong(HttpStatus::BadRequest));
}
if value.len() > config.get_max_header_value_size()
&& config.get_max_header_value_size() != DEFAULT_LOW_SECURITY_MAX_HEADER_VALUE_SIZE
{
return Err(RequestError::HeaderValueTooLong(HttpStatus::BadRequest));
}
request
.get_mut_headers()
.entry(key.to_owned())
.or_default()
.push_back(value.to_owned());
}
Ok(())
}
pub fn is_hop_by_hop(name: &str) -> bool {
matches!(
name,
CONNECTION
| KEEP_ALIVE
| PROXY_AUTHENTICATE
| PROXY_AUTHORIZATION
| TE
| TRAILER_HEADER
| TRANSFER_ENCODING
| UPGRADE
)
}
pub fn declared_content_length(
headers: &ProtocolHeaders,
max_body_size: usize,
) -> Result<Option<usize>, RequestError> {
let Some(value) = headers.get(CONTENT_LENGTH) else {
return Ok(None);
};
let Ok(value) = value.to_str() else {
return Err(RequestError::InvalidContentLength(HttpStatus::BadRequest));
};
let declared: usize = parse_ascii_length(value)?;
if declared > max_body_size && max_body_size != DEFAULT_LOW_SECURITY_MAX_BODY_SIZE {
return Err(RequestError::ContentLengthTooLarge(
HttpStatus::PayloadTooLarge,
));
}
Ok(Some(declared))
}
pub(crate) fn parse_ascii_length(value: &str) -> Result<usize, RequestError> {
let digits: &str = value.trim();
if digits.is_empty() || !digits.bytes().all(|byte: u8| byte.is_ascii_digit()) {
return Err(RequestError::InvalidContentLength(HttpStatus::BadRequest));
}
digits
.parse::<usize>()
.map_err(|_: ParseIntError| RequestError::InvalidContentLength(HttpStatus::BadRequest))
}
pub fn is_bodyless_status(status_code: usize, is_head: bool) -> bool {
is_head
|| status_code < 200
|| status_code == HttpStatus::NoContent.code()
|| status_code == HttpStatus::NotModified.code()
}
pub fn push_bounded_body(
limit: usize,
body: &mut Vec<u8>,
chunk: Bytes,
) -> Result<(), RequestError> {
if body.len() + chunk.len() > limit && limit != DEFAULT_LOW_SECURITY_MAX_BODY_SIZE {
return Err(RequestError::ContentLengthTooLarge(
HttpStatus::PayloadTooLarge,
));
}
body.extend_from_slice(chunk.as_ref());
Ok(())
}
pub fn request_target(path: &str, query: Option<&str>) -> String {
match query {
Some(query) if !query.is_empty() => {
let mut target: String = String::with_capacity(path.len() + query.len() + 1);
target.push_str(path);
target.push(QUESTION_MARK);
target.push_str(query);
target
}
_ => path.to_owned(),
}
}