2121#include "ipv4.h"
2222#include "userinput.h"
2323#include "log.h"
24+ #include "picohttpparser.h"
2425
2526#include <assert.h>
2627#include <ctype.h>
@@ -144,6 +145,57 @@ static const char *find_header(
144145}
145146
146147
148+ static inline int header_name_cmp (const struct phr_header header , const char * s )
149+ {
150+ return strncmp (header .name , s , header .name_len );
151+ }
152+
153+
154+ static inline int header_value_cmp (const struct phr_header header , const char * s )
155+ {
156+ return strncmp (header .value , s , header .value_len );
157+ }
158+
159+
160+ /*
161+ * Extract "Content-Size" and "Transfer-Encoding: chunked" from headers.
162+ *
163+ * @param[out] content_size
164+ * @param[out] chunked
165+ * @return 1 in case of success
166+ * < 0 in case of error
167+ */
168+ static int parse_response_header (const struct phr_header * headers , size_t num_headers ,
169+ uint32_t * content_size , int * chunked )
170+ {
171+ uint32_t content_length = 0 ;
172+ int transfer_encoding_chunked = 0 ;
173+
174+ for (int i = 0 ; i != num_headers ; ++ i ) {
175+ if (header_name_cmp (headers [i ], "Content-Length" ) == 0 ) {
176+ long l = strtol (headers [i ].name , NULL , 10 );
177+
178+ if (errno || l < 0 )
179+ return ERR_HTTP_INVALID ;
180+ else if (l > UINT32_MAX )
181+ return ERR_HTTP_TOO_LONG ;
182+ content_length = l ;
183+ }
184+ if (header_name_cmp (headers [i ], "Transfer-Encoding" ) == 0 ) {
185+ if (header_value_cmp (headers [i ], "chunked" ) == 0 )
186+ transfer_encoding_chunked = 1 ;
187+ else
188+ return ERR_HTTP_INVALID ;
189+ }
190+ }
191+ if (content_length && transfer_encoding_chunked )
192+ return ERR_HTTP_INVALID ;
193+ * content_size = content_length ;
194+ * chunked = transfer_encoding_chunked ;
195+ return 1 ;
196+ }
197+
198+
147199/*
148200 * Receives data from the HTTP server.
149201 *
@@ -161,7 +213,7 @@ int http_receive(
161213{
162214 uint32_t capacity = HTTP_BUFFER_SIZE ;
163215 char * buffer ;
164- uint32_t bytes_read = 0 ;
216+ uint32_t bytes_read = 0 , last_bytes_read = 0 ;
165217 uint32_t header_size = 0 ;
166218 uint32_t content_size = 0 ;
167219 int chunked = 0 ;
@@ -183,31 +235,44 @@ int http_receive(
183235 free (buffer );
184236 return ERR_HTTP_SSL ;
185237 }
238+ last_bytes_read = bytes_read ;
186239 bytes_read += n ;
187240
188241 log_debug_details ("%s:\n%s\n" , __func__ , buffer );
189242
190243 if (!header_size ) {
191244 /* Have we reached the end of the HTTP header? */
192- static const char EOH [4 ] = "\r\n\r\n" ;
193- const char * eoh = memmem (buffer , bytes_read ,
194- EOH , sizeof (EOH ));
195-
196- if (eoh ) {
197- header_size = eoh - buffer + sizeof (EOH );
198-
199- /* Get the body size. */
200- const char * header = find_header (buffer ,
201- "Content-Length: " ,
202- header_size );
203-
204- if (header )
205- content_size = atoi (header );
206-
207- if (find_header (buffer ,
208- "Transfer-Encoding: chunked" ,
209- header_size ))
210- chunked = 1 ;
245+ int minor_version , status ;
246+ const char * msg ;
247+ size_t msg_len ;
248+ struct phr_header headers [100 ]; // FIXME
249+ size_t num_headers = ARRAY_SIZE (headers );
250+
251+ n = phr_parse_response (buffer , bytes_read ,
252+ & minor_version , & status ,
253+ & msg , & msg_len ,
254+ headers , & num_headers ,
255+ last_bytes_read );
256+ if (n > 0 ) {
257+ header_size = n ;
258+ n = parse_response_header (headers , num_headers ,
259+ & content_size , & chunked );
260+ if (n < 0 ) {
261+ free (buffer );
262+ return n ;
263+ }
264+ if (content_size > UINT32_MAX - header_size ) {
265+ free (buffer );
266+ return ERR_HTTP_TOO_LONG ;
267+ } else if (content_size == 0 && !chunked ) {
268+ free (buffer );
269+ return ERR_HTTP_INVALID ;
270+ }
271+ } else if (n == -2 ) {
272+ /* response is partial, continue the loop */
273+ } else {
274+ /* failed to parse the response */
275+ assert (n == -1 );
211276 }
212277 }
213278
0 commit comments