1 /*
2  * SPDX-FileCopyrightText: 2018-2021 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 
8 #include <stdlib.h>
9 #include <sys/param.h>
10 #include <esp_log.h>
11 #include <esp_err.h>
12 #include <http_parser.h>
13 
14 #include <esp_http_server.h>
15 #include "esp_httpd_priv.h"
16 #include "osal.h"
17 
18 static const char *TAG = "httpd_parse";
19 
20 typedef struct {
21     /* Parser settings for http_parser_execute() */
22     http_parser_settings settings;
23 
24     /* Request being parsed */
25     struct httpd_req *req;
26 
27     /* Status of the parser describes the part of the
28      * HTTP request packet being processed at any moment.
29      */
30     enum {
31         PARSING_IDLE = 0,
32         PARSING_URL,
33         PARSING_HDR_FIELD,
34         PARSING_HDR_VALUE,
35         PARSING_BODY,
36         PARSING_COMPLETE,
37         PARSING_FAILED
38     } status;
39 
40     /* Response error code in case of PARSING_FAILED */
41     httpd_err_code_t error;
42 
43     /* For storing last callback parameters */
44     struct {
45         const char *at;
46         size_t      length;
47     } last;
48 
49     /* State variables */
50     bool   paused;          /*!< Parser is paused */
51     size_t pre_parsed;      /*!< Length of data to be skipped while parsing */
52     size_t raw_datalen;     /*!< Full length of the raw data in scratch buffer */
53 } parser_data_t;
54 
verify_url(http_parser * parser)55 static esp_err_t verify_url (http_parser *parser)
56 {
57     parser_data_t *parser_data  = (parser_data_t *) parser->data;
58     struct httpd_req *r         = parser_data->req;
59     struct httpd_req_aux *ra    = r->aux;
60     struct http_parser_url *res = &ra->url_parse_res;
61 
62     /* Get previous values of the parser callback arguments */
63     const char *at = parser_data->last.at;
64     size_t  length = parser_data->last.length;
65 
66     if ((r->method = parser->method) < 0) {
67         ESP_LOGW(TAG, LOG_FMT("HTTP Operation not supported"));
68         parser_data->error = HTTPD_501_METHOD_NOT_IMPLEMENTED;
69         return ESP_FAIL;
70     }
71 
72     if (sizeof(r->uri) < (length + 1)) {
73         ESP_LOGW(TAG, LOG_FMT("URI length (%d) greater than supported (%d)"),
74                  length, sizeof(r->uri));
75         parser_data->error = HTTPD_414_URI_TOO_LONG;
76         return ESP_FAIL;
77     }
78 
79     /* Keep URI with terminating null character. Note URI string pointed
80      * by 'at' is not NULL terminated, therefore use length provided by
81      * parser while copying the URI to buffer */
82     strlcpy((char *)r->uri, at, (length + 1));
83     ESP_LOGD(TAG, LOG_FMT("received URI = %s"), r->uri);
84 
85     /* Make sure version is HTTP/1.1 */
86     if ((parser->http_major != 1) && (parser->http_minor != 1)) {
87         ESP_LOGW(TAG, LOG_FMT("unsupported HTTP version = %d.%d"),
88                  parser->http_major, parser->http_minor);
89         parser_data->error = HTTPD_505_VERSION_NOT_SUPPORTED;
90         return ESP_FAIL;
91     }
92 
93     /* Parse URL and keep result for later */
94     http_parser_url_init(res);
95     if (http_parser_parse_url(r->uri, strlen(r->uri),
96                               r->method == HTTP_CONNECT, res)) {
97         ESP_LOGW(TAG, LOG_FMT("http_parser_parse_url failed with errno = %d"),
98                               parser->http_errno);
99         parser_data->error = HTTPD_400_BAD_REQUEST;
100         return ESP_FAIL;
101     }
102     return ESP_OK;
103 }
104 
105 /* http_parser callback on finding url in HTTP request
106  * Will be invoked ATLEAST once every packet
107  */
cb_url(http_parser * parser,const char * at,size_t length)108 static esp_err_t cb_url(http_parser *parser,
109                         const char *at, size_t length)
110 {
111     parser_data_t *parser_data = (parser_data_t *) parser->data;
112 
113     if (parser_data->status == PARSING_IDLE) {
114         ESP_LOGD(TAG, LOG_FMT("message begin"));
115 
116         /* Store current values of the parser callback arguments */
117         parser_data->last.at     = at;
118         parser_data->last.length = 0;
119         parser_data->status      = PARSING_URL;
120     } else if (parser_data->status != PARSING_URL) {
121         ESP_LOGE(TAG, LOG_FMT("unexpected state transition"));
122         parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
123         parser_data->status = PARSING_FAILED;
124         return ESP_FAIL;
125     }
126 
127     ESP_LOGD(TAG, LOG_FMT("processing url = %.*s"), length, at);
128 
129     /* Update length of URL string */
130     if ((parser_data->last.length += length) > HTTPD_MAX_URI_LEN) {
131         ESP_LOGW(TAG, LOG_FMT("URI length (%d) greater than supported (%d)"),
132                  parser_data->last.length, HTTPD_MAX_URI_LEN);
133         parser_data->error = HTTPD_414_URI_TOO_LONG;
134         parser_data->status = PARSING_FAILED;
135         return ESP_FAIL;
136     }
137     return ESP_OK;
138 }
139 
pause_parsing(http_parser * parser,const char * at)140 static esp_err_t pause_parsing(http_parser *parser, const char* at)
141 {
142     parser_data_t *parser_data = (parser_data_t *) parser->data;
143     struct httpd_req *r        = parser_data->req;
144     struct httpd_req_aux *ra   = r->aux;
145 
146     /* The length of data that was not parsed due to interruption
147      * and hence needs to be read again later for parsing */
148     ssize_t unparsed = parser_data->raw_datalen - (at - ra->scratch);
149     if (unparsed < 0) {
150         ESP_LOGE(TAG, LOG_FMT("parsing beyond valid data = %d"), -unparsed);
151         return ESP_ERR_INVALID_STATE;
152     }
153 
154     /* Push back the un-parsed data into pending buffer for
155      * receiving again with httpd_recv_with_opt() later when
156      * read_block() executes */
157     if (unparsed && (unparsed != httpd_unrecv(r, at, unparsed))) {
158         ESP_LOGE(TAG, LOG_FMT("data too large for un-recv = %d"), unparsed);
159         return ESP_FAIL;
160     }
161 
162     /* Signal http_parser to pause execution and save the maximum
163      * possible length, of the yet un-parsed data, that may get
164      * parsed before http_parser_execute() returns. This pre_parsed
165      * length will be updated then to reflect the actual length
166      * that got parsed, and must be skipped when parsing resumes */
167     parser_data->pre_parsed = unparsed;
168     http_parser_pause(parser, 1);
169     parser_data->paused = true;
170     ESP_LOGD(TAG, LOG_FMT("paused"));
171     return ESP_OK;
172 }
173 
continue_parsing(http_parser * parser,size_t length)174 static size_t continue_parsing(http_parser *parser, size_t length)
175 {
176     parser_data_t *data = (parser_data_t *) parser->data;
177 
178     /* Part of the received data may have been parsed earlier
179      * so we must skip that before parsing resumes */
180     length = MIN(length, data->pre_parsed);
181     data->pre_parsed -= length;
182     ESP_LOGD(TAG, LOG_FMT("skip pre-parsed data of size = %d"), length);
183 
184     http_parser_pause(parser, 0);
185     data->paused = false;
186     ESP_LOGD(TAG, LOG_FMT("un-paused"));
187     return length;
188 }
189 
190 /* http_parser callback on header field in HTTP request
191  * May be invoked ATLEAST once every header field
192  */
cb_header_field(http_parser * parser,const char * at,size_t length)193 static esp_err_t cb_header_field(http_parser *parser, const char *at, size_t length)
194 {
195     parser_data_t *parser_data = (parser_data_t *) parser->data;
196     struct httpd_req *r        = parser_data->req;
197     struct httpd_req_aux *ra   = r->aux;
198 
199     /* Check previous status */
200     if (parser_data->status == PARSING_URL) {
201         if (verify_url(parser) != ESP_OK) {
202             /* verify_url would already have set the
203              * error field of parser data, so only setting
204              * status to failed */
205             parser_data->status = PARSING_FAILED;
206             return ESP_FAIL;
207         }
208 
209         ESP_LOGD(TAG, LOG_FMT("headers begin"));
210         /* Last at is set to start of scratch where headers
211          * will be received next */
212         parser_data->last.at     = ra->scratch;
213         parser_data->last.length = 0;
214         parser_data->status      = PARSING_HDR_FIELD;
215 
216         /* Stop parsing for now and give control to process */
217         if (pause_parsing(parser, at) != ESP_OK) {
218             parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
219             parser_data->status = PARSING_FAILED;
220             return ESP_FAIL;
221         }
222     } else if (parser_data->status == PARSING_HDR_VALUE) {
223         /* Overwrite terminator (CRLFs) following last header
224          * (key: value) pair with null characters */
225         char *term_start = (char *)parser_data->last.at + parser_data->last.length;
226         memset(term_start, '\0', at - term_start);
227 
228         /* Store current values of the parser callback arguments */
229         parser_data->last.at     = at;
230         parser_data->last.length = 0;
231         parser_data->status      = PARSING_HDR_FIELD;
232 
233         /* Increment header count */
234         ra->req_hdrs_count++;
235     } else if (parser_data->status != PARSING_HDR_FIELD) {
236         ESP_LOGE(TAG, LOG_FMT("unexpected state transition"));
237         parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
238         parser_data->status = PARSING_FAILED;
239         return ESP_FAIL;
240     }
241 
242     ESP_LOGD(TAG, LOG_FMT("processing field = %.*s"), length, at);
243 
244     /* Update length of header string */
245     parser_data->last.length += length;
246     return ESP_OK;
247 }
248 
249 /* http_parser callback on header value in HTTP request.
250  * May be invoked ATLEAST once every header value
251  */
cb_header_value(http_parser * parser,const char * at,size_t length)252 static esp_err_t cb_header_value(http_parser *parser, const char *at, size_t length)
253 {
254     parser_data_t *parser_data = (parser_data_t *) parser->data;
255 
256     /* Check previous status */
257     if (parser_data->status == PARSING_HDR_FIELD) {
258         /* Store current values of the parser callback arguments */
259         parser_data->last.at     = at;
260         parser_data->last.length = 0;
261         parser_data->status      = PARSING_HDR_VALUE;
262 
263         if (length == 0) {
264             /* As per behavior of http_parser, when length > 0,
265              * `at` points to the start of CRLF. But, in the
266              * case when header value is empty (zero length),
267              * then `at` points to the position right after
268              * the CRLF. Since for our purpose we need `last.at`
269              * to point to exactly where the CRLF starts, it
270              * needs to be adjusted by the right offset */
271             char *at_adj = (char *)parser_data->last.at;
272             /* Find the end of header field string */
273             while (*(--at_adj) != ':');
274             /* Now skip leading spaces' */
275             while (*(++at_adj) == ' ');
276             /* Now we are at the right position */
277             parser_data->last.at = at_adj;
278         }
279     } else if (parser_data->status != PARSING_HDR_VALUE) {
280         ESP_LOGE(TAG, LOG_FMT("unexpected state transition"));
281         parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
282         parser_data->status = PARSING_FAILED;
283         return ESP_FAIL;
284     }
285 
286     ESP_LOGD(TAG, LOG_FMT("processing value = %.*s"), length, at);
287 
288     /* Update length of header string */
289     parser_data->last.length += length;
290     return ESP_OK;
291 }
292 
293 /* http_parser callback on completing headers in HTTP request.
294  * Will be invoked ONLY once every packet
295  */
cb_headers_complete(http_parser * parser)296 static esp_err_t cb_headers_complete(http_parser *parser)
297 {
298     parser_data_t *parser_data = (parser_data_t *) parser->data;
299     struct httpd_req *r        = parser_data->req;
300     struct httpd_req_aux *ra   = r->aux;
301 
302     /* Check previous status */
303     if (parser_data->status == PARSING_URL) {
304         ESP_LOGD(TAG, LOG_FMT("no headers"));
305         if (verify_url(parser) != ESP_OK) {
306             /* verify_url would already have set the
307              * error field of parser data, so only setting
308              * status to failed */
309             parser_data->status = PARSING_FAILED;
310             return ESP_FAIL;
311         }
312     } else if (parser_data->status == PARSING_HDR_VALUE) {
313         /* Locate end of last header */
314         char *at = (char *)parser_data->last.at + parser_data->last.length;
315 
316         /* Check if there is data left to parse. This value should
317          * at least be equal to the number of line terminators, i.e. 2 */
318         ssize_t remaining_length = parser_data->raw_datalen - (at - ra->scratch);
319         if (remaining_length < 2) {
320             ESP_LOGE(TAG, LOG_FMT("invalid length of data remaining to be parsed"));
321             parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
322             parser_data->status = PARSING_FAILED;
323             return ESP_FAIL;
324         }
325 
326         /* Locate end of headers section by skipping the remaining
327          * two line terminators. No assumption is made here about the
328          * termination sequence used apart from the necessity that it
329          * must end with an LF, because:
330          *      1) some clients may send non standard LFs instead of
331          *         CRLFs for indicating termination.
332          *      2) it is the responsibility of http_parser to check
333          *         that the termination is either CRLF or LF and
334          *         not any other sequence */
335         unsigned short remaining_terminators = 2;
336         while (remaining_length-- && remaining_terminators) {
337             if (*at == '\n') {
338                 remaining_terminators--;
339             }
340             /* Overwrite termination characters with null */
341             *(at++) = '\0';
342         }
343         if (remaining_terminators) {
344             ESP_LOGE(TAG, LOG_FMT("incomplete termination of headers"));
345             parser_data->error = HTTPD_400_BAD_REQUEST;
346             parser_data->status = PARSING_FAILED;
347             return ESP_FAIL;
348         }
349 
350         /* Place the parser ptr right after the end of headers section */
351         parser_data->last.at = at;
352 
353         /* Increment header count */
354         ra->req_hdrs_count++;
355     } else {
356         ESP_LOGE(TAG, LOG_FMT("unexpected state transition"));
357         parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
358         parser_data->status = PARSING_FAILED;
359         return ESP_FAIL;
360     }
361 
362     /* In absence of body/chunked encoding, http_parser sets content_len to -1 */
363     r->content_len = ((int)parser->content_length != -1 ?
364                       parser->content_length : 0);
365 
366     ESP_LOGD(TAG, LOG_FMT("bytes read     = %d"),  parser->nread);
367     ESP_LOGD(TAG, LOG_FMT("content length = %zu"), r->content_len);
368 
369     /* Handle upgrade requests - only WebSocket is supported for now */
370     if (parser->upgrade) {
371 #ifdef CONFIG_HTTPD_WS_SUPPORT
372         ESP_LOGD(TAG, LOG_FMT("Got an upgrade request"));
373 
374         /* If there's no "Upgrade" header field, then it's not WebSocket. */
375         char ws_upgrade_hdr_val[] = "websocket";
376         if (httpd_req_get_hdr_value_str(r, "Upgrade", ws_upgrade_hdr_val, sizeof(ws_upgrade_hdr_val)) != ESP_OK) {
377             ESP_LOGW(TAG, LOG_FMT("Upgrade header does not match the length of \"websocket\""));
378             parser_data->error = HTTPD_400_BAD_REQUEST;
379             parser_data->status = PARSING_FAILED;
380             return ESP_FAIL;
381         }
382 
383         /* If "Upgrade" field's key is not "websocket", then we should also forget about it. */
384         if (strcasecmp("websocket", ws_upgrade_hdr_val) != 0) {
385             ESP_LOGW(TAG, LOG_FMT("Upgrade header found but it's %s"), ws_upgrade_hdr_val);
386             parser_data->error = HTTPD_400_BAD_REQUEST;
387             parser_data->status = PARSING_FAILED;
388             return ESP_FAIL;
389         }
390 
391         /* Now set handshake flag to true */
392         ra->ws_handshake_detect = true;
393 #else
394         ESP_LOGD(TAG, LOG_FMT("WS functions has been disabled, Upgrade request is not supported."));
395         parser_data->error = HTTPD_400_BAD_REQUEST;
396         parser_data->status = PARSING_FAILED;
397         return ESP_FAIL;
398 #endif
399     }
400 
401     parser_data->status = PARSING_BODY;
402     ra->remaining_len = r->content_len;
403     return ESP_OK;
404 }
405 
406 /* Last http_parser callback if body present in HTTP request.
407  * Will be invoked ONLY once every packet
408  */
cb_on_body(http_parser * parser,const char * at,size_t length)409 static esp_err_t cb_on_body(http_parser *parser, const char *at, size_t length)
410 {
411     parser_data_t *parser_data = (parser_data_t *) parser->data;
412 
413     /* Check previous status */
414     if (parser_data->status != PARSING_BODY) {
415         ESP_LOGE(TAG, LOG_FMT("unexpected state transition"));
416         parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
417         parser_data->status = PARSING_FAILED;
418         return ESP_FAIL;
419     }
420 
421     /* Pause parsing so that if part of another packet
422      * is in queue then it doesn't get parsed, which
423      * may reset the parser state and cause current
424      * request packet to be lost */
425     if (pause_parsing(parser, at) != ESP_OK) {
426         parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
427         parser_data->status = PARSING_FAILED;
428         return ESP_FAIL;
429     }
430 
431     parser_data->last.at     = 0;
432     parser_data->last.length = 0;
433     parser_data->status      = PARSING_COMPLETE;
434     ESP_LOGD(TAG, LOG_FMT("body begins"));
435     return ESP_OK;
436 }
437 
438 /* Last http_parser callback if body absent in HTTP request.
439  * Will be invoked ONLY once every packet
440  */
cb_no_body(http_parser * parser)441 static esp_err_t cb_no_body(http_parser *parser)
442 {
443     parser_data_t *parser_data = (parser_data_t *) parser->data;
444 
445     /* Check previous status */
446     if (parser_data->status == PARSING_URL) {
447         ESP_LOGD(TAG, LOG_FMT("no headers"));
448         if (verify_url(parser) != ESP_OK) {
449             /* verify_url would already have set the
450              * error field of parser data, so only setting
451              * status to failed */
452             parser_data->status = PARSING_FAILED;
453             return ESP_FAIL;
454         }
455     } else if (parser_data->status != PARSING_BODY) {
456         ESP_LOGE(TAG, LOG_FMT("unexpected state transition"));
457         parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
458         parser_data->status = PARSING_FAILED;
459         return ESP_FAIL;
460     }
461 
462     /* Pause parsing so that if part of another packet
463      * is in queue then it doesn't get parsed, which
464      * may reset the parser state and cause current
465      * request packet to be lost */
466     if (pause_parsing(parser, parser_data->last.at) != ESP_OK) {
467         parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
468         parser_data->status = PARSING_FAILED;
469         return ESP_FAIL;
470     }
471 
472     parser_data->last.at     = 0;
473     parser_data->last.length = 0;
474     parser_data->status      = PARSING_COMPLETE;
475     ESP_LOGD(TAG, LOG_FMT("message complete"));
476     return ESP_OK;
477 }
478 
read_block(httpd_req_t * req,size_t offset,size_t length)479 static int read_block(httpd_req_t *req, size_t offset, size_t length)
480 {
481     struct httpd_req_aux *raux  = req->aux;
482 
483     /* Limits the read to scratch buffer size */
484     ssize_t buf_len = MIN(length, (sizeof(raux->scratch) - offset));
485     if (buf_len <= 0) {
486         return 0;
487     }
488 
489     /* Receive data into buffer. If data is pending (from unrecv) then return
490      * immediately after receiving pending data, as pending data may just complete
491      * this request packet. */
492     int nbytes = httpd_recv_with_opt(req, raux->scratch + offset, buf_len, true);
493     if (nbytes < 0) {
494         ESP_LOGD(TAG, LOG_FMT("error in httpd_recv"));
495         /* If timeout occurred allow the
496          * situation to be handled */
497         if (nbytes == HTTPD_SOCK_ERR_TIMEOUT) {
498             /* Invoke error handler which may return ESP_OK
499              * to signal for retrying call to recv(), else it may
500              * return ESP_FAIL to signal for closure of socket */
501             return (httpd_req_handle_err(req, HTTPD_408_REQ_TIMEOUT) == ESP_OK) ?
502                     HTTPD_SOCK_ERR_TIMEOUT : HTTPD_SOCK_ERR_FAIL;
503         }
504         /* Some socket error occurred. Return failure
505          * to force closure of underlying socket.
506          * Error message is not sent as socket may not
507          * be valid anymore */
508         return HTTPD_SOCK_ERR_FAIL;
509     } else if (nbytes == 0) {
510         ESP_LOGD(TAG, LOG_FMT("connection closed"));
511         /* Connection closed by client so no
512          * need to send error response */
513         return HTTPD_SOCK_ERR_FAIL;
514     }
515 
516     ESP_LOGD(TAG, LOG_FMT("received HTTP request block size = %d"), nbytes);
517     return nbytes;
518 }
519 
parse_block(http_parser * parser,size_t offset,size_t length)520 static int parse_block(http_parser *parser, size_t offset, size_t length)
521 {
522     parser_data_t        *data  = (parser_data_t *)(parser->data);
523     httpd_req_t          *req   = data->req;
524     struct httpd_req_aux *raux  = req->aux;
525     size_t nparsed = 0;
526 
527     if (!length) {
528         /* Parsing is still happening but nothing to
529          * parse means no more space left on buffer,
530          * therefore it can be inferred that the
531          * request URI/header must be too long */
532         ESP_LOGW(TAG, LOG_FMT("request URI/header too long"));
533         switch (data->status) {
534             case PARSING_URL:
535                 data->error = HTTPD_414_URI_TOO_LONG;
536                 break;
537             case PARSING_HDR_FIELD:
538             case PARSING_HDR_VALUE:
539                 data->error = HTTPD_431_REQ_HDR_FIELDS_TOO_LARGE;
540                 break;
541             default:
542                 ESP_LOGE(TAG, LOG_FMT("unexpected state"));
543                 data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
544                 break;
545         }
546         data->status = PARSING_FAILED;
547         return -1;
548     }
549 
550     /* Un-pause the parsing if paused */
551     if (data->paused) {
552         nparsed = continue_parsing(parser, length);
553         length -= nparsed;
554         offset += nparsed;
555         if (!length) {
556             return nparsed;
557         }
558     }
559 
560     /* Execute http_parser */
561     nparsed = http_parser_execute(parser, &data->settings,
562                                   raux->scratch + offset, length);
563 
564     /* Check state */
565     if (data->status == PARSING_FAILED) {
566         /* It is expected that the error field of
567          * parser data should have been set by now */
568         ESP_LOGW(TAG, LOG_FMT("parsing failed"));
569         return -1;
570     } else if (data->paused) {
571         /* Update the value of pre_parsed which was set when
572          * pause_parsing() was called. (length - nparsed) is
573          * the length of the data that will need to be parsed
574          * again later and hence must be deducted from the
575          * pre_parsed length */
576         data->pre_parsed -= (length - nparsed);
577         return 0;
578     } else if (nparsed != length) {
579         /* http_parser error */
580         data->error  = HTTPD_400_BAD_REQUEST;
581         data->status = PARSING_FAILED;
582         ESP_LOGW(TAG, LOG_FMT("incomplete (%d/%d) with parser error = %d"),
583                  nparsed, length, parser->http_errno);
584         return -1;
585     }
586 
587     /* Return with the total length of the request packet
588      * that has been parsed till now */
589     ESP_LOGD(TAG, LOG_FMT("parsed block size = %d"), offset + nparsed);
590     return offset + nparsed;
591 }
592 
parse_init(httpd_req_t * r,http_parser * parser,parser_data_t * data)593 static void parse_init(httpd_req_t *r, http_parser *parser, parser_data_t *data)
594 {
595     /* Initialize parser data */
596     memset(data, 0, sizeof(parser_data_t));
597     data->req = r;
598 
599     /* Initialize parser */
600     http_parser_init(parser, HTTP_REQUEST);
601     parser->data = (void *)data;
602 
603     /* Initialize parser settings */
604     http_parser_settings_init(&data->settings);
605 
606     /* Set parser callbacks */
607     data->settings.on_url              = cb_url;
608     data->settings.on_header_field     = cb_header_field;
609     data->settings.on_header_value     = cb_header_value;
610     data->settings.on_headers_complete = cb_headers_complete;
611     data->settings.on_body             = cb_on_body;
612     data->settings.on_message_complete = cb_no_body;
613 }
614 
615 /* Function that receives TCP data and runs parser on it
616  */
httpd_parse_req(struct httpd_data * hd)617 static esp_err_t httpd_parse_req(struct httpd_data *hd)
618 {
619     httpd_req_t *r = &hd->hd_req;
620     int blk_len,  offset;
621     http_parser   parser;
622     parser_data_t parser_data;
623 
624     /* Initialize parser */
625     parse_init(r, &parser, &parser_data);
626 
627     /* Set offset to start of scratch buffer */
628     offset = 0;
629     do {
630         /* Read block into scratch buffer */
631         if ((blk_len = read_block(r, offset, PARSER_BLOCK_SIZE)) < 0) {
632             if (blk_len == HTTPD_SOCK_ERR_TIMEOUT) {
633                 /* Retry read in case of non-fatal timeout error.
634                  * read_block() ensures that the timeout error is
635                  * handled properly so that this doesn't get stuck
636                  * in an infinite loop */
637                 continue;
638             }
639             /* If not HTTPD_SOCK_ERR_TIMEOUT, returned error must
640              * be HTTPD_SOCK_ERR_FAIL which means we need to return
641              * failure and thereby close the underlying socket */
642             return ESP_FAIL;
643         }
644 
645         /* This is used by the callbacks to track
646          * data usage of the buffer */
647         parser_data.raw_datalen = blk_len + offset;
648 
649         /* Parse data block from buffer */
650         if ((offset = parse_block(&parser, offset, blk_len)) < 0) {
651             /* HTTP error occurred.
652              * Send error code as response status and
653              * invoke error handler */
654             return httpd_req_handle_err(r, parser_data.error);
655         }
656     } while (parser_data.status != PARSING_COMPLETE);
657 
658     ESP_LOGD(TAG, LOG_FMT("parsing complete"));
659     return httpd_uri(hd);
660 }
661 
init_req(httpd_req_t * r,httpd_config_t * config)662 static void init_req(httpd_req_t *r, httpd_config_t *config)
663 {
664     r->handle = 0;
665     r->method = 0;
666     memset((char*)r->uri, 0, sizeof(r->uri));
667     r->content_len = 0;
668     r->aux = 0;
669     r->user_ctx = 0;
670     r->sess_ctx = 0;
671     r->free_ctx = 0;
672     r->ignore_sess_ctx_changes = 0;
673 }
674 
init_req_aux(struct httpd_req_aux * ra,httpd_config_t * config)675 static void init_req_aux(struct httpd_req_aux *ra, httpd_config_t *config)
676 {
677     ra->sd = 0;
678     memset(ra->scratch, 0, sizeof(ra->scratch));
679     ra->remaining_len = 0;
680     ra->status = 0;
681     ra->content_type = 0;
682     ra->first_chunk_sent = 0;
683     ra->req_hdrs_count = 0;
684     ra->resp_hdrs_count = 0;
685 #if CONFIG_HTTPD_WS_SUPPORT
686     ra->ws_handshake_detect = false;
687 #endif
688     memset(ra->resp_hdrs, 0, config->max_resp_headers * sizeof(struct resp_hdr));
689 }
690 
httpd_req_cleanup(httpd_req_t * r)691 static void httpd_req_cleanup(httpd_req_t *r)
692 {
693     struct httpd_req_aux *ra = r->aux;
694 
695     /* Check if the context has changed and needs to be cleared */
696     if ((r->ignore_sess_ctx_changes == false) && (ra->sd->ctx != r->sess_ctx)) {
697         httpd_sess_free_ctx(ra->sd->ctx, ra->sd->free_ctx);
698     }
699 
700 #if CONFIG_HTTPD_WS_SUPPORT
701     /* Close the socket when a WebSocket Close request is received */
702     if (ra->sd->ws_close) {
703         ESP_LOGD(TAG, LOG_FMT("Try closing WS connection at FD: %d"), ra->sd->fd);
704         httpd_sess_trigger_close(r->handle, ra->sd->fd);
705     }
706 #endif
707 
708     /* Retrieve session info from the request into the socket database. */
709     ra->sd->ctx = r->sess_ctx;
710     ra->sd->free_ctx = r->free_ctx;
711     ra->sd->ignore_sess_ctx_changes = r->ignore_sess_ctx_changes;
712 
713     /* Clear out the request and request_aux structures */
714     ra->sd = NULL;
715     r->handle = NULL;
716     r->aux = NULL;
717     r->user_ctx = NULL;
718 }
719 
720 /* Function that processes incoming TCP data and
721  * updates the http request data httpd_req_t
722  */
httpd_req_new(struct httpd_data * hd,struct sock_db * sd)723 esp_err_t httpd_req_new(struct httpd_data *hd, struct sock_db *sd)
724 {
725     httpd_req_t *r = &hd->hd_req;
726     init_req(r, &hd->config);
727     init_req_aux(&hd->hd_req_aux, &hd->config);
728     r->handle = hd;
729     r->aux = &hd->hd_req_aux;
730 
731     /* Associate the request to the socket */
732     struct httpd_req_aux *ra = r->aux;
733     ra->sd = sd;
734 
735     /* Set defaults */
736     ra->status = (char *)HTTPD_200;
737     ra->content_type = (char *)HTTPD_TYPE_TEXT;
738     ra->first_chunk_sent = false;
739 
740     /* Copy session info to the request */
741     r->sess_ctx = sd->ctx;
742     r->free_ctx = sd->free_ctx;
743     r->ignore_sess_ctx_changes = sd->ignore_sess_ctx_changes;
744 
745     esp_err_t ret;
746 
747 #ifdef CONFIG_HTTPD_WS_SUPPORT
748     /* Copy user_ctx to the request */
749     r->user_ctx = sd->ws_user_ctx;
750     /* Handle WebSocket */
751     ESP_LOGD(TAG, LOG_FMT("New request, has WS? %s, sd->ws_handler valid? %s, sd->ws_close? %s"),
752              sd->ws_handshake_done ? "Yes" : "No",
753              sd->ws_handler != NULL ? "Yes" : "No",
754              sd->ws_close ? "Yes" : "No");
755     if (sd->ws_handshake_done && sd->ws_handler != NULL) {
756         ret = httpd_ws_get_frame_type(r);
757         ESP_LOGD(TAG, LOG_FMT("New WS request from existing socket, ws_type=%d"), ra->ws_type);
758 
759         if (ra->ws_type == HTTPD_WS_TYPE_CLOSE) {
760             /*  Only mark ws_close to true if it's a CLOSE frame */
761             sd->ws_close = true;
762         } else if (ra->ws_type == HTTPD_WS_TYPE_PONG) {
763             /* Pass the PONG frames to the handler as well, as user app might send PINGs */
764             ESP_LOGD(TAG, LOG_FMT("Received PONG frame"));
765         }
766 
767         /* Call handler if it's a non-control frame (or if handler requests control frames, as well) */
768         if (ret == ESP_OK &&
769             (ra->ws_type < HTTPD_WS_TYPE_CLOSE || sd->ws_control_frames)) {
770             ret = sd->ws_handler(r);
771         }
772 
773         if (ret != ESP_OK) {
774             httpd_req_cleanup(r);
775         }
776         return ret;
777     }
778 #endif
779 
780     /* Parse request */
781     ret = httpd_parse_req(hd);
782     if (ret != ESP_OK) {
783         httpd_req_cleanup(r);
784     }
785     return ret;
786 }
787 
788 /* Function that resets the http request data
789  */
httpd_req_delete(struct httpd_data * hd)790 esp_err_t httpd_req_delete(struct httpd_data *hd)
791 {
792     httpd_req_t *r = &hd->hd_req;
793     struct httpd_req_aux *ra = r->aux;
794 
795     /* Finish off reading any pending/leftover data */
796     while (ra->remaining_len) {
797         /* Any length small enough not to overload the stack, but large
798          * enough to finish off the buffers fast */
799         char dummy[CONFIG_HTTPD_PURGE_BUF_LEN];
800         int recv_len = MIN(sizeof(dummy), ra->remaining_len);
801         recv_len = httpd_req_recv(r, dummy, recv_len);
802         if (recv_len <= 0) {
803             httpd_req_cleanup(r);
804             return ESP_FAIL;
805         }
806 
807         ESP_LOGD(TAG, LOG_FMT("purging data size : %d bytes"), recv_len);
808 
809 #ifdef CONFIG_HTTPD_LOG_PURGE_DATA
810         /* Enabling this will log discarded binary HTTP content data at
811          * Debug level. For large content data this may not be desirable
812          * as it will clutter the log */
813         ESP_LOGD(TAG, "================= PURGED DATA =================");
814         ESP_LOG_BUFFER_HEX_LEVEL(TAG, dummy, recv_len, ESP_LOG_DEBUG);
815         ESP_LOGD(TAG, "===============================================");
816 #endif
817     }
818 
819     httpd_req_cleanup(r);
820     return ESP_OK;
821 }
822 
823 /* Validates the request to prevent users from calling APIs, that are to
824  * be called only inside URI handler, outside the handler context
825  */
httpd_validate_req_ptr(httpd_req_t * r)826 bool httpd_validate_req_ptr(httpd_req_t *r)
827 {
828     if (r) {
829         struct httpd_data *hd = (struct httpd_data *) r->handle;
830         if (hd) {
831             /* Check if this function is running in the context of
832              * the correct httpd server thread */
833             if (httpd_os_thread_handle() == hd->hd_td.handle) {
834                 return true;
835             }
836         }
837     }
838     return false;
839 }
840 
841 /* Helper function to get a URL query tag from a query string of the type param1=val1&param2=val2 */
httpd_query_key_value(const char * qry_str,const char * key,char * val,size_t val_size)842 esp_err_t httpd_query_key_value(const char *qry_str, const char *key, char *val, size_t val_size)
843 {
844     if (qry_str == NULL || key == NULL || val == NULL) {
845         return ESP_ERR_INVALID_ARG;
846     }
847 
848     const char   *qry_ptr = qry_str;
849     const size_t  buf_len = val_size;
850 
851     while (strlen(qry_ptr)) {
852         /* Search for the '=' character. Else, it would mean
853          * that the parameter is invalid */
854         const char *val_ptr = strchr(qry_ptr, '=');
855         if (!val_ptr) {
856             break;
857         }
858         size_t offset = val_ptr - qry_ptr;
859 
860         /* If the key, does not match, continue searching.
861          * Compare lengths first as key from url is not
862          * null terminated (has '=' in the end) */
863         if ((offset != strlen(key)) ||
864             (strncasecmp(qry_ptr, key, offset))) {
865             /* Get the name=val string. Multiple name=value pairs
866              * are separated by '&' */
867             qry_ptr = strchr(val_ptr, '&');
868             if (!qry_ptr) {
869                 break;
870             }
871             qry_ptr++;
872             continue;
873         }
874 
875         /* Locate start of next query */
876         qry_ptr = strchr(++val_ptr, '&');
877         /* Or this could be the last query, in which
878          * case get to the end of query string */
879         if (!qry_ptr) {
880             qry_ptr = val_ptr + strlen(val_ptr);
881         }
882 
883         /* Update value length, including one byte for null */
884         val_size = qry_ptr - val_ptr + 1;
885 
886         /* Copy value to the caller's buffer. */
887         strlcpy(val, val_ptr, MIN(val_size, buf_len));
888 
889         /* If buffer length is smaller than needed, return truncation error */
890         if (buf_len < val_size) {
891             return ESP_ERR_HTTPD_RESULT_TRUNC;
892         }
893         return ESP_OK;
894     }
895     ESP_LOGD(TAG, LOG_FMT("key %s not found"), key);
896     return ESP_ERR_NOT_FOUND;
897 }
898 
httpd_req_get_url_query_len(httpd_req_t * r)899 size_t httpd_req_get_url_query_len(httpd_req_t *r)
900 {
901     if (r == NULL) {
902         return 0;
903     }
904 
905     if (!httpd_valid_req(r)) {
906         return 0;
907     }
908 
909     struct httpd_req_aux   *ra  = r->aux;
910     struct http_parser_url *res = &ra->url_parse_res;
911 
912     /* Check if query field is present in the URL */
913     if (res->field_set & (1 << UF_QUERY)) {
914         return res->field_data[UF_QUERY].len;
915     }
916     return 0;
917 }
918 
httpd_req_get_url_query_str(httpd_req_t * r,char * buf,size_t buf_len)919 esp_err_t httpd_req_get_url_query_str(httpd_req_t *r, char *buf, size_t buf_len)
920 {
921     if (r == NULL || buf == NULL) {
922         return ESP_ERR_INVALID_ARG;
923     }
924 
925     if (!httpd_valid_req(r)) {
926         return ESP_ERR_HTTPD_INVALID_REQ;
927     }
928 
929     struct httpd_req_aux   *ra  = r->aux;
930     struct http_parser_url *res = &ra->url_parse_res;
931 
932     /* Check if query field is present in the URL */
933     if (res->field_set & (1 << UF_QUERY)) {
934         const char *qry = r->uri + res->field_data[UF_QUERY].off;
935 
936         /* Minimum required buffer len for keeping
937          * null terminated query string */
938         size_t min_buf_len = res->field_data[UF_QUERY].len + 1;
939 
940         strlcpy(buf, qry, MIN(buf_len, min_buf_len));
941         if (buf_len < min_buf_len) {
942             return ESP_ERR_HTTPD_RESULT_TRUNC;
943         }
944         return ESP_OK;
945     }
946     return ESP_ERR_NOT_FOUND;
947 }
948 
949 /* Get the length of the value string of a header request field */
httpd_req_get_hdr_value_len(httpd_req_t * r,const char * field)950 size_t httpd_req_get_hdr_value_len(httpd_req_t *r, const char *field)
951 {
952     if (r == NULL || field == NULL) {
953         return 0;
954     }
955 
956     if (!httpd_valid_req(r)) {
957         return 0;
958     }
959 
960     struct httpd_req_aux *ra = r->aux;
961     const char   *hdr_ptr = ra->scratch;         /*!< Request headers are kept in scratch buffer */
962     unsigned      count   = ra->req_hdrs_count;  /*!< Count set during parsing  */
963 
964     while (count--) {
965         /* Search for the ':' character. Else, it would mean
966          * that the field is invalid
967          */
968         const char *val_ptr = strchr(hdr_ptr, ':');
969         if (!val_ptr) {
970             break;
971         }
972 
973         /* If the field, does not match, continue searching.
974          * Compare lengths first as field from header is not
975          * null terminated (has ':' in the end).
976          */
977         if ((val_ptr - hdr_ptr != strlen(field)) ||
978             (strncasecmp(hdr_ptr, field, strlen(field)))) {
979             if (count) {
980                 /* Jump to end of header field-value string */
981                 hdr_ptr = 1 + strchr(hdr_ptr, '\0');
982 
983                 /* Skip all null characters (with which the line
984                  * terminators had been overwritten) */
985                 while (*hdr_ptr == '\0') {
986                     hdr_ptr++;
987                 }
988             }
989             continue;
990         }
991 
992         /* Skip ':' */
993         val_ptr++;
994 
995         /* Skip preceding space */
996         while ((*val_ptr != '\0') && (*val_ptr == ' ')) {
997             val_ptr++;
998         }
999         return strlen(val_ptr);
1000     }
1001     return 0;
1002 }
1003 
1004 /* Get the value of a field from the request headers */
httpd_req_get_hdr_value_str(httpd_req_t * r,const char * field,char * val,size_t val_size)1005 esp_err_t httpd_req_get_hdr_value_str(httpd_req_t *r, const char *field, char *val, size_t val_size)
1006 {
1007     if (r == NULL || field == NULL) {
1008         return ESP_ERR_INVALID_ARG;
1009     }
1010 
1011     if (!httpd_valid_req(r)) {
1012         return ESP_ERR_HTTPD_INVALID_REQ;
1013     }
1014 
1015     struct httpd_req_aux *ra = r->aux;
1016     const char   *hdr_ptr = ra->scratch;         /*!< Request headers are kept in scratch buffer */
1017     unsigned     count    = ra->req_hdrs_count;  /*!< Count set during parsing  */
1018     const size_t buf_len  = val_size;
1019 
1020     while (count--) {
1021         /* Search for the ':' character. Else, it would mean
1022          * that the field is invalid
1023          */
1024         const char *val_ptr = strchr(hdr_ptr, ':');
1025         if (!val_ptr) {
1026             break;
1027         }
1028 
1029         /* If the field, does not match, continue searching.
1030          * Compare lengths first as field from header is not
1031          * null terminated (has ':' in the end).
1032          */
1033         if ((val_ptr - hdr_ptr != strlen(field)) ||
1034             (strncasecmp(hdr_ptr, field, strlen(field)))) {
1035             if (count) {
1036                 /* Jump to end of header field-value string */
1037                 hdr_ptr = 1 + strchr(hdr_ptr, '\0');
1038 
1039                 /* Skip all null characters (with which the line
1040                  * terminators had been overwritten) */
1041                 while (*hdr_ptr == '\0') {
1042                     hdr_ptr++;
1043                 }
1044             }
1045             continue;
1046         }
1047 
1048         /* Skip ':' */
1049         val_ptr++;
1050 
1051         /* Skip preceding space */
1052         while ((*val_ptr != '\0') && (*val_ptr == ' ')) {
1053             val_ptr++;
1054         }
1055 
1056         /* Get the NULL terminated value and copy it to the caller's buffer. */
1057         strlcpy(val, val_ptr, buf_len);
1058 
1059         /* Update value length, including one byte for null */
1060         val_size = strlen(val_ptr) + 1;
1061 
1062         /* If buffer length is smaller than needed, return truncation error */
1063         if (buf_len < val_size) {
1064             return ESP_ERR_HTTPD_RESULT_TRUNC;
1065         }
1066         return ESP_OK;
1067     }
1068     return ESP_ERR_NOT_FOUND;
1069 }
1070 
1071 /* Helper function to get a cookie value from a cookie string of the type "cookie1=val1; cookie2=val2" */
httpd_cookie_key_value(const char * cookie_str,const char * key,char * val,size_t * val_size)1072 esp_err_t static httpd_cookie_key_value(const char *cookie_str, const char *key, char *val, size_t *val_size)
1073 {
1074     if (cookie_str == NULL || key == NULL || val == NULL) {
1075         return ESP_ERR_INVALID_ARG;
1076     }
1077 
1078     const char *cookie_ptr = cookie_str;
1079     const size_t buf_len = *val_size;
1080     size_t _val_size = *val_size;
1081 
1082     while (strlen(cookie_ptr)) {
1083         /* Search for the '=' character. Else, it would mean
1084          * that the parameter is invalid */
1085         const char *val_ptr = strchr(cookie_ptr, '=');
1086         if (!val_ptr) {
1087             break;
1088         }
1089         size_t offset = val_ptr - cookie_ptr;
1090 
1091         /* If the key, does not match, continue searching.
1092          * Compare lengths first as key from cookie string is not
1093          * null terminated (has '=' in the end) */
1094         if ((offset != strlen(key)) || (strncasecmp(cookie_ptr, key, offset) != 0)) {
1095             /* Get the name=val string. Multiple name=value pairs
1096              * are separated by '; ' */
1097             cookie_ptr = strchr(val_ptr, ' ');
1098             if (!cookie_ptr) {
1099                 break;
1100             }
1101             cookie_ptr++;
1102             continue;
1103         }
1104 
1105         /* Locate start of next query */
1106         cookie_ptr = strchr(++val_ptr, ';');
1107         /* Or this could be the last query, in which
1108          * case get to the end of query string */
1109         if (!cookie_ptr) {
1110             cookie_ptr = val_ptr + strlen(val_ptr);
1111         }
1112 
1113         /* Update value length, including one byte for null */
1114         _val_size = cookie_ptr - val_ptr + 1;
1115 
1116         /* Copy value to the caller's buffer. */
1117         strlcpy(val, val_ptr, MIN(_val_size, buf_len));
1118 
1119         /* If buffer length is smaller than needed, return truncation error */
1120         if (buf_len < _val_size) {
1121             *val_size = _val_size;
1122             return ESP_ERR_HTTPD_RESULT_TRUNC;
1123         }
1124         /* Save amount of bytes copied to caller's buffer */
1125         *val_size = MIN(_val_size, buf_len);
1126         return ESP_OK;
1127     }
1128     ESP_LOGD(TAG, LOG_FMT("cookie %s not found"), key);
1129     return ESP_ERR_NOT_FOUND;
1130 }
1131 
1132 /* Get the value of a cookie from the request headers */
httpd_req_get_cookie_val(httpd_req_t * req,const char * cookie_name,char * val,size_t * val_size)1133 esp_err_t httpd_req_get_cookie_val(httpd_req_t *req, const char *cookie_name, char *val, size_t *val_size)
1134 {
1135     esp_err_t ret;
1136     size_t hdr_len_cookie = httpd_req_get_hdr_value_len(req, "Cookie");
1137     char *cookie_str = NULL;
1138 
1139     if (hdr_len_cookie <= 0) {
1140         return ESP_ERR_NOT_FOUND;
1141     }
1142     cookie_str = malloc(hdr_len_cookie + 1);
1143     if (cookie_str == NULL) {
1144         ESP_LOGE(TAG, "Failed to allocate memory for cookie string");
1145         return ESP_ERR_NO_MEM;
1146     }
1147 
1148     if (httpd_req_get_hdr_value_str(req, "Cookie", cookie_str, hdr_len_cookie + 1) != ESP_OK) {
1149         ESP_LOGW(TAG, "Cookie not found in header uri:[%s]", req->uri);
1150         free(cookie_str);
1151         return ESP_ERR_NOT_FOUND;
1152     }
1153 
1154     ret = httpd_cookie_key_value(cookie_str, cookie_name, val, val_size);
1155     free(cookie_str);
1156     return ret;
1157 
1158 }
1159