| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985 |
- // Copyright 2018 Espressif Systems (Shanghai) PTE LTD
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- #include <stdlib.h>
- #include <sys/param.h>
- #include <esp_log.h>
- #include <esp_err.h>
- #include <http_parser.h>
- #include <esp_http_server.h>
- #include "esp_httpd_priv.h"
- #include "osal.h"
- static const char *TAG = "httpd_parse";
- typedef struct {
- /* Parser settings for http_parser_execute() */
- http_parser_settings settings;
- /* Request being parsed */
- struct httpd_req *req;
- /* Status of the parser describes the part of the
- * HTTP request packet being processed at any moment.
- */
- enum {
- PARSING_IDLE = 0,
- PARSING_URL,
- PARSING_HDR_FIELD,
- PARSING_HDR_VALUE,
- PARSING_BODY,
- PARSING_COMPLETE,
- PARSING_FAILED
- } status;
- /* Response error code in case of PARSING_FAILED */
- httpd_err_code_t error;
- /* For storing last callback parameters */
- struct {
- const char *at;
- size_t length;
- } last;
- /* State variables */
- bool paused; /*!< Parser is paused */
- size_t pre_parsed; /*!< Length of data to be skipped while parsing */
- size_t raw_datalen; /*!< Full length of the raw data in scratch buffer */
- } parser_data_t;
- static esp_err_t verify_url (http_parser *parser)
- {
- parser_data_t *parser_data = (parser_data_t *) parser->data;
- struct httpd_req *r = parser_data->req;
- struct httpd_req_aux *ra = r->aux;
- struct http_parser_url *res = &ra->url_parse_res;
- /* Get previous values of the parser callback arguments */
- const char *at = parser_data->last.at;
- size_t length = parser_data->last.length;
- if ((r->method = parser->method) < 0) {
- ESP_LOGW(TAG, LOG_FMT("HTTP Operation not supported"));
- parser_data->error = HTTPD_501_METHOD_NOT_IMPLEMENTED;
- return ESP_FAIL;
- }
- if (sizeof(r->uri) < (length + 1)) {
- ESP_LOGW(TAG, LOG_FMT("URI length (%d) greater than supported (%d)"),
- length, sizeof(r->uri));
- parser_data->error = HTTPD_414_URI_TOO_LONG;
- return ESP_FAIL;
- }
- /* Keep URI with terminating null character. Note URI string pointed
- * by 'at' is not NULL terminated, therefore use length provided by
- * parser while copying the URI to buffer */
- strlcpy((char *)r->uri, at, (length + 1));
- ESP_LOGD(TAG, LOG_FMT("received URI = %s"), r->uri);
- /* Make sure version is HTTP/1.1 */
- if ((parser->http_major != 1) && (parser->http_minor != 1)) {
- ESP_LOGW(TAG, LOG_FMT("unsupported HTTP version = %d.%d"),
- parser->http_major, parser->http_minor);
- parser_data->error = HTTPD_505_VERSION_NOT_SUPPORTED;
- return ESP_FAIL;
- }
- /* Parse URL and keep result for later */
- http_parser_url_init(res);
- if (http_parser_parse_url(r->uri, strlen(r->uri),
- r->method == HTTP_CONNECT, res)) {
- ESP_LOGW(TAG, LOG_FMT("http_parser_parse_url failed with errno = %d"),
- parser->http_errno);
- parser_data->error = HTTPD_400_BAD_REQUEST;
- return ESP_FAIL;
- }
- return ESP_OK;
- }
- /* http_parser callback on finding url in HTTP request
- * Will be invoked ATLEAST once every packet
- */
- static esp_err_t cb_url(http_parser *parser,
- const char *at, size_t length)
- {
- parser_data_t *parser_data = (parser_data_t *) parser->data;
- if (parser_data->status == PARSING_IDLE) {
- ESP_LOGD(TAG, LOG_FMT("message begin"));
- /* Store current values of the parser callback arguments */
- parser_data->last.at = at;
- parser_data->last.length = 0;
- parser_data->status = PARSING_URL;
- } else if (parser_data->status != PARSING_URL) {
- ESP_LOGE(TAG, LOG_FMT("unexpected state transition"));
- parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
- parser_data->status = PARSING_FAILED;
- return ESP_FAIL;
- }
- ESP_LOGD(TAG, LOG_FMT("processing url = %.*s"), length, at);
- /* Update length of URL string */
- if ((parser_data->last.length += length) > HTTPD_MAX_URI_LEN) {
- ESP_LOGW(TAG, LOG_FMT("URI length (%d) greater than supported (%d)"),
- parser_data->last.length, HTTPD_MAX_URI_LEN);
- parser_data->error = HTTPD_414_URI_TOO_LONG;
- parser_data->status = PARSING_FAILED;
- return ESP_FAIL;
- }
- return ESP_OK;
- }
- static esp_err_t pause_parsing(http_parser *parser, const char* at)
- {
- parser_data_t *parser_data = (parser_data_t *) parser->data;
- struct httpd_req *r = parser_data->req;
- struct httpd_req_aux *ra = r->aux;
- /* The length of data that was not parsed due to interruption
- * and hence needs to be read again later for parsing */
- ssize_t unparsed = parser_data->raw_datalen - (at - ra->scratch);
- if (unparsed < 0) {
- ESP_LOGE(TAG, LOG_FMT("parsing beyond valid data = %d"), -unparsed);
- return ESP_ERR_INVALID_STATE;
- }
- /* Push back the un-parsed data into pending buffer for
- * receiving again with httpd_recv_with_opt() later when
- * read_block() executes */
- if (unparsed && (unparsed != httpd_unrecv(r, at, unparsed))) {
- ESP_LOGE(TAG, LOG_FMT("data too large for un-recv = %d"), unparsed);
- return ESP_FAIL;
- }
- /* Signal http_parser to pause execution and save the maximum
- * possible length, of the yet un-parsed data, that may get
- * parsed before http_parser_execute() returns. This pre_parsed
- * length will be updated then to reflect the actual length
- * that got parsed, and must be skipped when parsing resumes */
- parser_data->pre_parsed = unparsed;
- http_parser_pause(parser, 1);
- parser_data->paused = true;
- ESP_LOGD(TAG, LOG_FMT("paused"));
- return ESP_OK;
- }
- static size_t continue_parsing(http_parser *parser, size_t length)
- {
- parser_data_t *data = (parser_data_t *) parser->data;
- /* Part of the received data may have been parsed earlier
- * so we must skip that before parsing resumes */
- length = MIN(length, data->pre_parsed);
- data->pre_parsed -= length;
- ESP_LOGD(TAG, LOG_FMT("skip pre-parsed data of size = %d"), length);
- http_parser_pause(parser, 0);
- data->paused = false;
- ESP_LOGD(TAG, LOG_FMT("un-paused"));
- return length;
- }
- /* http_parser callback on header field in HTTP request
- * May be invoked ATLEAST once every header field
- */
- static esp_err_t cb_header_field(http_parser *parser, const char *at, size_t length)
- {
- parser_data_t *parser_data = (parser_data_t *) parser->data;
- struct httpd_req *r = parser_data->req;
- struct httpd_req_aux *ra = r->aux;
- /* Check previous status */
- if (parser_data->status == PARSING_URL) {
- if (verify_url(parser) != ESP_OK) {
- /* verify_url would already have set the
- * error field of parser data, so only setting
- * status to failed */
- parser_data->status = PARSING_FAILED;
- return ESP_FAIL;
- }
- ESP_LOGD(TAG, LOG_FMT("headers begin"));
- /* Last at is set to start of scratch where headers
- * will be received next */
- parser_data->last.at = ra->scratch;
- parser_data->last.length = 0;
- parser_data->status = PARSING_HDR_FIELD;
- /* Stop parsing for now and give control to process */
- if (pause_parsing(parser, at) != ESP_OK) {
- parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
- parser_data->status = PARSING_FAILED;
- return ESP_FAIL;
- }
- } else if (parser_data->status == PARSING_HDR_VALUE) {
- /* Overwrite terminator (CRLFs) following last header
- * (key: value) pair with null characters */
- char *term_start = (char *)parser_data->last.at + parser_data->last.length;
- memset(term_start, '\0', at - term_start);
- /* Store current values of the parser callback arguments */
- parser_data->last.at = at;
- parser_data->last.length = 0;
- parser_data->status = PARSING_HDR_FIELD;
- /* Increment header count */
- ra->req_hdrs_count++;
- } else if (parser_data->status != PARSING_HDR_FIELD) {
- ESP_LOGE(TAG, LOG_FMT("unexpected state transition"));
- parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
- parser_data->status = PARSING_FAILED;
- return ESP_FAIL;
- }
- ESP_LOGD(TAG, LOG_FMT("processing field = %.*s"), length, at);
- /* Update length of header string */
- parser_data->last.length += length;
- return ESP_OK;
- }
- /* http_parser callback on header value in HTTP request.
- * May be invoked ATLEAST once every header value
- */
- static esp_err_t cb_header_value(http_parser *parser, const char *at, size_t length)
- {
- parser_data_t *parser_data = (parser_data_t *) parser->data;
- /* Check previous status */
- if (parser_data->status == PARSING_HDR_FIELD) {
- /* Store current values of the parser callback arguments */
- parser_data->last.at = at;
- parser_data->last.length = 0;
- parser_data->status = PARSING_HDR_VALUE;
- } else if (parser_data->status != PARSING_HDR_VALUE) {
- ESP_LOGE(TAG, LOG_FMT("unexpected state transition"));
- parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
- parser_data->status = PARSING_FAILED;
- return ESP_FAIL;
- }
- ESP_LOGD(TAG, LOG_FMT("processing value = %.*s"), length, at);
- /* Update length of header string */
- parser_data->last.length += length;
- return ESP_OK;
- }
- /* http_parser callback on completing headers in HTTP request.
- * Will be invoked ONLY once every packet
- */
- static esp_err_t cb_headers_complete(http_parser *parser)
- {
- parser_data_t *parser_data = (parser_data_t *) parser->data;
- struct httpd_req *r = parser_data->req;
- struct httpd_req_aux *ra = r->aux;
- /* Check previous status */
- if (parser_data->status == PARSING_URL) {
- ESP_LOGD(TAG, LOG_FMT("no headers"));
- if (verify_url(parser) != ESP_OK) {
- /* verify_url would already have set the
- * error field of parser data, so only setting
- * status to failed */
- parser_data->status = PARSING_FAILED;
- return ESP_FAIL;
- }
- } else if (parser_data->status == PARSING_HDR_VALUE) {
- /* Locate end of last header */
- char *at = (char *)parser_data->last.at + parser_data->last.length;
- /* Check if there is data left to parse. This value should
- * at least be equal to the number of line terminators, i.e. 2 */
- ssize_t remaining_length = parser_data->raw_datalen - (at - ra->scratch);
- if (remaining_length < 2) {
- ESP_LOGE(TAG, LOG_FMT("invalid length of data remaining to be parsed"));
- parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
- parser_data->status = PARSING_FAILED;
- return ESP_FAIL;
- }
- /* Locate end of headers section by skipping the remaining
- * two line terminators. No assumption is made here about the
- * termination sequence used apart from the necessity that it
- * must end with an LF, because:
- * 1) some clients may send non standard LFs instead of
- * CRLFs for indicating termination.
- * 2) it is the responsibility of http_parser to check
- * that the termination is either CRLF or LF and
- * not any other sequence */
- unsigned short remaining_terminators = 2;
- while (remaining_length-- && remaining_terminators) {
- if (*at == '\n') {
- remaining_terminators--;
- }
- /* Overwrite termination characters with null */
- *(at++) = '\0';
- }
- if (remaining_terminators) {
- ESP_LOGE(TAG, LOG_FMT("incomplete termination of headers"));
- parser_data->error = HTTPD_400_BAD_REQUEST;
- parser_data->status = PARSING_FAILED;
- return ESP_FAIL;
- }
- /* Place the parser ptr right after the end of headers section */
- parser_data->last.at = at;
- /* Increment header count */
- ra->req_hdrs_count++;
- } else {
- ESP_LOGE(TAG, LOG_FMT("unexpected state transition"));
- parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
- parser_data->status = PARSING_FAILED;
- return ESP_FAIL;
- }
- /* In absence of body/chunked encoding, http_parser sets content_len to -1 */
- r->content_len = ((int)parser->content_length != -1 ?
- parser->content_length : 0);
- ESP_LOGD(TAG, LOG_FMT("bytes read = %d"), parser->nread);
- ESP_LOGD(TAG, LOG_FMT("content length = %zu"), r->content_len);
- if (parser->upgrade) {
- ESP_LOGW(TAG, LOG_FMT("upgrade from HTTP not supported"));
- /* There is no specific HTTP error code to notify the client that
- * upgrade is not supported, thus sending 400 Bad Request */
- parser_data->error = HTTPD_400_BAD_REQUEST;
- parser_data->status = PARSING_FAILED;
- return ESP_FAIL;
- }
- parser_data->status = PARSING_BODY;
- ra->remaining_len = r->content_len;
- return ESP_OK;
- }
- /* Last http_parser callback if body present in HTTP request.
- * Will be invoked ONLY once every packet
- */
- static esp_err_t cb_on_body(http_parser *parser, const char *at, size_t length)
- {
- parser_data_t *parser_data = (parser_data_t *) parser->data;
- /* Check previous status */
- if (parser_data->status != PARSING_BODY) {
- ESP_LOGE(TAG, LOG_FMT("unexpected state transition"));
- parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
- parser_data->status = PARSING_FAILED;
- return ESP_FAIL;
- }
- /* Pause parsing so that if part of another packet
- * is in queue then it doesn't get parsed, which
- * may reset the parser state and cause current
- * request packet to be lost */
- if (pause_parsing(parser, at) != ESP_OK) {
- parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
- parser_data->status = PARSING_FAILED;
- return ESP_FAIL;
- }
- parser_data->last.at = 0;
- parser_data->last.length = 0;
- parser_data->status = PARSING_COMPLETE;
- ESP_LOGD(TAG, LOG_FMT("body begins"));
- return ESP_OK;
- }
- /* Last http_parser callback if body absent in HTTP request.
- * Will be invoked ONLY once every packet
- */
- static esp_err_t cb_no_body(http_parser *parser)
- {
- parser_data_t *parser_data = (parser_data_t *) parser->data;
- /* Check previous status */
- if (parser_data->status == PARSING_URL) {
- ESP_LOGD(TAG, LOG_FMT("no headers"));
- if (verify_url(parser) != ESP_OK) {
- /* verify_url would already have set the
- * error field of parser data, so only setting
- * status to failed */
- parser_data->status = PARSING_FAILED;
- return ESP_FAIL;
- }
- } else if (parser_data->status != PARSING_BODY) {
- ESP_LOGE(TAG, LOG_FMT("unexpected state transition"));
- parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
- parser_data->status = PARSING_FAILED;
- return ESP_FAIL;
- }
- /* Pause parsing so that if part of another packet
- * is in queue then it doesn't get parsed, which
- * may reset the parser state and cause current
- * request packet to be lost */
- if (pause_parsing(parser, parser_data->last.at) != ESP_OK) {
- parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
- parser_data->status = PARSING_FAILED;
- return ESP_FAIL;
- }
- parser_data->last.at = 0;
- parser_data->last.length = 0;
- parser_data->status = PARSING_COMPLETE;
- ESP_LOGD(TAG, LOG_FMT("message complete"));
- return ESP_OK;
- }
- static int read_block(httpd_req_t *req, size_t offset, size_t length)
- {
- struct httpd_req_aux *raux = req->aux;
- /* Limits the read to scratch buffer size */
- ssize_t buf_len = MIN(length, (sizeof(raux->scratch) - offset));
- if (buf_len <= 0) {
- return 0;
- }
- /* Receive data into buffer. If data is pending (from unrecv) then return
- * immediately after receiving pending data, as pending data may just complete
- * this request packet. */
- int nbytes = httpd_recv_with_opt(req, raux->scratch + offset, buf_len, true);
- if (nbytes < 0) {
- ESP_LOGD(TAG, LOG_FMT("error in httpd_recv"));
- /* If timeout occurred allow the
- * situation to be handled */
- if (nbytes == HTTPD_SOCK_ERR_TIMEOUT) {
- /* Invoke error handler which may return ESP_OK
- * to signal for retrying call to recv(), else it may
- * return ESP_FAIL to signal for closure of socket */
- return (httpd_req_handle_err(req, HTTPD_408_REQ_TIMEOUT) == ESP_OK) ?
- HTTPD_SOCK_ERR_TIMEOUT : HTTPD_SOCK_ERR_FAIL;
- }
- /* Some socket error occurred. Return failure
- * to force closure of underlying socket.
- * Error message is not sent as socket may not
- * be valid anymore */
- return HTTPD_SOCK_ERR_FAIL;
- } else if (nbytes == 0) {
- ESP_LOGD(TAG, LOG_FMT("connection closed"));
- /* Connection closed by client so no
- * need to send error response */
- return HTTPD_SOCK_ERR_FAIL;
- }
- ESP_LOGD(TAG, LOG_FMT("received HTTP request block size = %d"), nbytes);
- return nbytes;
- }
- static int parse_block(http_parser *parser, size_t offset, size_t length)
- {
- parser_data_t *data = (parser_data_t *)(parser->data);
- httpd_req_t *req = data->req;
- struct httpd_req_aux *raux = req->aux;
- size_t nparsed = 0;
- if (!length) {
- /* Parsing is still happening but nothing to
- * parse means no more space left on buffer,
- * therefore it can be inferred that the
- * request URI/header must be too long */
- ESP_LOGW(TAG, LOG_FMT("request URI/header too long"));
- switch (data->status) {
- case PARSING_URL:
- data->error = HTTPD_414_URI_TOO_LONG;
- break;
- case PARSING_HDR_FIELD:
- case PARSING_HDR_VALUE:
- data->error = HTTPD_431_REQ_HDR_FIELDS_TOO_LARGE;
- break;
- default:
- ESP_LOGE(TAG, LOG_FMT("unexpected state"));
- data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
- break;
- }
- data->status = PARSING_FAILED;
- return -1;
- }
- /* Un-pause the parsing if paused */
- if (data->paused) {
- nparsed = continue_parsing(parser, length);
- length -= nparsed;
- offset += nparsed;
- if (!length) {
- return nparsed;
- }
- }
- /* Execute http_parser */
- nparsed = http_parser_execute(parser, &data->settings,
- raux->scratch + offset, length);
- /* Check state */
- if (data->status == PARSING_FAILED) {
- /* It is expected that the error field of
- * parser data should have been set by now */
- ESP_LOGW(TAG, LOG_FMT("parsing failed"));
- return -1;
- } else if (data->paused) {
- /* Update the value of pre_parsed which was set when
- * pause_parsing() was called. (length - nparsed) is
- * the length of the data that will need to be parsed
- * again later and hence must be deducted from the
- * pre_parsed length */
- data->pre_parsed -= (length - nparsed);
- return 0;
- } else if (nparsed != length) {
- /* http_parser error */
- data->error = HTTPD_400_BAD_REQUEST;
- data->status = PARSING_FAILED;
- ESP_LOGW(TAG, LOG_FMT("incomplete (%d/%d) with parser error = %d"),
- nparsed, length, parser->http_errno);
- return -1;
- }
- /* Return with the total length of the request packet
- * that has been parsed till now */
- ESP_LOGD(TAG, LOG_FMT("parsed block size = %d"), offset + nparsed);
- return offset + nparsed;
- }
- static void parse_init(httpd_req_t *r, http_parser *parser, parser_data_t *data)
- {
- /* Initialize parser data */
- memset(data, 0, sizeof(parser_data_t));
- data->req = r;
- /* Initialize parser */
- http_parser_init(parser, HTTP_REQUEST);
- parser->data = (void *)data;
- /* Initialize parser settings */
- http_parser_settings_init(&data->settings);
- /* Set parser callbacks */
- data->settings.on_url = cb_url;
- data->settings.on_header_field = cb_header_field;
- data->settings.on_header_value = cb_header_value;
- data->settings.on_headers_complete = cb_headers_complete;
- data->settings.on_body = cb_on_body;
- data->settings.on_message_complete = cb_no_body;
- }
- /* Function that receives TCP data and runs parser on it
- */
- static esp_err_t httpd_parse_req(struct httpd_data *hd)
- {
- httpd_req_t *r = &hd->hd_req;
- int blk_len, offset;
- http_parser parser;
- parser_data_t parser_data;
- /* Initialize parser */
- parse_init(r, &parser, &parser_data);
- /* Set offset to start of scratch buffer */
- offset = 0;
- do {
- /* Read block into scratch buffer */
- if ((blk_len = read_block(r, offset, PARSER_BLOCK_SIZE)) < 0) {
- if (blk_len == HTTPD_SOCK_ERR_TIMEOUT) {
- /* Retry read in case of non-fatal timeout error.
- * read_block() ensures that the timeout error is
- * handled properly so that this doesn't get stuck
- * in an infinite loop */
- continue;
- }
- /* If not HTTPD_SOCK_ERR_TIMEOUT, returned error must
- * be HTTPD_SOCK_ERR_FAIL which means we need to return
- * failure and thereby close the underlying socket */
- return ESP_FAIL;
- }
- /* This is used by the callbacks to track
- * data usage of the buffer */
- parser_data.raw_datalen = blk_len + offset;
- /* Parse data block from buffer */
- if ((offset = parse_block(&parser, offset, blk_len)) < 0) {
- /* HTTP error occurred.
- * Send error code as response status and
- * invoke error handler */
- return httpd_req_handle_err(r, parser_data.error);
- }
- } while (parser_data.status != PARSING_COMPLETE);
- ESP_LOGD(TAG, LOG_FMT("parsing complete"));
- return httpd_uri(hd);
- }
- static void init_req(httpd_req_t *r, httpd_config_t *config)
- {
- r->handle = 0;
- r->method = 0;
- memset((char*)r->uri, 0, sizeof(r->uri));
- r->content_len = 0;
- r->aux = 0;
- r->user_ctx = 0;
- r->sess_ctx = 0;
- r->free_ctx = 0;
- r->ignore_sess_ctx_changes = 0;
- }
- static void init_req_aux(struct httpd_req_aux *ra, httpd_config_t *config)
- {
- ra->sd = 0;
- memset(ra->scratch, 0, sizeof(ra->scratch));
- ra->remaining_len = 0;
- ra->status = 0;
- ra->content_type = 0;
- ra->first_chunk_sent = 0;
- ra->req_hdrs_count = 0;
- ra->resp_hdrs_count = 0;
- memset(ra->resp_hdrs, 0, config->max_resp_headers * sizeof(struct resp_hdr));
- }
- static void httpd_req_cleanup(httpd_req_t *r)
- {
- struct httpd_req_aux *ra = r->aux;
- /* Check if the context has changed and needs to be cleared */
- if ((r->ignore_sess_ctx_changes == false) && (ra->sd->ctx != r->sess_ctx)) {
- httpd_sess_free_ctx(ra->sd->ctx, ra->sd->free_ctx);
- }
- /* Retrieve session info from the request into the socket database. */
- ra->sd->ctx = r->sess_ctx;
- ra->sd->free_ctx = r->free_ctx;
- ra->sd->ignore_sess_ctx_changes = r->ignore_sess_ctx_changes;
- /* Clear out the request and request_aux structures */
- ra->sd = NULL;
- r->handle = NULL;
- r->aux = NULL;
- }
- /* Function that processes incoming TCP data and
- * updates the http request data httpd_req_t
- */
- esp_err_t httpd_req_new(struct httpd_data *hd, struct sock_db *sd)
- {
- httpd_req_t *r = &hd->hd_req;
- init_req(r, &hd->config);
- init_req_aux(&hd->hd_req_aux, &hd->config);
- r->handle = hd;
- r->aux = &hd->hd_req_aux;
- /* Associate the request to the socket */
- struct httpd_req_aux *ra = r->aux;
- ra->sd = sd;
- /* Set defaults */
- ra->status = (char *)HTTPD_200;
- ra->content_type = (char *)HTTPD_TYPE_TEXT;
- ra->first_chunk_sent = false;
- /* Copy session info to the request */
- r->sess_ctx = sd->ctx;
- r->free_ctx = sd->free_ctx;
- r->ignore_sess_ctx_changes = sd->ignore_sess_ctx_changes;
- /* Parse request */
- esp_err_t err = httpd_parse_req(hd);
- if (err != ESP_OK) {
- httpd_req_cleanup(r);
- }
- return err;
- }
- /* Function that resets the http request data
- */
- esp_err_t httpd_req_delete(struct httpd_data *hd)
- {
- httpd_req_t *r = &hd->hd_req;
- struct httpd_req_aux *ra = r->aux;
- /* Finish off reading any pending/leftover data */
- while (ra->remaining_len) {
- /* Any length small enough not to overload the stack, but large
- * enough to finish off the buffers fast */
- char dummy[CONFIG_HTTPD_PURGE_BUF_LEN];
- int recv_len = MIN(sizeof(dummy), ra->remaining_len);
- recv_len = httpd_req_recv(r, dummy, recv_len);
- if (recv_len < 0) {
- httpd_req_cleanup(r);
- return ESP_FAIL;
- }
- ESP_LOGD(TAG, LOG_FMT("purging data size : %d bytes"), recv_len);
- #ifdef CONFIG_HTTPD_LOG_PURGE_DATA
- /* Enabling this will log discarded binary HTTP content data at
- * Debug level. For large content data this may not be desirable
- * as it will clutter the log */
- ESP_LOGD(TAG, "================= PURGED DATA =================");
- ESP_LOG_BUFFER_HEX_LEVEL(TAG, dummy, recv_len, ESP_LOG_DEBUG);
- ESP_LOGD(TAG, "===============================================");
- #endif
- }
- httpd_req_cleanup(r);
- return ESP_OK;
- }
- /* Validates the request to prevent users from calling APIs, that are to
- * be called only inside URI handler, outside the handler context
- */
- bool httpd_validate_req_ptr(httpd_req_t *r)
- {
- if (r) {
- struct httpd_data *hd = (struct httpd_data *) r->handle;
- if (hd) {
- /* Check if this function is running in the context of
- * the correct httpd server thread */
- if (httpd_os_thread_handle() == hd->hd_td.handle) {
- return true;
- }
- }
- }
- return false;
- }
- /* Helper function to get a URL query tag from a query string of the type param1=val1¶m2=val2 */
- esp_err_t httpd_query_key_value(const char *qry_str, const char *key, char *val, size_t val_size)
- {
- if (qry_str == NULL || key == NULL || val == NULL) {
- return ESP_ERR_INVALID_ARG;
- }
- const char *qry_ptr = qry_str;
- const size_t buf_len = val_size;
- while (strlen(qry_ptr)) {
- /* Search for the '=' character. Else, it would mean
- * that the parameter is invalid */
- const char *val_ptr = strchr(qry_ptr, '=');
- if (!val_ptr) {
- break;
- }
- size_t offset = val_ptr - qry_ptr;
- /* If the key, does not match, continue searching.
- * Compare lengths first as key from url is not
- * null terminated (has '=' in the end) */
- if ((offset != strlen(key)) ||
- (strncasecmp(qry_ptr, key, offset))) {
- /* Get the name=val string. Multiple name=value pairs
- * are separated by '&' */
- qry_ptr = strchr(val_ptr, '&');
- if (!qry_ptr) {
- break;
- }
- qry_ptr++;
- continue;
- }
- /* Locate start of next query */
- qry_ptr = strchr(++val_ptr, '&');
- /* Or this could be the last query, in which
- * case get to the end of query string */
- if (!qry_ptr) {
- qry_ptr = val_ptr + strlen(val_ptr);
- }
- /* Update value length, including one byte for null */
- val_size = qry_ptr - val_ptr + 1;
- /* Copy value to the caller's buffer. */
- strlcpy(val, val_ptr, MIN(val_size, buf_len));
- /* If buffer length is smaller than needed, return truncation error */
- if (buf_len < val_size) {
- return ESP_ERR_HTTPD_RESULT_TRUNC;
- }
- return ESP_OK;
- }
- ESP_LOGD(TAG, LOG_FMT("key %s not found"), key);
- return ESP_ERR_NOT_FOUND;
- }
- size_t httpd_req_get_url_query_len(httpd_req_t *r)
- {
- if (r == NULL) {
- return 0;
- }
- if (!httpd_valid_req(r)) {
- return 0;
- }
- struct httpd_req_aux *ra = r->aux;
- struct http_parser_url *res = &ra->url_parse_res;
- /* Check if query field is present in the URL */
- if (res->field_set & (1 << UF_QUERY)) {
- return res->field_data[UF_QUERY].len;
- }
- return 0;
- }
- esp_err_t httpd_req_get_url_query_str(httpd_req_t *r, char *buf, size_t buf_len)
- {
- if (r == NULL || buf == NULL) {
- return ESP_ERR_INVALID_ARG;
- }
- if (!httpd_valid_req(r)) {
- return ESP_ERR_HTTPD_INVALID_REQ;
- }
- struct httpd_req_aux *ra = r->aux;
- struct http_parser_url *res = &ra->url_parse_res;
- /* Check if query field is present in the URL */
- if (res->field_set & (1 << UF_QUERY)) {
- const char *qry = r->uri + res->field_data[UF_QUERY].off;
- /* Minimum required buffer len for keeping
- * null terminated query string */
- size_t min_buf_len = res->field_data[UF_QUERY].len + 1;
- strlcpy(buf, qry, MIN(buf_len, min_buf_len));
- if (buf_len < min_buf_len) {
- return ESP_ERR_HTTPD_RESULT_TRUNC;
- }
- return ESP_OK;
- }
- return ESP_ERR_NOT_FOUND;
- }
- /* Get the length of the value string of a header request field */
- size_t httpd_req_get_hdr_value_len(httpd_req_t *r, const char *field)
- {
- if (r == NULL || field == NULL) {
- return 0;
- }
- if (!httpd_valid_req(r)) {
- return 0;
- }
- struct httpd_req_aux *ra = r->aux;
- const char *hdr_ptr = ra->scratch; /*!< Request headers are kept in scratch buffer */
- unsigned count = ra->req_hdrs_count; /*!< Count set during parsing */
- while (count--) {
- /* Search for the ':' character. Else, it would mean
- * that the field is invalid
- */
- const char *val_ptr = strchr(hdr_ptr, ':');
- if (!val_ptr) {
- break;
- }
- /* If the field, does not match, continue searching.
- * Compare lengths first as field from header is not
- * null terminated (has ':' in the end).
- */
- if ((val_ptr - hdr_ptr != strlen(field)) ||
- (strncasecmp(hdr_ptr, field, strlen(field)))) {
- if (count) {
- /* Jump to end of header field-value string */
- hdr_ptr = 1 + strchr(hdr_ptr, '\0');
- /* Skip all null characters (with which the line
- * terminators had been overwritten) */
- while (*hdr_ptr == '\0') {
- hdr_ptr++;
- }
- }
- continue;
- }
- /* Skip ':' */
- val_ptr++;
- /* Skip preceding space */
- while ((*val_ptr != '\0') && (*val_ptr == ' ')) {
- val_ptr++;
- }
- return strlen(val_ptr);
- }
- return 0;
- }
- /* Get the value of a field from the request headers */
- esp_err_t httpd_req_get_hdr_value_str(httpd_req_t *r, const char *field, char *val, size_t val_size)
- {
- if (r == NULL || field == NULL) {
- return ESP_ERR_INVALID_ARG;
- }
- if (!httpd_valid_req(r)) {
- return ESP_ERR_HTTPD_INVALID_REQ;
- }
- struct httpd_req_aux *ra = r->aux;
- const char *hdr_ptr = ra->scratch; /*!< Request headers are kept in scratch buffer */
- unsigned count = ra->req_hdrs_count; /*!< Count set during parsing */
- const size_t buf_len = val_size;
- while (count--) {
- /* Search for the ':' character. Else, it would mean
- * that the field is invalid
- */
- const char *val_ptr = strchr(hdr_ptr, ':');
- if (!val_ptr) {
- break;
- }
- /* If the field, does not match, continue searching.
- * Compare lengths first as field from header is not
- * null terminated (has ':' in the end).
- */
- if ((val_ptr - hdr_ptr != strlen(field)) ||
- (strncasecmp(hdr_ptr, field, strlen(field)))) {
- if (count) {
- /* Jump to end of header field-value string */
- hdr_ptr = 1 + strchr(hdr_ptr, '\0');
- /* Skip all null characters (with which the line
- * terminators had been overwritten) */
- while (*hdr_ptr == '\0') {
- hdr_ptr++;
- }
- }
- continue;
- }
- /* Skip ':' */
- val_ptr++;
- /* Skip preceding space */
- while ((*val_ptr != '\0') && (*val_ptr == ' ')) {
- val_ptr++;
- }
- /* Get the NULL terminated value and copy it to the caller's buffer. */
- strlcpy(val, val_ptr, buf_len);
- /* Update value length, including one byte for null */
- val_size = strlen(val_ptr) + 1;
- /* If buffer length is smaller than needed, return truncation error */
- if (buf_len < val_size) {
- return ESP_ERR_HTTPD_RESULT_TRUNC;
- }
- return ESP_OK;
- }
- return ESP_ERR_NOT_FOUND;
- }
|