Skip to content

Commit 223eb8b

Browse files
committed
head support
1 parent 0d6193c commit 223eb8b

File tree

3 files changed

+77
-25
lines changed

3 files changed

+77
-25
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
11
[package]
22
name = "rusty-live-server"
3-
version = "0.5.2"
3+
version = "0.5.3"
44
edition = "2021"
55

66
[dependencies]
77
base64 = "0.22.1"
88
sha1 = { version = "0.11.0-pre.3", default-features = false }
9-
tokio = { version = "1.44.2", default-features = false, features = ["sync", "rt", "net", "io-util", "fs", "time", "macros", "rt-multi-thread"] }
10-
notify = { version = "8.0.0", default-features = false, features = ["macos_kqueue"], optional = true }
9+
tokio = { version = "1.44.2", default-features = false, features = [
10+
"sync",
11+
"rt",
12+
"net",
13+
"io-util",
14+
"fs",
15+
"time",
16+
"macros",
17+
"rt-multi-thread",
18+
] }
19+
notify = { version = "8.0.0", default-features = false, features = [
20+
"macos_kqueue",
21+
], optional = true }
1122
blake3 = { version = "1.8.1", optional = true }
12-
log = { version ="0.4.27", optional = true }
23+
log = { version = "0.4.27", optional = true }
1324
mime_guess = "2.0.5"
1425

1526
[features]

src/routing.rs

Lines changed: 61 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::{
44
};
55

66
use tokio::{
7-
io::{AsyncReadExt as _, AsyncWriteExt as _},
7+
io::{AsyncBufReadExt as _, AsyncWriteExt as _, BufReader},
88
net::TcpStream,
99
};
1010

