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