1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
use {
crate::{asset, Request, Response, Result},
httparse,
std::{
io::{self, prelude::*, BufReader, Read, Write},
net::{SocketAddr, TcpListener, TcpStream, ToSocketAddrs},
sync::{Arc, Mutex},
},
threadpool::ThreadPool,
};
const MAX_CONNECTIONS: usize = 10;
type Router = Arc<Mutex<crate::Router>>;
pub fn run<T: ToSocketAddrs>(addr: T, router: Router) -> Result<()> {
let pool = ThreadPool::new(MAX_CONNECTIONS);
let server = TcpListener::bind(&addr)?;
let addr = server.local_addr()?;
println!("~ vial running at http://{}", addr);
for stream in server.incoming() {
let router = router.clone();
let stream = stream?;
pool.execute(move || {
if let Err(e) = handle_request(stream, &router) {
eprintln!("!! {}", e);
}
});
}
Ok(())
}
fn handle_request(mut stream: TcpStream, router: &Router) -> Result<()> {
let mut buffer = vec![];
let mut read_buf = [0u8; 512];
let req = loop {
let n = stream.read(&mut read_buf)?;
if n == 0 {
return Err(error!("Empty response"));
}
buffer.extend_from_slice(&read_buf[..n]);
if let Some(req) = Request::from_raw_http_request(&mut buffer)? {
break req;
}
};
write_response(stream, req, router)?;
Ok(())
}
fn write_response(mut stream: TcpStream, mut req: Request, router: &Router) -> Result<()> {
let router = router.lock().unwrap();
let method = req.method().to_string();
let path = req.path().to_string();
let mut response = if asset::exists(req.path()) {
if let Some(req_etag) = req.header("If-None-Match") {
if req_etag == asset::hash(req.path()) {
Response::from(304)
} else {
Response::from_file(req.path())
}
} else {
Response::from_file(req.path())
}
} else if let Some(action) = router.action_for(&mut req) {
action(req)
} else {
Response::from(404).with_body("404 Not Found")
};
let code = response.code;
response.write(stream)?;
println!("{} {} {}", method, code, path);
Ok(())
}