@@ -16,13 +16,34 @@ pub async fn handle_client(
1616
signal: Arc<Signal>,
1717
fs: impl FileSystemInterface,
1818
) {
19-
let mut buffer = [0; 512];
20-
if stream.read(&mut buffer).await.is_ok() {
21-
let request = String::from_utf8_lossy(&buffer[..]);
22-
let mut parts = request.split_whitespace();
23-
let protocol = parts.next();
24-
let temp = (protocol, parts.next(), parts.next());
25-
if let (Some("GET"), Some(path), Some(_)) = temp {
19+
let mut reader = BufReader::new(&mut stream);
20+
let mut request = String::new();
21+
let mut line = String::new();
22+
23+
loop {
24+
let bytes_read = reader.read_line(&mut line).await;
25+
line.clear();
26+
let bytes_read = match bytes_read {
27+
Ok(v) => v,
28+
Err(_) => {
29+
return;
30+
}
31+
};
32+
if bytes_read == 0 {
33+
return;
34+
}
35+
36+
request.push_str(&line);
37+
if request.contains("\r\n\r\n") {
38+
break;
39+
}
40+
}
41+
let mut parts = request.split_whitespace();
42+
let protocol = parts.next();
43+
let temp = (protocol, parts.next(), parts.next());
44+
if let (Some(method), Some(path), Some(_)) = temp {
45+
if matches!(method, "GET" | "HEAD") {
46+
let head = method == "HEAD";
2647
let mut file_path = base_dir.to_path_buf();
2748
let mut websocket = None;
2849
let path = path.split_once('?').map(|v| v.0).unwrap_or(path);
@@ -42,33 +63,41 @@ pub async fn handle_client(
4263
if let Some(key) = websocket {
4364
let _ = handle_websocket(stream, key, signal).await;
4465
} else if path == "/favicon.ico" {
45-
serve_favicon(&file_path, &mut stream, fs).await;
66+
serve_favicon(&file_path, &mut stream, fs, head).await;
4667
} else if file_path.is_dir() {
47-
if serve_directory(&file_path, &mut stream, fs).await.is_err() {
68+
if serve_directory(&file_path, &mut stream, fs, head)
69+
.await
70+
.is_err()
71+
{
4872
serve_500(&mut stream).await;
4973
}
5074
} else if file_path.is_file() {
51-
if serve_file(&file_path, &mut stream, fs).await.is_err() {
75+
if serve_file(&file_path, &mut stream, fs, head).await.is_err() {
5276
serve_500(&mut stream).await;
5377
}
5478
} else {
5579
serve_404(&mut stream).await;
5680
}
57-
} else if let (Some("POST"), Some("/ping"), Some(_)) = temp {
58-
let contents = "pong";
59-
let response = format!("HTTP/1.1 200 OK\r\nContent-Type: text\r\n\r\n{}", contents);
60-
let _ = stream.write(response.as_bytes()).await;
6181
}
82+
} else if let (Some("POST"), Some("/ping"), Some(_)) = temp {
83+
let contents = "pong";
84+
let response = format!("HTTP/1.1 200 OK\r\nContent-Type: text\r\n\r\n{}", contents);
85+
let _ = stream.write(response.as_bytes()).await;
6286
}
6387
}
6488

6589
async fn serve_directory(
6690
dir: &Path,
6791
stream: &mut TcpStream,
6892
fs: impl FileSystemInterface,
93+
head: bool,
6994
) -> crate::Result<()> {
7095
let mut response = String::new();
71-
response.push_str("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n");
96+
response.push_str("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n");
97+
if head {
98+
let _ = stream.write(response.as_bytes()).await?;
99+
return Ok(());
100+
}
72101
response.push_str("<html><body><ul>");
73102
let mut entries = fs.get_dir(dir).await?;
74103
let mut found_index = None;
@@ -89,7 +118,7 @@ async fn serve_directory(
89118

90119
drop(entries);
91120
if let Some(found) = found_index {
92-
return Ok(serve_file(&found, stream, fs).await?);
121+
return Ok(serve_file(&found, stream, fs, head).await?);
93122
}
94123

95124
response.push_str("</ul></body></html>");
@@ -101,6 +130,7 @@ async fn serve_file(
101130
file_path: &Path,
102131
stream: &mut TcpStream,
103132
fs: impl FileSystemInterface,
133+
head: bool,
104134
) -> crate::Result<()> {
105135
let is_html = file_path
106136
.as_os_str()
@@ -119,11 +149,14 @@ async fn serve_file(
119149
}
120150

121151
let response = format!(
122-
"HTTP/1.1 200 OK\r\nnContent-Type: {}\r\nContent-Length: {}\r\n\r\n",
152+
"HTTP/1.1 200 OK\r\nnContent-Type: {}\r\nContent-Length: {}\r\nConnection: close\r\n\r\n",
123153
mime,
124154
contents.len()
125155
);
126156
let _ = stream.write(response.as_bytes()).await;
157+
if head {
158+
return Ok(());
159+
}
127160
let _ = stream.write(&contents).await;
128161
Ok(())
129162
}
@@ -138,15 +171,23 @@ async fn serve_500(stream: &mut TcpStream) {
138171
let _ = stream.write(response.as_bytes()).await;
139172
}
140173

141-
async fn serve_favicon(path: &Path, stream: &mut TcpStream, fs: impl FileSystemInterface) {
174+
async fn serve_favicon(
175+
path: &Path,
176+
stream: &mut TcpStream,
177+
fs: impl FileSystemInterface,
178+
head: bool,
179+
) {
142180
let bytes = match fs.get_file(path).await {
143181
Ok(mut v) => v.read_to_end().await,
144182
Err(_) => include_bytes!("../favicon.ico").to_vec(),
145183
};
146184
let response = format!(
147-
"HTTP/1.1 200 OK\r\nContent-Type: image/x-icon\r\nContent-Length: {}\r\n\r\n",
185+
"HTTP/1.1 200 OK\r\nContent-Type: image/x-icon\r\nContent-Length: {}\r\nConnection: close\r\n\r\n",
148186
bytes.len()
149187
);
150188
let _ = stream.write(response.as_bytes()).await;
189+
if head {
190+
return;
191+
}
151192
let _ = stream.write(&bytes).await;
152193
}

0 commit comments

Comments
 (0)