From 47a106879a0219e64d6cd40a3ee0ee5381163dfb Mon Sep 17 00:00:00 2001 From: Anurag Kar Date: Mon, 8 Oct 2018 16:45:45 +0530 Subject: [PATCH 1/7] HTTP Server : Return HTTPD_SOCK_ERR_ based on errno set during send / recv This feature allows `httpd_req_recv()` and `httpd_send()` functions to return specific `HTTPD_SOCK_ERR_` codes in case of socket errors. This is useful in case of errors like `EAGAIN`, `EINTR`, etc. when the user may want to retry `httpd_req_recv()` / `httpd_send()` function call. --- components/http_server/include/http_server.h | 32 ++++++++++++-- components/http_server/src/httpd_txrx.c | 46 +++++++++++++++----- 2 files changed, 62 insertions(+), 16 deletions(-) diff --git a/components/http_server/include/http_server.h b/components/http_server/include/http_server.h index cdbd039ca8..fa65dd32fb 100644 --- a/components/http_server/include/http_server.h +++ b/components/http_server/include/http_server.h @@ -348,19 +348,39 @@ esp_err_t httpd_unregister_uri(httpd_handle_t handle, const char* uri); * @{ */ +#define HTTPD_SOCK_ERR_FAIL -1 +#define HTTPD_SOCK_ERR_INVALID -2 +#define HTTPD_SOCK_ERR_TIMEOUT -3 + /** * @brief Prototype for HTTPDs low-level send function + * + * @note User specified send function must handle errors internally, + * depending upon the set value of errno, and return specific + * HTTPD_SOCK_ERR_ codes, which will eventually be conveyed as + * return value of httpd_send() function + * * @return * - Bytes : The number of bytes sent successfully - * - -VE : In case of error + * - HTTPD_SOCK_ERR_INVALID : Invalid arguments + * - HTTPD_SOCK_ERR_TIMEOUT : Timeout/interrupted while calling socket send() + * - HTTPD_SOCK_ERR_FAIL : Unrecoverable error while calling socket send() */ typedef int (*httpd_send_func_t)(int sockfd, const char *buf, size_t buf_len, int flags); /** * @brief Prototype for HTTPDs low-level recv function + * + * @note User specified recv function must handle errors internally, + * depending upon the set value of errno, and return specific + * HTTPD_SOCK_ERR_ codes, which will eventually be conveyed as + * return value of httpd_req_recv() function + * * @return * - Bytes : The number of bytes received successfully - * - -VE : In case of error + * - HTTPD_SOCK_ERR_INVALID : Invalid arguments + * - HTTPD_SOCK_ERR_TIMEOUT : Timeout/interrupted while calling socket recv() + * - HTTPD_SOCK_ERR_FAIL : Unrecoverable error while calling socket recv() */ typedef int (*httpd_recv_func_t)(int sockfd, char *buf, size_t buf_len, int flags); @@ -462,7 +482,9 @@ int httpd_req_to_sockfd(httpd_req_t *r); * @return * - Bytes : Number of bytes read into the buffer successfully * - Zero : When no more data is left for read - * - -1 : On raw recv error / Null arguments / Request pointer is invalid + * - HTTPD_SOCK_ERR_INVALID : Invalid arguments + * - HTTPD_SOCK_ERR_TIMEOUT : Timeout/interrupted while calling socket recv() + * - HTTPD_SOCK_ERR_FAIL : Unrecoverable error while calling socket recv() */ int httpd_req_recv(httpd_req_t *r, char *buf, size_t buf_len); @@ -796,7 +818,9 @@ esp_err_t httpd_resp_send_404(httpd_req_t *r); * * @return * - Bytes : Number of bytes that were sent successfully - * - -1 : Error in raw send / Invalid request / Null arguments + * - HTTPD_SOCK_ERR_INVALID : Invalid arguments + * - HTTPD_SOCK_ERR_TIMEOUT : Timeout/interrupted while calling socket send() + * - HTTPD_SOCK_ERR_FAIL : Unrecoverable error while calling socket send() */ int httpd_send(httpd_req_t *r, const char *buf, size_t buf_len); diff --git a/components/http_server/src/httpd_txrx.c b/components/http_server/src/httpd_txrx.c index 5cf95b351d..d45d960d46 100644 --- a/components/http_server/src/httpd_txrx.c +++ b/components/http_server/src/httpd_txrx.c @@ -55,18 +55,18 @@ esp_err_t httpd_set_recv_override(httpd_req_t *r, httpd_recv_func_t recv_func) int httpd_send(httpd_req_t *r, const char *buf, size_t buf_len) { if (r == NULL || buf == NULL) { - return -1; + return HTTPD_SOCK_ERR_INVALID; } if (!httpd_valid_req(r)) { - return -1; + return HTTPD_SOCK_ERR_INVALID; } struct httpd_req_aux *ra = r->aux; int ret = ra->sd->send_fn(ra->sd->fd, buf, buf_len, 0); if (ret < 0) { ESP_LOGD(TAG, LOG_FMT("error in send_fn")); - return -1; + return ret; } return ret; } @@ -128,7 +128,7 @@ int httpd_recv_with_opt(httpd_req_t *r, char *buf, size_t buf_len, bool halt_aft int ret = ra->sd->recv_fn(ra->sd->fd, buf, buf_len, 0); if (ret < 0) { ESP_LOGD(TAG, LOG_FMT("error in recv_fn")); - return -1; + return ret; } ESP_LOGD(TAG, LOG_FMT("received length = %d"), ret + pending_len); @@ -429,12 +429,12 @@ esp_err_t httpd_resp_send_err(httpd_req_t *req, httpd_err_resp_t error) int httpd_req_recv(httpd_req_t *r, char *buf, size_t buf_len) { if (r == NULL || buf == NULL) { - return -1; + return HTTPD_SOCK_ERR_INVALID; } if (!httpd_valid_req(r)) { ESP_LOGW(TAG, LOG_FMT("invalid request")); - return -1; + return HTTPD_SOCK_ERR_INVALID; } struct httpd_req_aux *ra = r->aux; @@ -450,8 +450,7 @@ int httpd_req_recv(httpd_req_t *r, char *buf, size_t buf_len) int ret = httpd_recv(r, buf, buf_len); if (ret < 0) { ESP_LOGD(TAG, LOG_FMT("error in httpd_recv")); - ra->remaining_len = 0; - return -1; + return ret; } ra->remaining_len -= ret; ESP_LOGD(TAG, LOG_FMT("received length = %d"), ret); @@ -473,15 +472,38 @@ int httpd_req_to_sockfd(httpd_req_t *r) return ra->sd->fd; } +static int httpd_sock_err(const char *ctx) +{ + int errval; + + ESP_LOGW(TAG, LOG_FMT("errno in %s : %d"), ctx, errno); + + switch(errno) { + case EAGAIN: + case EINTR: + errval = HTTPD_SOCK_ERR_TIMEOUT; + break; + case EINVAL: + case EBADF: + case EFAULT: + case ENOTSOCK: + errval = HTTPD_SOCK_ERR_INVALID; + break; + default: + errval = HTTPD_SOCK_ERR_FAIL; + } + return errval; +} + int httpd_default_send(int sockfd, const char *buf, size_t buf_len, int flags) { if (buf == NULL) { - return ESP_ERR_INVALID_ARG; + return HTTPD_SOCK_ERR_INVALID; } int ret = send(sockfd, buf, buf_len, flags); if (ret < 0) { - ESP_LOGW(TAG, LOG_FMT("error in send = %d"), errno); + return httpd_sock_err("send"); } return ret; } @@ -489,12 +511,12 @@ int httpd_default_send(int sockfd, const char *buf, size_t buf_len, int flags) int httpd_default_recv(int sockfd, char *buf, size_t buf_len, int flags) { if (buf == NULL) { - return ESP_ERR_INVALID_ARG; + return HTTPD_SOCK_ERR_INVALID; } int ret = recv(sockfd, buf, buf_len, flags); if (ret < 0) { - ESP_LOGW(TAG, LOG_FMT("error in recv = %d"), errno); + return httpd_sock_err("recv"); } return ret; } From 7e04e283d58a6f6f871f57121f35594f29c4ef51 Mon Sep 17 00:00:00 2001 From: Anurag Kar Date: Tue, 9 Oct 2018 11:42:23 +0530 Subject: [PATCH 2/7] HTTP Server : Use getsockopt instead of errno --- components/http_server/src/httpd_txrx.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/components/http_server/src/httpd_txrx.c b/components/http_server/src/httpd_txrx.c index d45d960d46..0d31eaed27 100644 --- a/components/http_server/src/httpd_txrx.c +++ b/components/http_server/src/httpd_txrx.c @@ -472,13 +472,19 @@ int httpd_req_to_sockfd(httpd_req_t *r) return ra->sd->fd; } -static int httpd_sock_err(const char *ctx) +static int httpd_sock_err(const char *ctx, int sockfd) { int errval; + int sock_err; + size_t sock_err_len = sizeof(sock_err); - ESP_LOGW(TAG, LOG_FMT("errno in %s : %d"), ctx, errno); + if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &sock_err, &sock_err_len) < 0) { + ESP_LOGE(TAG, LOG_FMT("error calling getsockopt : %d"), errno); + return HTTPD_SOCK_ERR_FAIL; + } + ESP_LOGW(TAG, LOG_FMT("error in %s : %d"), ctx, sock_err); - switch(errno) { + switch(sock_err) { case EAGAIN: case EINTR: errval = HTTPD_SOCK_ERR_TIMEOUT; @@ -503,7 +509,7 @@ int httpd_default_send(int sockfd, const char *buf, size_t buf_len, int flags) int ret = send(sockfd, buf, buf_len, flags); if (ret < 0) { - return httpd_sock_err("send"); + return httpd_sock_err("send", sockfd); } return ret; } @@ -516,7 +522,7 @@ int httpd_default_recv(int sockfd, char *buf, size_t buf_len, int flags) int ret = recv(sockfd, buf, buf_len, flags); if (ret < 0) { - return httpd_sock_err("recv"); + return httpd_sock_err("recv", sockfd); } return ret; } From 30632c0c340f99de9c51c7a023ebe32c5ae2811a Mon Sep 17 00:00:00 2001 From: Anurag Kar Date: Tue, 9 Oct 2018 17:54:33 +0530 Subject: [PATCH 3/7] HTTP Server : Bug fixed in httpd_recv logic and updated function descriptions --- components/http_server/include/http_server.h | 5 +++-- components/http_server/src/httpd_txrx.c | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/components/http_server/include/http_server.h b/components/http_server/include/http_server.h index fa65dd32fb..dec25768df 100644 --- a/components/http_server/include/http_server.h +++ b/components/http_server/include/http_server.h @@ -378,6 +378,7 @@ typedef int (*httpd_send_func_t)(int sockfd, const char *buf, size_t buf_len, in * * @return * - Bytes : The number of bytes received successfully + * - 0 : Buffer length parameter is zero / connection closed by peer * - HTTPD_SOCK_ERR_INVALID : Invalid arguments * - HTTPD_SOCK_ERR_TIMEOUT : Timeout/interrupted while calling socket recv() * - HTTPD_SOCK_ERR_FAIL : Unrecoverable error while calling socket recv() @@ -480,8 +481,8 @@ int httpd_req_to_sockfd(httpd_req_t *r); * @param[in] buf_len Length of the buffer * * @return - * - Bytes : Number of bytes read into the buffer successfully - * - Zero : When no more data is left for read + * - Bytes : Number of bytes read into the buffer successfully + * - 0 : Buffer length parameter is zero / connection closed by peer * - HTTPD_SOCK_ERR_INVALID : Invalid arguments * - HTTPD_SOCK_ERR_TIMEOUT : Timeout/interrupted while calling socket recv() * - HTTPD_SOCK_ERR_FAIL : Unrecoverable error while calling socket recv() diff --git a/components/http_server/src/httpd_txrx.c b/components/http_server/src/httpd_txrx.c index 0d31eaed27..ed90cb75da 100644 --- a/components/http_server/src/httpd_txrx.c +++ b/components/http_server/src/httpd_txrx.c @@ -128,6 +128,15 @@ int httpd_recv_with_opt(httpd_req_t *r, char *buf, size_t buf_len, bool halt_aft int ret = ra->sd->recv_fn(ra->sd->fd, buf, buf_len, 0); if (ret < 0) { ESP_LOGD(TAG, LOG_FMT("error in recv_fn")); + if ((ret == HTTPD_SOCK_ERR_TIMEOUT) && (pending_len != 0)) { + /* If recv() timeout occurred, but pending data is + * present, return length of pending data. + * This behavior is similar to that of socket recv() + * function, which, in case has only partially read the + * requested length, due to timeout, returns with read + * length, rather than error */ + return pending_len; + } return ret; } From ae5989528e119d05d8b1db0027064dc5605e566d Mon Sep 17 00:00:00 2001 From: Anurag Kar Date: Tue, 9 Oct 2018 18:03:41 +0530 Subject: [PATCH 4/7] HTTP Server : Added helper functions for sending HTTP error 408 and 500 --- components/http_server/include/http_server.h | 47 ++++++++++++++++++++ components/http_server/src/httpd_txrx.c | 10 +++++ 2 files changed, 57 insertions(+) diff --git a/components/http_server/include/http_server.h b/components/http_server/include/http_server.h index dec25768df..6a8e65c794 100644 --- a/components/http_server/include/http_server.h +++ b/components/http_server/include/http_server.h @@ -687,6 +687,7 @@ esp_err_t httpd_resp_send_chunk(httpd_req_t *r, const char *buf, size_t buf_len) #define HTTPD_207 "207 Multi-Status" /*!< HTTP Response 207 */ #define HTTPD_400 "400 Bad Request" /*!< HTTP Response 400 */ #define HTTPD_404 "404 Not Found" /*!< HTTP Response 404 */ +#define HTTPD_408 "408 Request Timeout" /*!< HTTP Response 408 */ #define HTTPD_500 "500 Internal Server Error" /*!< HTTP Response 500 */ /** @@ -791,6 +792,52 @@ esp_err_t httpd_resp_set_hdr(httpd_req_t *r, const char *field, const char *valu */ esp_err_t httpd_resp_send_404(httpd_req_t *r); +/** + * @brief Helper function for HTTP 408 + * + * Send HTTP 408 message. If you wish to send additional data in the body of the + * response, please use the lower-level functions directly. + * + * @note + * - This API is supposed to be called only from the context of + * a URI handler where httpd_req_t* request pointer is valid. + * - Once this API is called, all request headers are purged, so + * request headers need be copied into separate buffers if + * they are required later. + * + * @param[in] r The request being responded to + * + * @return + * - ESP_OK : On successfully sending the response packet + * - ESP_ERR_INVALID_ARG : Null arguments + * - ESP_ERR_HTTPD_RESP_SEND : Error in raw send + * - ESP_ERR_HTTPD_INVALID_REQ : Invalid request pointer + */ +esp_err_t httpd_resp_send_408(httpd_req_t *r); + +/** + * @brief Helper function for HTTP 500 + * + * Send HTTP 500 message. If you wish to send additional data in the body of the + * response, please use the lower-level functions directly. + * + * @note + * - This API is supposed to be called only from the context of + * a URI handler where httpd_req_t* request pointer is valid. + * - Once this API is called, all request headers are purged, so + * request headers need be copied into separate buffers if + * they are required later. + * + * @param[in] r The request being responded to + * + * @return + * - ESP_OK : On successfully sending the response packet + * - ESP_ERR_INVALID_ARG : Null arguments + * - ESP_ERR_HTTPD_RESP_SEND : Error in raw send + * - ESP_ERR_HTTPD_INVALID_REQ : Invalid request pointer + */ +esp_err_t httpd_resp_send_500(httpd_req_t *r); + /** * @brief Raw HTTP send * diff --git a/components/http_server/src/httpd_txrx.c b/components/http_server/src/httpd_txrx.c index ed90cb75da..1ccebabe4f 100644 --- a/components/http_server/src/httpd_txrx.c +++ b/components/http_server/src/httpd_txrx.c @@ -376,6 +376,16 @@ esp_err_t httpd_resp_send_404(httpd_req_t *r) return httpd_resp_send_err(r, HTTPD_404_NOT_FOUND); } +esp_err_t httpd_resp_send_408(httpd_req_t *r) +{ + return httpd_resp_send_err(r, HTTPD_408_REQ_TIMEOUT); +} + +esp_err_t httpd_resp_send_500(httpd_req_t *r) +{ + return httpd_resp_send_err(r, HTTPD_500_SERVER_ERROR); +} + esp_err_t httpd_resp_send_err(httpd_req_t *req, httpd_err_resp_t error) { const char *msg; From 1437646ae07f667ee1a4010cb9dd9a7ba4185e12 Mon Sep 17 00:00:00 2001 From: Anurag Kar Date: Tue, 9 Oct 2018 18:04:37 +0530 Subject: [PATCH 5/7] HTTP Server : Automated 408 error response restricted to timeout in receiving packet header --- components/http_server/src/httpd_parse.c | 14 +++++++------- components/http_server/src/httpd_uri.c | 1 - 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/components/http_server/src/httpd_parse.c b/components/http_server/src/httpd_parse.c index 64629f5181..9a15e2f207 100644 --- a/components/http_server/src/httpd_parse.c +++ b/components/http_server/src/httpd_parse.c @@ -86,7 +86,7 @@ static esp_err_t verify_url (http_parser *parser) } /* Keep URI with terminating null character. Note URI string pointed - * by 'at' is not NULL terminated, therfore use length provided by + * 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); @@ -291,7 +291,7 @@ static esp_err_t cb_headers_complete(http_parser *parser) return ESP_FAIL; } - /* In absence of body/chunked enoding, http_parser sets content_len to -1 */ + /* 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); @@ -391,14 +391,14 @@ static int read_block(httpd_req_t *req, size_t offset, size_t length) } /* Receive data into buffer. If data is pending (from unrecv) then return - * immediatly after receiving pending data, as pending data may just complete + * 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")); - /* Connection error. Notify Timeout in all cases. - * Need some way to check errno for ETIMEDOUT. */ - httpd_resp_send_err(req, HTTPD_408_REQ_TIMEOUT); + if (nbytes == HTTPD_SOCK_ERR_TIMEOUT) { + httpd_resp_send_err(req, HTTPD_408_REQ_TIMEOUT); + } return -1; } else if (nbytes == 0) { ESP_LOGD(TAG, LOG_FMT("connection closed")); @@ -500,7 +500,7 @@ static esp_err_t httpd_parse_req(struct httpd_data *hd) http_parser parser; parser_data_t parser_data; - /* Initilaize parser */ + /* Initialize parser */ parse_init(r, &parser, &parser_data); /* Set offset to start of scratch buffer */ diff --git a/components/http_server/src/httpd_uri.c b/components/http_server/src/httpd_uri.c index 07909e2a69..b74a7f62a3 100644 --- a/components/http_server/src/httpd_uri.c +++ b/components/http_server/src/httpd_uri.c @@ -216,7 +216,6 @@ esp_err_t httpd_uri(struct httpd_data *hd) if (uri->handler(req) != ESP_OK) { /* Handler returns error, this socket should be closed */ ESP_LOGW(TAG, LOG_FMT("uri handler execution failed")); - httpd_resp_send_err(req, HTTPD_408_REQ_TIMEOUT); return ESP_FAIL; } return ESP_OK; From 6a3fa5c180497b26cb207e94fc9f2ba436b9d485 Mon Sep 17 00:00:00 2001 From: Anurag Kar Date: Tue, 9 Oct 2018 18:07:38 +0530 Subject: [PATCH 6/7] HTTP Server Examples : Updated examples to manually return 408 and 500 errors --- .../http_server/advanced_tests/main/tests.c | 16 +++++++++++--- .../advanced_tests/scripts/test.py | 22 +++++++++---------- .../persistent_sockets/main/main.c | 10 +++++++-- .../protocols/http_server/simple/main/main.c | 11 ++++++++-- 4 files changed, 40 insertions(+), 19 deletions(-) diff --git a/examples/protocols/http_server/advanced_tests/main/tests.c b/examples/protocols/http_server/advanced_tests/main/tests.c index 1aa64bcbc6..0d7d8327f6 100644 --- a/examples/protocols/http_server/advanced_tests/main/tests.c +++ b/examples/protocols/http_server/advanced_tests/main/tests.c @@ -50,13 +50,17 @@ esp_err_t echo_post_handler(httpd_req_t *req) int ret; if (!buf) { + httpd_resp_send_500(req); return ESP_FAIL; } while (off < req->content_len) { /* Read data received in the request */ ret = httpd_req_recv(req, buf + off, req->content_len - off); - if (ret < 0) { + if (ret <= 0) { + if (ret == HTTPD_SOCK_ERR_TIMEOUT) { + httpd_resp_send_408(req); + } free (buf); return ESP_FAIL; } @@ -105,7 +109,10 @@ esp_err_t adder_post_handler(httpd_req_t *req) /* Read data received in the request */ ret = httpd_req_recv(req, buf, sizeof(buf)); - if (ret < 0) { + if (ret <= 0) { + if (ret == HTTPD_SOCK_ERR_TIMEOUT) { + httpd_resp_send_408(req); + } return ESP_FAIL; } @@ -137,7 +144,10 @@ esp_err_t leftover_data_post_handler(httpd_req_t *req) /* Read data received in the request */ ret = httpd_req_recv(req, buf, sizeof(buf) - 1); - if (ret < 0) { + if (ret <= 0) { + if (ret == HTTPD_SOCK_ERR_TIMEOUT) { + httpd_resp_send_408(req); + } return ESP_FAIL; } diff --git a/examples/protocols/http_server/advanced_tests/scripts/test.py b/examples/protocols/http_server/advanced_tests/scripts/test.py index f1fcedaec4..4f8f0fd4d5 100644 --- a/examples/protocols/http_server/advanced_tests/scripts/test.py +++ b/examples/protocols/http_server/advanced_tests/scripts/test.py @@ -350,7 +350,7 @@ def get_hello(dut, port): def put_hello(dut, port): # PUT /hello returns 405' - Utility.console_log("[test] PUT /hello returns 405' =>", end=' ') + Utility.console_log("[test] PUT /hello returns 405 =>", end=' ') conn = http.client.HTTPConnection(dut, int(port), timeout=15) conn.request("PUT", "/hello", "Hello") resp = conn.getresponse() @@ -363,7 +363,7 @@ def put_hello(dut, port): def post_hello(dut, port): # POST /hello returns 405' - Utility.console_log("[test] POST /hello returns 404' =>", end=' ') + Utility.console_log("[test] POST /hello returns 404 =>", end=' ') conn = http.client.HTTPConnection(dut, int(port), timeout=15) conn.request("POST", "/hello", "Hello") resp = conn.getresponse() @@ -376,7 +376,7 @@ def post_hello(dut, port): def post_echo(dut, port): # POST /echo echoes data' - Utility.console_log("[test] POST /echo echoes data' =>", end=' ') + Utility.console_log("[test] POST /echo echoes data =>", end=' ') conn = http.client.HTTPConnection(dut, int(port), timeout=15) conn.request("POST", "/echo", "Hello") resp = conn.getresponse() @@ -392,7 +392,7 @@ def post_echo(dut, port): def put_echo(dut, port): # PUT /echo echoes data' - Utility.console_log("[test] PUT /echo echoes data' =>", end=' ') + Utility.console_log("[test] PUT /echo echoes data =>", end=' ') conn = http.client.HTTPConnection(dut, int(port), timeout=15) conn.request("PUT", "/echo", "Hello") resp = conn.getresponse() @@ -408,7 +408,7 @@ def put_echo(dut, port): def get_echo(dut, port): # GET /echo returns 404' - Utility.console_log("[test] GET /echo returns 405' =>", end=' ') + Utility.console_log("[test] GET /echo returns 405 =>", end=' ') conn = http.client.HTTPConnection(dut, int(port), timeout=15) conn.request("GET", "/echo") resp = conn.getresponse() @@ -595,7 +595,7 @@ def packet_size_limit_test(dut, port, test_size): s.request("POST", url=path, body=random_data) resp = s.getresponse() if not test_val("Error", "200", str(resp.status)): - if test_val("Error", "408", str(resp.status)): + if test_val("Error", "500", str(resp.status)): Utility.console_log("Data too large to be allocated") test_size = test_size//10 else: @@ -619,14 +619,12 @@ def packet_size_limit_test(dut, port, test_size): def code_500_server_error_test(dut, port): Utility.console_log("[test] 500 Server Error test =>", end=' ') s = Session(dut, port) - s.client.sendall(b"abcdefgh\0") + # Sending a very large content length will cause malloc to fail + content_len = 2**31 + s.client.sendall(("POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nContent-Length: " + str(content_len) + "\r\n\r\nABCD").encode()) s.read_resp_hdrs() resp = s.read_resp_data() - # Presently server sends back 400 Bad Request - #if not test_val("Server Error", "500", s.status): - #s.close() - #return False - if not test_val("Server Error", "400", s.status): + if not test_val("Server Error", "500", s.status): s.close() return False s.close() diff --git a/examples/protocols/http_server/persistent_sockets/main/main.c b/examples/protocols/http_server/persistent_sockets/main/main.c index 996a05afea..ea6bea5b27 100644 --- a/examples/protocols/http_server/persistent_sockets/main/main.c +++ b/examples/protocols/http_server/persistent_sockets/main/main.c @@ -48,7 +48,10 @@ esp_err_t adder_post_handler(httpd_req_t *req) /* Read data received in the request */ ret = httpd_req_recv(req, buf, sizeof(buf)); - if (ret < 0) { + if (ret <= 0) { + if (ret == HTTPD_SOCK_ERR_TIMEOUT) { + httpd_resp_send_408(req); + } return ESP_FAIL; } @@ -111,7 +114,10 @@ esp_err_t adder_put_handler(httpd_req_t *req) /* Read data received in the request */ ret = httpd_req_recv(req, buf, sizeof(buf)); - if (ret < 0) { + if (ret <= 0) { + if (ret == HTTPD_SOCK_ERR_TIMEOUT) { + httpd_resp_send_408(req); + } return ESP_FAIL; } diff --git a/examples/protocols/http_server/simple/main/main.c b/examples/protocols/http_server/simple/main/main.c index 7e2bd49072..5c8d266a59 100644 --- a/examples/protocols/http_server/simple/main/main.c +++ b/examples/protocols/http_server/simple/main/main.c @@ -122,7 +122,11 @@ esp_err_t echo_post_handler(httpd_req_t *req) while (remaining > 0) { /* Read the data for the request */ if ((ret = httpd_req_recv(req, buf, - MIN(remaining, sizeof(buf)))) < 0) { + MIN(remaining, sizeof(buf)))) <= 0) { + if (ret == HTTPD_SOCK_ERR_TIMEOUT) { + /* Retry receiving if timeout occurred */ + continue; + } return ESP_FAIL; } @@ -156,7 +160,10 @@ esp_err_t ctrl_put_handler(httpd_req_t *req) char buf; int ret; - if ((ret = httpd_req_recv(req, &buf, 1)) < 0) { + if ((ret = httpd_req_recv(req, &buf, 1)) <= 0) { + if (ret == HTTPD_SOCK_ERR_TIMEOUT) { + httpd_resp_send_408(req); + } return ESP_FAIL; } From a0961ad79c73c515d59e24e621ad35f8b82e4d16 Mon Sep 17 00:00:00 2001 From: Anurag Kar Date: Tue, 9 Oct 2018 20:17:26 +0530 Subject: [PATCH 7/7] HTTP Server Docs : Updated to demonstrate handling of timeout errors --- docs/en/api-reference/protocols/http_server.rst | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/en/api-reference/protocols/http_server.rst b/docs/en/api-reference/protocols/http_server.rst index 9016574e14..320dee3f86 100644 --- a/docs/en/api-reference/protocols/http_server.rst +++ b/docs/en/api-reference/protocols/http_server.rst @@ -29,15 +29,26 @@ Application Example /* Our URI handler function to be called during POST /uri request */ esp_err_t post_handler(httpd_req_t *req) { - /* Read request content */ + /* Destination buffer for content of HTTP POST request. + * httpd_req_recv() accepts char* only, but content could + * as well be any binary data (needs type casting). + * In case of string data, null termination will be absent, and + * content length would give length of string */ char[100] content; /* Truncate if content length larger than the buffer */ size_t recv_size = MIN(req->content_len, sizeof(content)); int ret = httpd_req_recv(req, content, recv_size); - if (ret < 0) { - /* In case of recv error, returning ESP_FAIL will + if (ret <= 0) { /* 0 return value indicates connection closed */ + /* Check if timeout occurred */ + if (ret == HTTPD_SOCK_ERR_TIMEOUT) { + /* In case of timeout one can choose to retry calling + * httpd_req_recv(), but to keep it simple, here we + * respond with an HTTP 408 (Request Timeout) error */ + httpd_resp_send_408(req); + } + /* In case of error, returning ESP_FAIL will * ensure that the underlying socket is closed */ return ESP_FAIL; }