Skip to content

Commit 9846584

Browse files
committed
feat(esp_http_server): Modified the calculation of buf_len
Modified the calculation of buf_len, so that scratch buffer allocates the memory according to requirement and not extra (except the last read chunk)
1 parent 97b6043 commit 9846584

File tree

19 files changed

+117
-91
lines changed

19 files changed

+117
-91
lines changed

components/esp_http_server/Kconfig

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,19 @@ menu "HTTP Server"
22

33

44
config HTTPD_MAX_REQ_HDR_LEN
5-
int "Max HTTP Request Header Length"
6-
default 2000
5+
int "HTTP Request Header Length limit"
6+
default 1024
7+
range 128 65536
78
help
8-
This sets the maximum supported size of headers section in HTTP request packet to be processed by the
9-
server
9+
This sets the default limit for the HTTP request header length. The limit can be
10+
configured at run time by setting max_req_hdr_len member of httpd_config_t structure.
11+
The memory allocated will depend on the actual header length. Hence keeping a sufficiently
12+
large max header length is recommended.
13+
1014

1115
config HTTPD_MAX_URI_LEN
1216
int "Max HTTP URI Length"
13-
default 2000
17+
default 512
1418
help
1519
This sets the maximum supported size of HTTP request URI to be processed by the server
1620

