Skip to content

Commit 97b6043

Browse files
committed
feat(esp_http_server): Dynamically allocate http server's scratch buffer
In this commit, esp_http_server's http_parser scratch is made dynamic. User is asked to give limit size for header and URI, according to which scratch buufer allocates memory upto limits
1 parent 88e3ea2 commit 97b6043

File tree

7 files changed

+83
-35
lines changed

7 files changed

+83
-35
lines changed

components/esp_http_server/Kconfig

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
menu "HTTP Server"
22

3+
34
config HTTPD_MAX_REQ_HDR_LEN
45
int "Max HTTP Request Header Length"
5-
default 512
6+
default 2000
67
help
78
This sets the maximum supported size of headers section in HTTP request packet to be processed by the
89
server
910

1011
config HTTPD_MAX_URI_LEN
1112
int "Max HTTP URI Length"
12-
default 512
13+
default 2000
1314
help
1415
This sets the maximum supported size of HTTP request URI to be processed by the server
1516

components/esp_http_server/include/esp_http_server.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2018-2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2018-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -53,6 +53,8 @@ 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, \
5658
.core_id = tskNO_AFFINITY, \
5759
.task_caps = (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT), \
5860
.server_port = 80, \
@@ -173,6 +175,12 @@ typedef struct httpd_config {
173175
BaseType_t core_id; /*!< The core the HTTP server task will run on */
174176
uint32_t task_caps; /*!< The memory capabilities to use when allocating the HTTP server task's stack */
175177

178+
/**
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.
181+
*/
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 */
176184
/**
177185
* TCP Port number for receiving and transmitting HTTP traffic
178186
*/

components/esp_http_server/src/esp_httpd_priv.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2018-2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2018-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -35,9 +35,6 @@ extern "C" {
3535
* exceed the scratch buffer size and should at least be 8 bytes */
3636
#define PARSER_BLOCK_SIZE 128
3737

38-
/* Calculate the maximum size needed for the scratch buffer */
39-
#define HTTPD_SCRATCH_BUF MAX(HTTPD_MAX_REQ_HDR_LEN, HTTPD_MAX_URI_LEN)
40-
4138
/* Formats a log string to prepend context function name */
4239
#define LOG_FMT(x) "%s: " x, __func__
4340

@@ -88,7 +85,11 @@ struct sock_db {
8885
*/
8986
struct httpd_req_aux {
9087
struct sock_db *sd; /*!< Pointer to socket database */
91-
char scratch[HTTPD_SCRATCH_BUF + 1]; /*!< Temporary buffer for our operations (1 byte extra for null termination) */
88+
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 */
9293
size_t remaining_len; /*!< Amount of data remaining to be fetched */
9394
char *status; /*!< HTTP response's status code */
9495
char *content_type; /*!< HTTP response's content type */

components/esp_http_server/src/httpd_parse.c

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ static esp_err_t cb_url(http_parser *parser,
112112
const char *at, size_t length)
113113
{
114114
parser_data_t *parser_data = (parser_data_t *) parser->data;
115-
115+
httpd_req_t *req = parser_data->req;
116+
struct httpd_req_aux *raux = req->aux;
116117
if (parser_data->status == PARSING_IDLE) {
117118
ESP_LOGD(TAG, LOG_FMT("message begin"));
118119

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

132133
/* Update length of URL string */
133-
if ((parser_data->last.length += length) > HTTPD_MAX_URI_LEN) {
134+
if ((parser_data->last.length += length) > raux->uri_buf_size_limit) {
134135
ESP_LOGW(TAG, LOG_FMT("URI length (%"NEWLIB_NANO_COMPAT_FORMAT") greater than supported (%d)"),
135-
NEWLIB_NANO_COMPAT_CAST(parser_data->last.length), HTTPD_MAX_URI_LEN);
136+
NEWLIB_NANO_COMPAT_CAST(parser_data->last.length), raux->uri_buf_size_limit);
136137
parser_data->error = HTTPD_414_URI_TOO_LONG;
137138
parser_data->status = PARSING_FAILED;
138139
return ESP_FAIL;
@@ -215,7 +216,7 @@ static esp_err_t cb_header_field(http_parser *parser, const char *at, size_t len
215216
parser_data->last.at = ra->scratch;
216217
parser_data->last.length = 0;
217218
parser_data->status = PARSING_HDR_FIELD;
218-
219+
ra->scratch_size_limit = ra->hdr_buf_size_limit;
219220
/* Stop parsing for now and give control to process */
220221
if (pause_parsing(parser, at) != ESP_OK) {
221222
parser_data->error = HTTPD_500_INTERNAL_SERVER_ERROR;
@@ -232,6 +233,7 @@ static esp_err_t cb_header_field(http_parser *parser, const char *at, size_t len
232233
parser_data->last.at = at;
233234
parser_data->last.length = 0;
234235
parser_data->status = PARSING_HDR_FIELD;
236+
ra->scratch_size_limit = ra->hdr_buf_size_limit;
235237

236238
/* Increment header count */
237239
ra->req_hdrs_count++;
@@ -413,7 +415,6 @@ static esp_err_t cb_headers_complete(http_parser *parser)
413415
static esp_err_t cb_on_body(http_parser *parser, const char *at, size_t length)
414416
{
415417
parser_data_t *parser_data = (parser_data_t *) parser->data;
416-
417418
/* Check previous status */
418419
if (parser_data->status != PARSING_BODY) {
419420
ESP_LOGE(TAG, LOG_FMT("unexpected state transition"));
@@ -445,7 +446,6 @@ static esp_err_t cb_on_body(http_parser *parser, const char *at, size_t length)
445446
static esp_err_t cb_no_body(http_parser *parser)
446447
{
447448
parser_data_t *parser_data = (parser_data_t *) parser->data;
448-
449449
/* Check previous status */
450450
if (parser_data->status == PARSING_URL) {
451451
ESP_LOGD(TAG, LOG_FMT("no headers"));
@@ -485,11 +485,21 @@ static int read_block(httpd_req_t *req, size_t offset, size_t length)
485485
struct httpd_req_aux *raux = req->aux;
486486

487487
/* Limits the read to scratch buffer size */
488-
ssize_t buf_len = MIN(length, (sizeof(raux->scratch) - offset));
488+
ssize_t buf_len = MIN(length, (raux->scratch_size_limit - offset));
489489
if (buf_len <= 0) {
490490
return 0;
491491
}
492-
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+
}
498+
if (raux->scratch == NULL) {
499+
return 0;
500+
}
501+
raux->scratch_cur_size += buf_len;
502+
ESP_LOGD(TAG, "scratch size = %d", raux->scratch_cur_size);
493503
/* Receive data into buffer. If data is pending (from unrecv) then return
494504
* immediately after receiving pending data, as pending data may just complete
495505
* this request packet. */
@@ -527,19 +537,20 @@ static int parse_block(http_parser *parser, size_t offset, size_t length)
527537
httpd_req_t *req = data->req;
528538
struct httpd_req_aux *raux = req->aux;
529539
size_t nparsed = 0;
530-
540+
data->last.at = raux->scratch;
531541
if (!length) {
532542
/* Parsing is still happening but nothing to
533543
* parse means no more space left on buffer,
534544
* therefore it can be inferred that the
535545
* request URI/header must be too long */
536-
ESP_LOGW(TAG, LOG_FMT("request URI/header too long"));
537546
switch (data->status) {
538547
case PARSING_URL:
548+
ESP_LOGW(TAG, LOG_FMT("request URI too long"));
539549
data->error = HTTPD_414_URI_TOO_LONG;
540550
break;
541551
case PARSING_HDR_FIELD:
542552
case PARSING_HDR_VALUE:
553+
ESP_LOGW(TAG, LOG_FMT("request header too long"));
543554
data->error = HTTPD_431_REQ_HDR_FIELDS_TOO_LARGE;
544555
break;
545556
default:
@@ -649,7 +660,6 @@ static esp_err_t httpd_parse_req(struct httpd_data *hd)
649660
/* This is used by the callbacks to track
650661
* data usage of the buffer */
651662
parser_data.raw_datalen = blk_len + offset;
652-
653663
/* Parse data block from buffer */
654664
if ((offset = parse_block(&parser, offset, blk_len)) < 0) {
655665
/* HTTP error occurred.
@@ -679,13 +689,17 @@ static void init_req(httpd_req_t *r, httpd_config_t *config)
679689
static void init_req_aux(struct httpd_req_aux *ra, httpd_config_t *config)
680690
{
681691
ra->sd = 0;
682-
memset(ra->scratch, 0, sizeof(ra->scratch));
683692
ra->remaining_len = 0;
684693
ra->status = 0;
685694
ra->content_type = 0;
686695
ra->first_chunk_sent = 0;
687696
ra->req_hdrs_count = 0;
688697
ra->resp_hdrs_count = 0;
698+
ra->scratch = NULL;
699+
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;
689703
#if CONFIG_HTTPD_WS_SUPPORT
690704
ra->ws_handshake_detect = false;
691705
#endif
@@ -716,6 +730,10 @@ static void httpd_req_cleanup(httpd_req_t *r)
716730

717731
/* Clear out the request and request_aux structures */
718732
ra->sd = NULL;
733+
free(ra->scratch);
734+
ra->scratch = NULL;
735+
ra->scratch_cur_size = 0;
736+
ra->scratch_size_limit = 0;
719737
r->handle = NULL;
720738
r->aux = NULL;
721739
r->user_ctx = NULL;

components/esp_http_server/src/httpd_txrx.c

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2018-2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2018-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -132,7 +132,6 @@ int httpd_recv_with_opt(httpd_req_t *r, char *buf, size_t buf_len, bool halt_aft
132132
}
133133
return ret;
134134
}
135-
136135
ESP_LOGD(TAG, LOG_FMT("received length = %"NEWLIB_NANO_COMPAT_FORMAT), NEWLIB_NANO_COMPAT_CAST((ret + pending_len)));
137136
return ret + pending_len;
138137
}
@@ -243,16 +242,27 @@ esp_err_t httpd_resp_send(httpd_req_t *r, const char *buf, ssize_t buf_len)
243242
if (buf_len == HTTPD_RESP_USE_STRLEN) {
244243
buf_len = strlen(buf);
245244
}
246-
245+
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) {
247+
return ESP_ERR_HTTPD_RESP_HDR;
248+
}
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");
256+
return ESP_ERR_HTTPD_ALLOC_MEM;
257+
}
258+
ra->scratch_cur_size = required_size;
259+
ESP_LOGD(TAG, "scratch size = %d", ra->scratch_cur_size);
247260
/* Request headers are no longer available */
248261
ra->req_hdrs_count = 0;
249262

250263
/* Size of essential headers is limited by scratch buffer size */
251-
if (snprintf(ra->scratch, sizeof(ra->scratch), httpd_hdr_str,
252-
ra->status, ra->content_type, buf_len) >= sizeof(ra->scratch)) {
253-
return ESP_ERR_HTTPD_RESP_HDR;
254-
}
255-
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);
256266
/* Sending essential headers */
257267
if (httpd_send_all(r, ra->scratch, strlen(ra->scratch)) != ESP_OK) {
258268
return ESP_ERR_HTTPD_RESP_SEND;
@@ -319,13 +329,24 @@ esp_err_t httpd_resp_send_chunk(httpd_req_t *r, const char *buf, ssize_t buf_len
319329

320330
/* Request headers are no longer available */
321331
ra->req_hdrs_count = 0;
322-
332+
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) {
334+
return ESP_ERR_HTTPD_RESP_HDR;
335+
}
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");
343+
return ESP_ERR_HTTPD_ALLOC_MEM;
344+
}
345+
ra->scratch_cur_size = required_size;
346+
ESP_LOGD(TAG, "scratch size = %d", ra->scratch_cur_size);
323347
if (!ra->first_chunk_sent) {
324348
/* Size of essential headers is limited by scratch buffer size */
325-
if (snprintf(ra->scratch, sizeof(ra->scratch), httpd_chunked_hdr_str,
326-
ra->status, ra->content_type) >= sizeof(ra->scratch)) {
327-
return ESP_ERR_HTTPD_RESP_HDR;
328-
}
349+
snprintf(ra->scratch, ra->scratch_cur_size, httpd_chunked_hdr_str, ra->status, ra->content_type);
329350

330351
/* Sending essential headers */
331352
if (httpd_send_all(r, ra->scratch, strlen(ra->scratch)) != ESP_OK) {

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);

examples/protocols/https_server/simple/main/main.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ static esp_err_t root_get_handler(httpd_req_t *req)
4444
{
4545
httpd_resp_set_type(req, "text/html");
4646
httpd_resp_send(req, "<h1>Hello Secure World!</h1>", HTTPD_RESP_USE_STRLEN);
47-
4847
return ESP_OK;
4948
}
5049

0 commit comments

Comments
 (0)