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¶m2=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