components/esp_http_server/include/esp_http_server.h

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@ initializer that should be kept in sync
5353
#define HTTPD_DEFAULT_CONFIG() { \
5454
.task_priority = tskIDLE_PRIORITY+5, \
5555
.stack_size = 4096, \
56-
.hdr_buf_size_limit = HTTPD_MAX_REQ_HDR_LEN, \
57-
.uri_buf_size_limit = HTTPD_MAX_URI_LEN, \
5856
.core_id = tskNO_AFFINITY, \
5957
.task_caps = (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT), \
58+
.max_req_hdr_len = CONFIG_HTTPD_MAX_REQ_HDR_LEN, \
59+
.max_uri_len = CONFIG_HTTPD_MAX_URI_LEN, \
6060
.server_port = 80, \
6161
.ctrl_port = ESP_HTTPD_DEF_CTRL_PORT, \
6262
.max_open_sockets = 7, \
@@ -176,11 +176,12 @@ typedef struct httpd_config {
176176
uint32_t task_caps; /*!< The memory capabilities to use when allocating the HTTP server task's stack */
177177

178178
/**
179-
* Size limits for the header and URI buffers respectively. These are just limits, actually allocation would be depend upon actual size of URI/header.
180-
* These are set to the values defined in the Kconfig file.
179+
* Size limits for the header and URI buffers respectively.
180+
* These are just limits, allocation would depend upon actual size of URI/header.
181181
*/
182-
uint16_t hdr_buf_size_limit; /*!< Size limit for the header buffer */
183-
uint16_t uri_buf_size_limit; /*!< Size limit for the URI buffer */
182+
size_t max_req_hdr_len; /*!< Size limit for the header buffer (By default this value is set to CONFIG_HTTPD_MAX_REQ_HDR_LEN, overwrite is possible) */
183+
size_t max_uri_len; /*!< Size limit for the URI buffer By default this value is set to CONFIG_HTTPD_MAX_URI_LEN, overwrite is possible) */
184+
184185
/**
185186
* TCP Port number for receiving and transmitting HTTP traffic
186187
*/
@@ -366,19 +367,13 @@ esp_err_t httpd_stop(httpd_handle_t handle);
366367
* @{
367368
*/
368369

369-
/* Max supported HTTP request header length */
370-
#define HTTPD_MAX_REQ_HDR_LEN CONFIG_HTTPD_MAX_REQ_HDR_LEN
371-
372-
/* Max supported HTTP request URI length */
373-
#define HTTPD_MAX_URI_LEN CONFIG_HTTPD_MAX_URI_LEN
374-
375370
/**
376371
* @brief HTTP Request Data Structure
377372
*/
378373
typedef struct httpd_req {
379374
httpd_handle_t handle; /*!< Handle to server instance */
380375
int method; /*!< The type of HTTP request, -1 if unsupported method, HTTP_ANY for wildcard method to support every method */
381-
const char uri[HTTPD_MAX_URI_LEN + 1]; /*!< The URI of this request (1 byte extra for null termination) */
376+
const char uri[CONFIG_HTTPD_MAX_URI_LEN + 1]; /*!< The URI of this request (1 byte extra for null termination) */
382377
size_t content_len; /*!< Length of the request body */
383378
void *aux; /*!< Internally used members */
384379

@@ -619,10 +614,10 @@ typedef enum {
619614
/* Incoming payload is too large */
620615
HTTPD_413_CONTENT_TOO_LARGE,
621616

622-
/* URI length greater than CONFIG_HTTPD_MAX_URI_LEN */
617+
/* URI length greater than CONFIG_CONFIG_HTTPD_MAX_URI_LEN */
623618
HTTPD_414_URI_TOO_LONG,
624619

625-
/* Headers section larger than CONFIG_HTTPD_MAX_REQ_HDR_LEN */
620+
/* Headers section larger than CONFIG_CONFIG_HTTPD_MAX_REQ_HDR_LEN */
626621
HTTPD_431_REQ_HDR_FIELDS_TOO_LARGE,
627622

628623
/* Used internally for retrieving the total count of errors */

components/esp_http_server/src/esp_httpd_priv.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@ extern "C" {
3131
#endif
3232

3333
/* Size of request data block/chunk (not to be confused with chunked encoded data)
34-
* that is received and parsed in one turn of the parsing process. This should not
35-
* exceed the scratch buffer size and should at least be 8 bytes */
34+
* that is received and parsed in one turn of the parsing process. */
3635
#define PARSER_BLOCK_SIZE 128
3736

3837
/* Formats a log string to prepend context function name */
@@ -86,10 +85,10 @@ struct sock_db {
8685
struct httpd_req_aux {
8786
struct sock_db *sd; /*!< Pointer to socket database */
8887
char *scratch; /*!< Temporary buffer for our operations (1 byte extra for null termination) */
89-
uint16_t scratch_size_limit; /*!< Scratch buffer size limit */
90-
uint16_t scratch_cur_size; /*!< Scratch buffer cur size */
91-
uint16_t hdr_buf_size_limit; /*!< Header buffer size limit */
92-
uint16_t uri_buf_size_limit; /*!< URI buffer size limit */
88+
size_t scratch_size_limit; /*!< Scratch buffer size limit (By default this value is set to CONFIG_HTTPD_MAX_REQ_HDR_LEN, overwrite is possible) */
89+
size_t scratch_cur_size; /*!< Scratch buffer cur size (By default this value is set to CONFIG_HTTPD_MAX_URI_LEN, overwrite is possible) */
90+
size_t max_req_hdr_len; /*!< Header buffer size limit */
91+
size_t max_uri_len; /*!< URI buffer size limit */
9392
size_t remaining_len; /*!< Amount of data remaining to be fetched */
9493
char *status; /*!< HTTP response's status code */
9594
char *content_type; /*!< HTTP response's content type */

components/esp_http_server/src/httpd_parse.c

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ static esp_err_t cb_url(http_parser *parser,
114114
parser_data_t *parser_data = (parser_data_t *) parser->data;
115115
httpd_req_t *req = parser_data->req;
116116
struct httpd_req_aux *raux = req->aux;
117+
117118
if (parser_data->status == PARSING_IDLE) {
118119
ESP_LOGD(TAG, LOG_FMT("message begin"));
119120

@@ -131,9 +132,9 @@ static esp_err_t cb_url(http_parser *parser,
131132
ESP_LOGD(TAG, LOG_FMT("processing url = %.*s"), (int)length, at);
132133

133134
/* Update length of URL string */
134-
if ((parser_data->last.length += length) > raux->uri_buf_size_limit) {
135+
if ((parser_data->last.length += length) > raux->max_uri_len) {
135136
ESP_LOGW(TAG, LOG_FMT("URI length (%"NEWLIB_NANO_COMPAT_FORMAT") greater than supported (%d)"),
136-
NEWLIB_NANO_COMPAT_CAST(parser_data->last.length), raux->uri_buf_size_limit);
137+
NEWLIB_NANO_COMPAT_CAST(parser_data->last.length), raux->max_uri_len);
137138
parser_data->error = HTTPD_414_URI_TOO_LONG;
138139
parser_data->status = PARSING_FAILED;
139140
return ESP_FAIL;
@@ -216,7 +217,8 @@ static esp_err_t cb_header_field(http_parser *parser, const char *at, size_t len
216217
parser_data->last.at = ra->scratch;
217218
parser_data->last.length = 0;
218219
parser_data->status = PARSING_HDR_FIELD;
219-
ra->scratch_size_limit = ra->hdr_buf_size_limit;
220+
ra->scratch_size_limit = ra->max_req_hdr_len;
221+
220222
/* Stop parsing for now and give control to process */
221223
if (pause_parsing(parser, at) != ESP_OK) {
222224
parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
@@ -233,7 +235,7 @@ static esp_err_t cb_header_field(http_parser *parser, const char *at, size_t len
233235
parser_data->last.at = at;
234236
parser_data->last.length = 0;
235237
parser_data->status = PARSING_HDR_FIELD;
236-
ra->scratch_size_limit = ra->hdr_buf_size_limit;
238+
ra->scratch_size_limit = ra->max_req_hdr_len;
237239

238240
/* Increment header count */
239241
ra->req_hdrs_count++;
@@ -415,6 +417,7 @@ static esp_err_t cb_headers_complete(http_parser *parser)
415417
static esp_err_t cb_on_body(http_parser *parser, const char *at, size_t length)
416418
{
417419
parser_data_t *parser_data = (parser_data_t *) parser->data;
420+
418421
/* Check previous status */
419422
if (parser_data->status != PARSING_BODY) {
420423
ESP_LOGE(TAG, LOG_FMT("unexpected state transition"));
@@ -446,6 +449,7 @@ static esp_err_t cb_on_body(http_parser *parser, const char *at, size_t length)
446449
static esp_err_t cb_no_body(http_parser *parser)
447450
{
448451
parser_data_t *parser_data = (parser_data_t *) parser->data;
452+
449453
/* Check previous status */
450454
if (parser_data->status == PARSING_URL) {
451455
ESP_LOGD(TAG, LOG_FMT("no headers"));
@@ -480,26 +484,32 @@ static esp_err_t cb_no_body(http_parser *parser)
480484
return ESP_OK;
481485
}
482486

483-
static int read_block(httpd_req_t *req, size_t offset, size_t length)
487+
static int read_block(httpd_req_t *req, http_parser *parser, size_t offset, size_t length)
484488
{
485489
struct httpd_req_aux *raux = req->aux;
490+
parser_data_t *parser_data = (parser_data_t *) parser->data;
486491

487492
/* Limits the read to scratch buffer size */
488493
ssize_t buf_len = MIN(length, (raux->scratch_size_limit - offset));
489494
if (buf_len <= 0) {
490495
return 0;
491496
}
492-
if (raux->scratch == NULL && buf_len < raux->scratch_size_limit) {
493-
raux->scratch = (char*) malloc(buf_len);
494-
}
495-
else if (raux->scratch != NULL && buf_len < raux->scratch_size_limit) {
496-
raux->scratch = (char*) realloc(raux->scratch, raux->scratch_cur_size + buf_len);
497-
}
497+
/* Calculate the offset of the current position from the start of the buffer,
498+
* as after reallocating the buffer, the base address of the buffer may change.
499+
*/
500+
size_t at_offset = parser_data->last.at - raux->scratch;
501+
/* Allocate the buffer according to offset and buf_len. Offset is
502+
from where the reading will start and buf_len is till what length
503+
the buffer will be read.
504+
*/
505+
raux->scratch = (char*) realloc(raux->scratch, offset + buf_len);
498506
if (raux->scratch == NULL) {
507+
ESP_LOGE(TAG, "Unable to allocate the scratch buffer");
499508
return 0;
500509
}
501-
raux->scratch_cur_size += buf_len;
502-
ESP_LOGD(TAG, "scratch size = %d", raux->scratch_cur_size);
510+
parser_data->last.at = raux->scratch + at_offset;
511+
raux->scratch_cur_size = offset + buf_len;
512+
ESP_LOGD(TAG, "scratch buf qsize = %d", raux->scratch_cur_size);
503513
/* Receive data into buffer. If data is pending (from unrecv) then return
504514
* immediately after receiving pending data, as pending data may just complete
505515
* this request packet. */
@@ -537,7 +547,7 @@ static int parse_block(http_parser *parser, size_t offset, size_t length)
537547
httpd_req_t *req = data->req;
538548
struct httpd_req_aux *raux = req->aux;
539549
size_t nparsed = 0;
540-
data->last.at = raux->scratch;
550+
541551
if (!length) {
542552
/* Parsing is still happening but nothing to
543553
* parse means no more space left on buffer,
@@ -643,7 +653,7 @@ static esp_err_t httpd_parse_req(struct httpd_data *hd)
643653
offset = 0;
644654
do {
645655
/* Read block into scratch buffer */
646-
if ((blk_len = read_block(r, offset, PARSER_BLOCK_SIZE)) < 0) {
656+
if ((blk_len = read_block(r, &parser, offset, PARSER_BLOCK_SIZE)) < 0) {
647657
if (blk_len == HTTPD_SOCK_ERR_TIMEOUT) {
648658
/* Retry read in case of non-fatal timeout error.
649659
* read_block() ensures that the timeout error is
@@ -660,6 +670,7 @@ static esp_err_t httpd_parse_req(struct httpd_data *hd)
660670
/* This is used by the callbacks to track
661671
* data usage of the buffer */
662672
parser_data.raw_datalen = blk_len + offset;
673+
663674
/* Parse data block from buffer */
664675
if ((offset = parse_block(&parser, offset, blk_len)) < 0) {
665676
/* HTTP error occurred.
@@ -697,9 +708,9 @@ static void init_req_aux(struct httpd_req_aux *ra, httpd_config_t *config)
697708
ra->resp_hdrs_count = 0;
698709
ra->scratch = NULL;
699710
ra->scratch_cur_size = 0;
700-
ra->hdr_buf_size_limit = config->hdr_buf_size_limit;
701-
ra->uri_buf_size_limit = config->uri_buf_size_limit;
702-
ra->scratch_size_limit = ra->uri_buf_size_limit;
711+
ra->max_req_hdr_len = (config->max_req_hdr_len > 0) ? config->max_req_hdr_len : CONFIG_HTTPD_MAX_REQ_HDR_LEN;
712+
ra->max_uri_len = (config->max_uri_len > 0) ? config->max_uri_len : CONFIG_HTTPD_MAX_URI_LEN;
713+
ra->scratch_size_limit = ra->max_uri_len;
703714
#if CONFIG_HTTPD_WS_SUPPORT
704715
ra->ws_handshake_detect = false;
705716
#endif
@@ -732,8 +743,8 @@ static void httpd_req_cleanup(httpd_req_t *r)
732743
ra->sd = NULL;
733744
free(ra->scratch);
734745
ra->scratch = NULL;
735-
ra->scratch_cur_size = 0;
736746
ra->scratch_size_limit = 0;
747+
ra->scratch_cur_size = 0;
737748
r->handle = NULL;
738749
r->aux = NULL;
739750
r->user_ctx = NULL;

components/esp_http_server/src/httpd_txrx.c

Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ int httpd_recv_with_opt(httpd_req_t *r, char *buf, size_t buf_len, bool halt_aft
132132
}
133133
return ret;
134134
}
135+
135136
ESP_LOGD(TAG, LOG_FMT("received length = %"NEWLIB_NANO_COMPAT_FORMAT), NEWLIB_NANO_COMPAT_CAST((ret + pending_len)));
136137
return ret + pending_len;
137138
}
@@ -242,29 +243,30 @@ esp_err_t httpd_resp_send(httpd_req_t *r, const char *buf, ssize_t buf_len)
242243
if (buf_len == HTTPD_RESP_USE_STRLEN) {
243244
buf_len = strlen(buf);
244245
}
246+
247+
/* Request headers are no longer available */
248+
ra->req_hdrs_count = 0;
249+
250+
/* Calculate the size of the headers. +1 for the null terminator */
245251
size_t required_size = snprintf(NULL, 0, httpd_hdr_str, ra->status, ra->content_type, buf_len) + 1;
246-
if (required_size >= HTTPD_MAX_REQ_HDR_LEN) {
252+
if (required_size > ra->max_req_hdr_len) {
247253
return ESP_ERR_HTTPD_RESP_HDR;
248254
}
249-
if (ra->scratch == NULL)
250-
ra->scratch = malloc(required_size);
251-
else {
252-
ra->scratch = realloc(ra->scratch, required_size);
253-
}
254-
if (ra->scratch == NULL) {
255-
ESP_LOGE(TAG, "Unable to allocate scratch buffer");
255+
char *res_buf = malloc(required_size); /* Temporary buffer to store the headers */
256+
if (res_buf == NULL) {
257+
ESP_LOGE(TAG, "Unable to allocate httpd send buffer");
256258
return ESP_ERR_HTTPD_ALLOC_MEM;
257259
}
258-
ra->scratch_cur_size = required_size;
259-
ESP_LOGD(TAG, "scratch size = %d", ra->scratch_cur_size);
260-
/* Request headers are no longer available */
261-
ra->req_hdrs_count = 0;
260+
ESP_LOGD(TAG, "httpd send buffer size = %d", strlen(res_buf));
262261

263-
/* Size of essential headers is limited by scratch buffer size */
264-
snprintf(ra->scratch, ra->scratch_cur_size, httpd_hdr_str, ra->status, ra->content_type, buf_len);
265-
ESP_LOG_BUFFER_HEXDUMP(TAG, ra->scratch, strlen(ra->scratch), ESP_LOG_INFO);
266-
/* Sending essential headers */
267-
if (httpd_send_all(r, ra->scratch, strlen(ra->scratch)) != ESP_OK) {
262+
esp_err_t ret = snprintf(res_buf, required_size, httpd_hdr_str, ra->status, ra->content_type, buf_len);
263+
if (ret < 0 || ret >= required_size) {
264+
free(res_buf);
265+
return ESP_ERR_HTTPD_RESP_HDR;
266+
}
267+
ret = httpd_send_all(r, res_buf, strlen(res_buf));
268+
free(res_buf);
269+
if (ret != ESP_OK) {
268270
return ESP_ERR_HTTPD_RESP_SEND;
269271
}
270272

@@ -329,27 +331,28 @@ esp_err_t httpd_resp_send_chunk(httpd_req_t *r, const char *buf, ssize_t buf_len
329331

330332
/* Request headers are no longer available */
331333
ra->req_hdrs_count = 0;
334+
335+
/* Calculate the size of the headers. +1 for the null terminator */
332336
size_t required_size = snprintf(NULL, 0, httpd_chunked_hdr_str, ra->status, ra->content_type) + 1;
333-
if (required_size >= HTTPD_MAX_REQ_HDR_LEN) {
337+
if (required_size > ra->max_req_hdr_len) {
334338
return ESP_ERR_HTTPD_RESP_HDR;
335339
}
336-
if (ra->scratch == NULL)
337-
ra->scratch = malloc(required_size);
338-
else {
339-
ra->scratch = realloc(ra->scratch, required_size);
340-
}
341-
if (ra->scratch == NULL) {
342-
ESP_LOGE(TAG, "Unable to allocate scratch buffer");
340+
char *res_buf = malloc(required_size); /* Temporary buffer to store the headers */
341+
if (res_buf == NULL) {
342+
ESP_LOGE(TAG, "Unable to allocate httpd send chunk buffer");
343343
return ESP_ERR_HTTPD_ALLOC_MEM;
344344
}
345-
ra->scratch_cur_size = required_size;
346-
ESP_LOGD(TAG, "scratch size = %d", ra->scratch_cur_size);
345+
ESP_LOGD(TAG, "httpd send chunk buffer size = %d", strlen(res_buf));
347346
if (!ra->first_chunk_sent) {
347+
esp_err_t ret = snprintf(res_buf, required_size, httpd_chunked_hdr_str, ra->status, ra->content_type);
348+
if (ret < 0 || ret >= required_size) {
349+
free(res_buf);
350+
return ESP_ERR_HTTPD_RESP_HDR;
351+
}
348352
/* Size of essential headers is limited by scratch buffer size */
349-
snprintf(ra->scratch, ra->scratch_cur_size, httpd_chunked_hdr_str, ra->status, ra->content_type);
350-
351-
/* Sending essential headers */
352-
if (httpd_send_all(r, ra->scratch, strlen(ra->scratch)) != ESP_OK) {
353+
ret = httpd_send_all(r, res_buf, strlen(res_buf));
354+
free(res_buf);
355+
if (ret != ESP_OK) {
353356
return ESP_ERR_HTTPD_RESP_SEND;
354357
}
355358

@@ -671,7 +674,10 @@ esp_err_t httpd_req_async_handler_complete(httpd_req_t *r)
671674

672675
struct httpd_req_aux *ra = r->aux;
673676
ra->sd->for_async_req = false;
674-
677+
free(ra->scratch);
678+
ra->scratch = NULL;
679+
ra->scratch_cur_size = 0;
680+
ra->scratch_size_limit = 0;
675681
free(ra->resp_hdrs);
676682
free(r->aux);
677683
free(r);

components/esp_https_server/include/esp_https_server.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ typedef struct httpd_ssl_config httpd_ssl_config_t;
150150
.stack_size = 10240, \
151151
.core_id = tskNO_AFFINITY, \
152152
.task_caps = (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT), \
153+
.max_req_hdr_len = CONFIG_HTTPD_MAX_REQ_HDR_LEN, \
154+
.max_uri_len = CONFIG_HTTPD_MAX_URI_LEN, \
153155
.server_port = 0, \
154156
.ctrl_port = ESP_HTTPD_DEF_CTRL_PORT+1, \
155157
.max_open_sockets = 4, \

components/esp_https_server/src/https_server.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ static esp_err_t create_secure_context(const struct httpd_ssl_config *config, ht
377377
}
378378

379379
/** Start the server */
380-
esp_err_t httpd_ssl_start(httpd_handle_t *pHandle, struct httpd_ssl_config *config)
380+
esp_err_t httpd_ssl_start(httpd_handle_t *pHandle, struct httpd_ssl_config *config)
381381
{
382382
assert(config != NULL);
383383
assert(pHandle != NULL);

docs/en/migration-guides/release-5.x/5.5/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ Migration from 5.4 to 5.5
88

99
system
1010
peripherals
11+
protocols

0 commit comments

Comments
 (0)