mirror of https://github.com/vinc/moros.git
Fix HTTP server (#609)
* Refactor index code * Fix chunked receive buffer * Fix join_path * Fix relative path
This commit is contained in:
parent
6ab9ad713b
commit
d04d1a0539
115
src/usr/httpd.rs
115
src/usr/httpd.rs
|
@ -23,6 +23,7 @@ use smoltcp::wire::IpAddress;
|
|||
|
||||
const MAX_CONNECTIONS: usize = 32;
|
||||
const POLL_DELAY_DIV: usize = 128;
|
||||
const INDEX: [&str; 4] = ["", "/index.html", "/index.htm", "/index.txt"];
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Request {
|
||||
|
@ -212,10 +213,8 @@ fn get(req: &Request, res: &mut Response) {
|
|||
res.body.extend_from_slice(b"<h1>Moved Permanently</h1>\r\n");
|
||||
} else {
|
||||
let mut not_found = true;
|
||||
for autocomplete in
|
||||
&["", "/index.html", "/index.htm", "/index.txt"]
|
||||
{
|
||||
let real_path = format!("{}{}", res.real_path, autocomplete);
|
||||
for index in INDEX {
|
||||
let real_path = format!("{}{}", res.real_path, index);
|
||||
if fs::is_dir(&real_path) {
|
||||
continue;
|
||||
}
|
||||
|
@ -338,6 +337,9 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
i += 1;
|
||||
}
|
||||
|
||||
// NOTE: This specific format is needed by `join_path`
|
||||
let dir = format!("/{}", fs::realpath(&dir).trim_matches('/'));
|
||||
|
||||
if let Some((ref mut iface, ref mut device)) = *sys::net::NET.lock() {
|
||||
let mut sockets = SocketSet::new(vec![]);
|
||||
|
||||
|
@ -381,45 +383,57 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
None => continue,
|
||||
};
|
||||
if socket.may_recv() {
|
||||
let res = socket.recv(|buf| {
|
||||
if let Some(req) = Request::from(endpoint.addr, buf) {
|
||||
let mut res = Response::new(req.clone());
|
||||
let sep = if req.path == "/" { "" } else { "/" };
|
||||
res.real_path = format!(
|
||||
"{}{}{}",
|
||||
dir,
|
||||
sep,
|
||||
req.path.strip_suffix('/').unwrap_or(&req.path)
|
||||
).replace("//", "/");
|
||||
|
||||
match req.verb.as_str() {
|
||||
"GET" => {
|
||||
get(&req, &mut res)
|
||||
}
|
||||
"PUT" if !read_only => {
|
||||
put(&req, &mut res)
|
||||
}
|
||||
"DELETE" if !read_only => {
|
||||
delete(&req, &mut res)
|
||||
}
|
||||
_ => {
|
||||
let s = b"<h1>Bad Request</h1>\r\n";
|
||||
res.body.extend_from_slice(s);
|
||||
res.code = 400;
|
||||
res.mime = "text/html".to_string();
|
||||
}
|
||||
// The amount of octets queued in the receive buffer may be
|
||||
// larger than the contiguous slice returned by `recv` so
|
||||
// we need to loop over chunks of it until it is empty.
|
||||
let recv_queue = socket.recv_queue();
|
||||
let mut receiving = true;
|
||||
let mut buf = vec![];
|
||||
while receiving {
|
||||
let res = socket.recv(|chunk| {
|
||||
buf.extend_from_slice(chunk);
|
||||
if buf.len() < recv_queue {
|
||||
return (chunk.len(), None);
|
||||
}
|
||||
res.end();
|
||||
println!("{}", res);
|
||||
(buf.len(), Some(res))
|
||||
} else {
|
||||
(0, None)
|
||||
receiving = false;
|
||||
|
||||
let addr = endpoint.addr;
|
||||
if let Some(req) = Request::from(addr, &buf) {
|
||||
let mut res = Response::new(req.clone());
|
||||
res.real_path = join_path(&dir, &req.path);
|
||||
|
||||
match req.verb.as_str() {
|
||||
"GET" => {
|
||||
get(&req, &mut res)
|
||||
}
|
||||
"PUT" if !read_only => {
|
||||
put(&req, &mut res)
|
||||
}
|
||||
"DELETE" if !read_only => {
|
||||
delete(&req, &mut res)
|
||||
}
|
||||
_ => {
|
||||
let s = b"<h1>Bad Request</h1>\r\n";
|
||||
res.body.extend_from_slice(s);
|
||||
res.code = 400;
|
||||
res.mime = "text/html".to_string();
|
||||
}
|
||||
}
|
||||
res.end();
|
||||
println!("{}", res);
|
||||
(chunk.len(), Some(res))
|
||||
} else {
|
||||
(0, None)
|
||||
}
|
||||
});
|
||||
if receiving {
|
||||
continue;
|
||||
}
|
||||
});
|
||||
if let Ok(Some(res)) = res {
|
||||
*keep_alive = res.is_persistent();
|
||||
for chunk in res.buf.chunks(buf_len) {
|
||||
send_queue.push_back(chunk.to_vec());
|
||||
if let Ok(Some(res)) = res {
|
||||
*keep_alive = res.is_persistent();
|
||||
for chunk in res.buf.chunks(buf_len) {
|
||||
send_queue.push_back(chunk.to_vec());
|
||||
}
|
||||
}
|
||||
}
|
||||
if socket.can_send() {
|
||||
|
@ -468,6 +482,15 @@ fn content_type(path: &str) -> String {
|
|||
}.to_string()
|
||||
}
|
||||
|
||||
// Join the requested file path to the root dir of the server
|
||||
fn join_path(dir: &str, path: &str) -> String {
|
||||
debug_assert!(dir.starts_with('/'));
|
||||
debug_assert!(path.starts_with('/'));
|
||||
let path = path.trim_matches('/');
|
||||
let sep = if dir == "/" || path == "" { "" } else { "/" };
|
||||
format!("{}{}{}", dir, sep, path)
|
||||
}
|
||||
|
||||
fn usage() {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
|
@ -491,3 +514,13 @@ fn usage() {
|
|||
csi_option, csi_reset
|
||||
);
|
||||
}
|
||||
|
||||
#[test_case]
|
||||
fn test_join_path() {
|
||||
assert_eq!(join_path("/foo", "/bar/"), "/foo/bar");
|
||||
assert_eq!(join_path("/foo", "/bar"), "/foo/bar");
|
||||
assert_eq!(join_path("/foo", "/"), "/foo");
|
||||
assert_eq!(join_path("/", "/bar/"), "/bar");
|
||||
assert_eq!(join_path("/", "/bar"), "/bar");
|
||||
assert_eq!(join_path("/", "/"), "/");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue