@@ -4,6 +4,7 @@ use compact_str::CompactString;
44use either:: Either ;
55use eyre:: { anyhow, bail, OptionExt } ;
66use fnv:: FnvHashMap ;
7+ use httparse:: { ParserConfig , Status } ;
78use memchr:: { memchr, memmem} ;
89use regex_lite:: { Match , Regex } ;
910use std:: str:: FromStr ;
@@ -121,27 +122,11 @@ fn to_str(str_like: &[u8]) -> &str {
121122 unsafe { std:: str:: from_utf8_unchecked ( str_like) }
122123}
123124
124- fn parse_headers ( headers : & [ u8 ] ) -> FnvHashMap < Header , & str > {
125- headers
126- . split ( |c| * c == b'\n' )
127- . filter_map ( |hdr_line| {
128- let idx = memchr:: memchr ( b':' , hdr_line) ?;
129- Some ( ( & hdr_line[ ..idx] , & hdr_line[ idx + 1 ..] ) )
130- } )
131- . flat_map ( |( header, content) | {
132- let header_str = to_str ( header) ;
133-
134- let Ok ( header) = header_str. parse :: < Header > ( ) else {
135- return None ;
136- } ;
137-
138- let content = to_str ( content) . trim ( ) ;
139- Some ( ( header, content) )
140- } )
141- . collect :: < FnvHashMap < _ , _ > > ( )
142- }
143-
144125fn parse_body ( body : & [ u8 ] ) -> Option < & str > {
126+ if body. is_empty ( ) {
127+ return None ;
128+ }
129+
145130 if body. first ( ) == Some ( & b'\0' ) {
146131 None
147132 } else {
@@ -156,33 +141,28 @@ fn parse_body(body: &[u8]) -> Option<&str> {
156141/// And probably explode if anything else than a well-formed request is parsed.
157142#[ inline]
158143pub fn parse_http ( request : & [ u8 ] ) -> AnyResult < Request > {
159- // https://www.rfc-editor.org/rfc/rfc9110.html#name-protocol-version
160- let Some ( index) = memmem:: find ( request, b"HTTP/1.1" ) else {
161- bail ! ( "err" ) ;
162- } ;
163-
164- let method_and_resource = & request[ ..index] ;
165- let rest = & request[ index..] ;
166-
167- let mut split = method_and_resource. split ( |c| c. is_ascii_whitespace ( ) ) ;
168- let method = split
169- . next ( )
170- . map ( Method :: try_from)
171- . ok_or_eyre ( "Malformed request." ) ?
172- . map_err ( |_| anyhow ! ( "Unknown http method." ) ) ?;
173- let resource = split. next ( ) . ok_or_eyre ( "Could not find resource." ) ?;
174-
175- let maybe_body = memmem:: find ( rest, b"\r \n \r \n " ) . map ( |idx| ( & rest[ ..idx] , & rest[ idx + 4 ..] ) ) ;
176-
177- let ( headers, body) = if let Some ( ( headers, body) ) = maybe_body {
178- ( parse_headers ( headers) , parse_body ( body) )
179- } else {
180- ( parse_headers ( rest) , None )
144+ let mut headers = [ httparse:: EMPTY_HEADER ; 4 ] ;
145+ let mut req = httparse:: Request :: new ( & mut headers) ;
146+ let body = ParserConfig :: default ( )
147+ . parse_request ( & mut req, request)
148+ . unwrap ( ) ;
149+
150+ let method = Method :: from_str ( req. method . unwrap ( ) ) . unwrap ( ) ;
151+ let resource = req. path . unwrap ( ) ;
152+ let headers = req
153+ . headers
154+ . iter ( )
155+ . map ( |c| ( Header :: from_str ( c. name ) . unwrap ( ) , to_str ( c. value ) ) )
156+ . collect :: < FnvHashMap < _ , _ > > ( ) ;
157+
158+ let body = match body {
159+ Status :: Complete ( idx) => parse_body ( & request[ idx..] ) ,
160+ Status :: Partial => unimplemented ! ( ) ,
181161 } ;
182162
183163 Ok ( Request {
184164 method,
185- resource : to_str ( resource ) ,
165+ resource,
186166 headers,
187167 body,
188168 } )
@@ -199,29 +179,21 @@ mod tests {
199179 let request = parse_http ( sample) . unwrap ( ) ;
200180 assert_eq ! ( request. method, Method :: GET ) ;
201181 assert_eq ! ( request. resource, "/somepath" ) ;
202- assert_eq ! ( request. headers. get( & Header :: HOST ) , Some ( & "ifconfig.me" ) ) ;
182+ assert_eq ! (
183+ request. headers. get( & Header :: CONTENT_TYPE ) ,
184+ Some ( & "text/html; charset=ISO-8859-4" )
185+ ) ;
203186 assert_eq ! ( request. body, Some ( r#"{"json_key": 10}"# ) ) ;
204187 }
205188
206189 #[ test]
207190 fn success_without_body ( ) {
208- let sample = b"GET /somepath HTTP/1.1\n Host: ifconfig.me\n User-Agent: curl/8.5.0\n Accept: */*\n Content-Type: text/html; charset=ISO-8859-4\n " ;
191+ let sample = b"GET /somepath HTTP/1.1\n Host: ifconfig.me\n User-Agent: curl/8.5.0\n Accept: */*\n Content-Type: text/html; charset=ISO-8859-4\r \n \r \ n " ;
209192
210193 let request = parse_http ( sample) . unwrap ( ) ;
211194 assert_eq ! ( request. method, Method :: GET ) ;
212195 assert_eq ! ( request. resource, "/somepath" ) ;
213196 assert_eq ! ( request. headers. get( & Header :: HOST ) , Some ( & "ifconfig.me" ) ) ;
214197 assert_eq ! ( request. body, None ) ;
215198 }
216-
217- #[ test]
218- fn success_no_route ( ) {
219- let sample = b"GET /clientes/1/transacao HTTP/1.1\n Host: localhost\n User-Agent: curl/8.5.0\n Accept: */*\n Content-Type: text/html; charset=ISO-8859-4\r \n \r \n {\" json_key\" : 10}" ;
220-
221- let request = parse_http ( sample) . unwrap ( ) ;
222- assert_eq ! ( request. method, Method :: GET ) ;
223- assert_eq ! ( request. resource, "/somepath" ) ;
224- assert_eq ! ( request. headers. get( & Header :: HOST ) , Some ( & "ifconfig.me" ) ) ;
225- assert_eq ! ( request. body, Some ( r#"{"json_key": 10}"# ) ) ;
226- }
227199}
0 commit comments