3
3
4
4
// Demo websocket server that listens on localhost port 1337.
5
5
// If accessed from a browser it will return a web page that will automatically attempt to
6
- // open a websocket connection to itself. Alternatively, the client.rs example can be used to
6
+ // open a websocket connection to itself. Alternatively, the main example can be used to
7
7
// open a websocket connection directly. The server will echo all Text and Ping messages back to
8
8
// the client as well as responding to any opening and closing handshakes.
9
9
// Note that we are using the standard library in the demo but the websocket library remains no_std
10
10
11
11
use embedded_websocket as ws;
12
+ use httparse:: Request ;
13
+ use once_cell:: sync:: Lazy ;
14
+ use route_recognizer:: Router ;
15
+ use std:: io:: { Read , Write } ;
12
16
use std:: net:: { TcpListener , TcpStream } ;
13
17
use std:: str:: Utf8Error ;
14
18
use std:: thread;
15
- use std:: {
16
- io:: { Read , Write } ,
17
- usize,
18
- } ;
19
19
use ws:: framer:: ReadResult ;
20
20
use ws:: {
21
21
framer:: { Framer , FramerError } ,
22
- WebSocketContext , WebSocketSendMessageType , WebSocketServer ,
22
+ WebSocketSendMessageType , WebSocketServer ,
23
23
} ;
24
24
25
25
type Result < T > = std:: result:: Result < T , WebServerError > ;
26
26
27
27
#[ derive( Debug ) ]
28
28
pub enum WebServerError {
29
29
Io ( std:: io:: Error ) ,
30
- Framer ( FramerError < std :: io :: Error > ) ,
30
+ Framer ( FramerError ) ,
31
31
WebSocket ( ws:: Error ) ,
32
+ HttpError ( String ) ,
32
33
Utf8Error ,
33
34
}
34
35
@@ -38,8 +39,8 @@ impl From<std::io::Error> for WebServerError {
38
39
}
39
40
}
40
41
41
- impl From < FramerError < std :: io :: Error > > for WebServerError {
42
- fn from ( err : FramerError < std :: io :: Error > ) -> WebServerError {
42
+ impl From < FramerError > for WebServerError {
43
+ fn from ( err : FramerError ) -> WebServerError {
43
44
WebServerError :: Framer ( err)
44
45
}
45
46
}
@@ -77,13 +78,24 @@ fn main() -> std::io::Result<()> {
77
78
Ok ( ( ) )
78
79
}
79
80
80
- fn handle_client ( mut stream : TcpStream ) -> Result < ( ) > {
81
- println ! ( "Client connected {}" , stream. peer_addr( ) ?) ;
82
- let mut read_buf = [ 0 ; 4000 ] ;
83
- let mut read_cursor = 0 ;
81
+ type Handler = Box < dyn Fn ( & mut TcpStream , & Request ) -> Result < ( ) > + Send + Sync > ;
82
+
83
+ static ROUTER : Lazy < Router < Handler > > = Lazy :: new ( || {
84
+ let mut router = Router :: new ( ) ;
85
+ router. add ( "/chat" , Box :: new ( handle_chat) as Handler ) ;
86
+ router. add ( "/" , Box :: new ( handle_root) as Handler ) ;
87
+ router
88
+ } ) ;
84
89
85
- if let Some ( websocket_context) = read_header ( & mut stream, & mut read_buf, & mut read_cursor) ? {
90
+ fn handle_chat ( stream : & mut TcpStream , req : & Request ) -> Result < ( ) > {
91
+ println ! ( "Received chat request: {:?}" , req. path) ;
92
+
93
+ if let Some ( websocket_context) =
94
+ ws:: read_http_header ( req. headers . iter ( ) . map ( |f| ( f. name , f. value ) ) ) ?
95
+ {
86
96
// this is a websocket upgrade HTTP request
97
+ let mut read_buf = [ 0 ; 4000 ] ;
98
+ let mut read_cursor = 0 ;
87
99
let mut write_buf = [ 0 ; 4000 ] ;
88
100
let mut frame_buf = [ 0 ; 4000 ] ;
89
101
let mut websocket = WebSocketServer :: new_server ( ) ;
@@ -95,85 +107,65 @@ fn handle_client(mut stream: TcpStream) -> Result<()> {
95
107
) ;
96
108
97
109
// complete the opening handshake with the client
98
- framer. accept ( & mut stream, & websocket_context) ?;
110
+ framer. accept ( stream, & websocket_context) ?;
99
111
println ! ( "Websocket connection opened" ) ;
100
112
101
113
// read websocket frames
102
- while let ReadResult :: Text ( text) = framer. read ( & mut stream, & mut frame_buf) ? {
114
+ while let ReadResult :: Text ( text) = framer. read ( stream, & mut frame_buf) ? {
103
115
println ! ( "Received: {}" , text) ;
104
116
105
117
// send the text back to the client
106
118
framer. write (
107
- & mut stream,
119
+ stream,
108
120
WebSocketSendMessageType :: Text ,
109
121
true ,
110
- text. as_bytes ( ) ,
122
+ format ! ( "shut the fuck up {}" , text) . as_bytes ( ) ,
111
123
) ?
112
124
}
113
125
114
126
println ! ( "Closing websocket connection" ) ;
115
- Ok ( ( ) )
116
- } else {
117
- Ok ( ( ) )
118
127
}
128
+
129
+ Ok ( ( ) )
119
130
}
120
131
121
- fn read_header (
122
- stream : & mut TcpStream ,
123
- read_buf : & mut [ u8 ] ,
124
- read_cursor : & mut usize ,
125
- ) -> Result < Option < WebSocketContext > > {
126
- loop {
127
- let mut headers = [ httparse:: EMPTY_HEADER ; 16 ] ;
128
- let mut request = httparse:: Request :: new ( & mut headers) ;
129
-
130
- let received_size = stream. read ( & mut read_buf[ * read_cursor..] ) ?;
131
-
132
- match request
133
- . parse ( & read_buf[ ..* read_cursor + received_size] )
134
- . unwrap ( )
135
- {
136
- httparse:: Status :: Complete ( len) => {
137
- // if we read exactly the right amount of bytes for the HTTP header then read_cursor would be 0
138
- * read_cursor += received_size - len;
139
- let headers = request. headers . iter ( ) . map ( |f| ( f. name , f. value ) ) ;
140
- match ws:: read_http_header ( headers) ? {
141
- Some ( websocket_context) => match request. path {
142
- Some ( "/chat" ) => {
143
- return Ok ( Some ( websocket_context) ) ;
144
- }
145
- _ => return_404_not_found ( stream, request. path ) ?,
146
- } ,
147
- None => {
148
- handle_non_websocket_http_request ( stream, request. path ) ?;
149
- }
150
- }
151
- return Ok ( None ) ;
152
- }
153
- // keep reading while the HTTP header is incomplete
154
- httparse:: Status :: Partial => * read_cursor += received_size,
155
- }
156
- }
132
+ fn handle_root ( stream : & mut TcpStream , _req : & Request ) -> Result < ( ) > {
133
+ stream. write_all ( & ROOT_HTML . as_bytes ( ) ) ?;
134
+ Ok ( ( ) )
157
135
}
158
136
159
- fn handle_non_websocket_http_request ( stream : & mut TcpStream , path : Option < & str > ) -> Result < ( ) > {
160
- println ! ( "Received file request: {:?}" , path) ;
137
+ fn handle_client ( mut stream : TcpStream ) -> Result < ( ) > {
138
+ println ! ( "Client connected {}" , stream. peer_addr( ) ?) ;
139
+ let mut read_buf = [ 0 ; 4000 ] ;
140
+ let mut read_cursor = 0 ;
161
141
162
- match path {
163
- Some ( "/" ) => stream. write_all ( & ROOT_HTML . as_bytes ( ) ) ?,
164
- unknown_path => {
165
- return_404_not_found ( stream, unknown_path) ?;
142
+ let mut headers = vec ! [ httparse:: EMPTY_HEADER ; 8 ] ;
143
+ let received_size = stream. read ( & mut read_buf[ read_cursor..] ) ?;
144
+ let request = loop {
145
+ let mut request = Request :: new ( & mut headers) ;
146
+ match request. parse ( & read_buf[ ..read_cursor + received_size] ) {
147
+ Ok ( httparse:: Status :: Partial ) => read_cursor += received_size,
148
+ Ok ( httparse:: Status :: Complete ( _) ) => break request,
149
+ Err ( httparse:: Error :: TooManyHeaders ) => {
150
+ headers. resize ( headers. len ( ) * 2 , httparse:: EMPTY_HEADER )
151
+ }
152
+ _ => panic ! ( "http parser error" ) ,
166
153
}
167
154
} ;
168
155
169
- Ok ( ( ) )
170
- }
171
-
172
- fn return_404_not_found ( stream : & mut TcpStream , unknown_path : Option < & str > ) -> Result < ( ) > {
173
- println ! ( "Unknown path: {:?}" , unknown_path) ;
174
- let html = "HTTP/1.1 404 Not Found\r \n Content-Length: 0\r \n Connection: close\r \n \r \n " ;
175
- stream. write_all ( & html. as_bytes ( ) ) ?;
176
- Ok ( ( ) )
156
+ match ROUTER . recognize ( request. path . unwrap_or ( "/" ) ) {
157
+ Ok ( handler) => handler. handler ( ) ( & mut stream, & request) ,
158
+ Err ( e) => {
159
+ println ! ( "Unknown path: {:?}" , request. path) ;
160
+ let html = format ! (
161
+ "HTTP/1.1 404 Not Found\r \n Content-Length: {len}\r \n Connection: close\r \n \r \n {msg}" ,
162
+ len = e. len( ) ,
163
+ msg = e
164
+ ) ;
165
+ stream. write_all ( & html. as_bytes ( ) ) ?;
166
+ Err ( WebServerError :: HttpError ( e) )
167
+ }
168
+ }
177
169
}
178
170
179
171
const ROOT_HTML : & str = "HTTP/1.1 200 OK\r \n Content-Type: text/html; charset=UTF-8\r \n Content-Length: 2590\r \n Connection: close\r \n \r \n <!doctype html>
0 commit comments