@@ -4,7 +4,7 @@ use std::{
4
4
} ;
5
5
6
6
use tokio:: {
7
- io:: { AsyncReadExt as _, AsyncWriteExt as _} ,
7
+ io:: { AsyncBufReadExt as _, AsyncWriteExt as _, BufReader } ,
8
8
net:: TcpStream ,
9
9
} ;
10
10
@@ -16,13 +16,34 @@ pub async fn handle_client(
16
16
signal : Arc < Signal > ,
17
17
fs : impl FileSystemInterface ,
18
18
) {
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" ;
26
47
let mut file_path = base_dir. to_path_buf ( ) ;
27
48
let mut websocket = None ;
28
49
let path = path. split_once ( '?' ) . map ( |v| v. 0 ) . unwrap_or ( path) ;
@@ -42,33 +63,41 @@ pub async fn handle_client(
42
63
if let Some ( key) = websocket {
43
64
let _ = handle_websocket ( stream, key, signal) . await ;
44
65
} else if path == "/favicon.ico" {
45
- serve_favicon ( & file_path, & mut stream, fs) . await ;
66
+ serve_favicon ( & file_path, & mut stream, fs, head ) . await ;
46
67
} 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
+ {
48
72
serve_500 ( & mut stream) . await ;
49
73
}
50
74
} 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 ( ) {
52
76
serve_500 ( & mut stream) . await ;
53
77
}
54
78
} else {
55
79
serve_404 ( & mut stream) . await ;
56
80
}
57
- } else if let ( Some ( "POST" ) , Some ( "/ping" ) , Some ( _) ) = temp {
58
- let contents = "pong" ;
59
- let response = format ! ( "HTTP/1.1 200 OK\r \n Content-Type: text\r \n \r \n {}" , contents) ;
60
- let _ = stream. write ( response. as_bytes ( ) ) . await ;
61
81
}
82
+ } else if let ( Some ( "POST" ) , Some ( "/ping" ) , Some ( _) ) = temp {
83
+ let contents = "pong" ;
84
+ let response = format ! ( "HTTP/1.1 200 OK\r \n Content-Type: text\r \n \r \n {}" , contents) ;
85
+ let _ = stream. write ( response. as_bytes ( ) ) . await ;
62
86
}
63
87
}
64
88
65
89
async fn serve_directory (
66
90
dir : & Path ,
67
91
stream : & mut TcpStream ,
68
92
fs : impl FileSystemInterface ,
93
+ head : bool ,
69
94
) -> crate :: Result < ( ) > {
70
95
let mut response = String :: new ( ) ;
71
- response. push_str ( "HTTP/1.1 200 OK\r \n Content-Type: text/html\r \n \r \n " ) ;
96
+ response. push_str ( "HTTP/1.1 200 OK\r \n Content-Type: text/html\r \n Connection: close\r \n \r \n " ) ;
97
+ if head {
98
+ let _ = stream. write ( response. as_bytes ( ) ) . await ?;
99
+ return Ok ( ( ) ) ;
100
+ }
72
101
response. push_str ( "<html><body><ul>" ) ;
73
102
let mut entries = fs. get_dir ( dir) . await ?;
74
103
let mut found_index = None ;
@@ -89,7 +118,7 @@ async fn serve_directory(
89
118
90
119
drop ( entries) ;
91
120
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 ?) ;
93
122
}
94
123
95
124
response. push_str ( "</ul></body></html>" ) ;
@@ -101,6 +130,7 @@ async fn serve_file(
101
130
file_path : & Path ,
102
131
stream : & mut TcpStream ,
103
132
fs : impl FileSystemInterface ,
133
+ head : bool ,
104
134
) -> crate :: Result < ( ) > {
105
135
let is_html = file_path
106
136
. as_os_str ( )
@@ -119,11 +149,14 @@ async fn serve_file(
119
149
}
120
150
121
151
let response = format ! (
122
- "HTTP/1.1 200 OK\r \n nContent-Type: {}\r \n Content-Length: {}\r \n \r \n " ,
152
+ "HTTP/1.1 200 OK\r \n nContent-Type: {}\r \n Content-Length: {}\r \n Connection: close \r \ n\r \n " ,
123
153
mime,
124
154
contents. len( )
125
155
) ;
126
156
let _ = stream. write ( response. as_bytes ( ) ) . await ;
157
+ if head {
158
+ return Ok ( ( ) ) ;
159
+ }
127
160
let _ = stream. write ( & contents) . await ;
128
161
Ok ( ( ) )
129
162
}
@@ -138,15 +171,23 @@ async fn serve_500(stream: &mut TcpStream) {
138
171
let _ = stream. write ( response. as_bytes ( ) ) . await ;
139
172
}
140
173
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
+ ) {
142
180
let bytes = match fs. get_file ( path) . await {
143
181
Ok ( mut v) => v. read_to_end ( ) . await ,
144
182
Err ( _) => include_bytes ! ( "../favicon.ico" ) . to_vec ( ) ,
145
183
} ;
146
184
let response = format ! (
147
- "HTTP/1.1 200 OK\r \n Content-Type: image/x-icon\r \n Content-Length: {}\r \n \r \n " ,
185
+ "HTTP/1.1 200 OK\r \n Content-Type: image/x-icon\r \n Content-Length: {}\r \n Connection: close \r \ n\r \n " ,
148
186
bytes. len( )
149
187
) ;
150
188
let _ = stream. write ( response. as_bytes ( ) ) . await ;
189
+ if head {
190
+ return ;
191
+ }
151
192
let _ = stream. write ( & bytes) . await ;
152
193
}
0 commit comments