Bound local HTTP resource use
This commit is contained in:
@@ -72,21 +72,27 @@ pub(super) fn send_response(
|
||||
.map_err(|error| error.to_string())
|
||||
}
|
||||
|
||||
pub(super) fn read_request(stream: &mut TcpStream) -> Result<HttpRequest, String> {
|
||||
pub(super) fn read_request(
|
||||
stream: &mut TcpStream,
|
||||
deadline: Instant,
|
||||
) -> Result<HttpRequest, String> {
|
||||
let mut bytes = Vec::new();
|
||||
let header_end = loop {
|
||||
if bytes.len() >= MAX_HEADER_BYTES {
|
||||
return Err("HTTP headers are too large".into());
|
||||
}
|
||||
let mut chunk = [0_u8; 4096];
|
||||
let read = stream.read(&mut chunk).map_err(|error| error.to_string())?;
|
||||
let read = read_with_deadline(stream, &mut chunk, deadline)?;
|
||||
if read == 0 {
|
||||
return Err("bad HTTP request".into());
|
||||
}
|
||||
bytes.extend_from_slice(&chunk[..read]);
|
||||
if let Some(end) = find_header_end(&bytes) {
|
||||
if end > MAX_HEADER_BYTES {
|
||||
return Err("HTTP headers are too large".into());
|
||||
}
|
||||
break end;
|
||||
}
|
||||
if bytes.len() >= MAX_HEADER_BYTES {
|
||||
return Err("HTTP headers are too large".into());
|
||||
}
|
||||
};
|
||||
let header = std::str::from_utf8(&bytes[..header_end])
|
||||
.map_err(|_| "HTTP headers are not UTF-8".to_owned())?;
|
||||
@@ -115,7 +121,7 @@ pub(super) fn read_request(stream: &mut TcpStream) -> Result<HttpRequest, String
|
||||
}
|
||||
while bytes.len() < header_end + length {
|
||||
let mut chunk = [0_u8; 8192];
|
||||
let read = stream.read(&mut chunk).map_err(|error| error.to_string())?;
|
||||
let read = read_with_deadline(stream, &mut chunk, deadline)?;
|
||||
if read == 0 {
|
||||
return Err("incomplete HTTP body".into());
|
||||
}
|
||||
@@ -128,6 +134,30 @@ pub(super) fn read_request(stream: &mut TcpStream) -> Result<HttpRequest, String
|
||||
})
|
||||
}
|
||||
|
||||
fn read_with_deadline(
|
||||
stream: &mut TcpStream,
|
||||
buffer: &mut [u8],
|
||||
deadline: Instant,
|
||||
) -> Result<usize, String> {
|
||||
let remaining = deadline.saturating_duration_since(Instant::now());
|
||||
if remaining.is_zero() {
|
||||
return Err("HTTP request timed out".into());
|
||||
}
|
||||
stream
|
||||
.set_read_timeout(Some(remaining.min(HTTP_IO_TIMEOUT)))
|
||||
.map_err(|error| error.to_string())?;
|
||||
stream.read(buffer).map_err(|error| {
|
||||
if matches!(
|
||||
error.kind(),
|
||||
std::io::ErrorKind::TimedOut | std::io::ErrorKind::WouldBlock
|
||||
) {
|
||||
"HTTP request timed out".into()
|
||||
} else {
|
||||
error.to_string()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn find_header_end(bytes: &[u8]) -> Option<usize> {
|
||||
bytes
|
||||
.windows(4)
|
||||
|
||||
Reference in New Issue
Block a user