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