diff --git a/components/esp_http_server/src/esp_httpd_priv.h b/components/esp_http_server/src/esp_httpd_priv.h index f7e1eff1bb..f4fa57e5a4 100644 --- a/components/esp_http_server/src/esp_httpd_priv.h +++ b/components/esp_http_server/src/esp_httpd_priv.h @@ -69,6 +69,7 @@ struct sock_db { httpd_recv_func_t recv_fn; /*!< Receive function for this socket */ httpd_pending_func_t pending_fn; /*!< Pending function for this socket */ uint64_t lru_counter; /*!< LRU Counter indicating when the socket was last used */ + bool lru_socket; /*!< Flag indicating LRU socket */ char pending_data[PARSER_BLOCK_SIZE]; /*!< Buffer for pending data to be received */ size_t pending_len; /*!< Length of pending data to be received */ #ifdef CONFIG_HTTPD_WS_SUPPORT diff --git a/components/esp_http_server/src/httpd_main.c b/components/esp_http_server/src/httpd_main.c index 67b86e1cd9..471db82c9c 100644 --- a/components/esp_http_server/src/httpd_main.c +++ b/components/esp_http_server/src/httpd_main.c @@ -67,8 +67,6 @@ static esp_err_t httpd_accept_conn(struct httpd_data *hd, int listen_fd) close(new_fd); return ESP_FAIL; } - httpd_sess_update_lru_counter(hd->hd_sd->handle, new_fd); - ESP_LOGD(TAG, LOG_FMT("complete")); return ESP_OK; } diff --git a/components/esp_http_server/src/httpd_sess.c b/components/esp_http_server/src/httpd_sess.c index 6d09cd38bd..5a8ecfdcff 100644 --- a/components/esp_http_server/src/httpd_sess.c +++ b/components/esp_http_server/src/httpd_sess.c @@ -350,6 +350,8 @@ esp_err_t httpd_sess_close_lru(struct httpd_data *hd) } } ESP_LOGD(TAG, LOG_FMT("fd = %d"), lru_fd); + struct sock_db *sd = httpd_sess_get(hd, lru_fd); + sd->lru_socket = true; return httpd_sess_trigger_close(hd, lru_fd); } @@ -380,11 +382,12 @@ static void httpd_sess_close(void *arg) { struct sock_db *sock_db = (struct sock_db *)arg; if (sock_db) { - if (sock_db->lru_counter == 0) { + if (sock_db->lru_counter == 0 && !sock_db->lru_socket) { ESP_LOGD(TAG, "Skipping session close for %d as it seems to be a race condition", sock_db->fd); return; } int fd = sock_db->fd; + sock_db->lru_socket = false; struct httpd_data *hd = (struct httpd_data *) sock_db->handle; httpd_sess_delete(hd, fd); close(fd); diff --git a/examples/protocols/http_server/simple/http_server_simple_test.py b/examples/protocols/http_server/simple/http_server_simple_test.py index 39fd753df0..fd11dc0b10 100644 --- a/examples/protocols/http_server/simple/http_server_simple_test.py +++ b/examples/protocols/http_server/simple/http_server_simple_test.py @@ -23,11 +23,42 @@ import os import string import random +import threading +import time +import socket + from tiny_test_fw import Utility import ttfw_idf from idf_http_server_test import client +class http_client_thread(threading.Thread): + def __init__(self, ip, port, delay): + threading.Thread.__init__(self) + self.ip = ip + self.port = port + self.delay = delay + self.exc = None + + # Thread function used to open a socket and wait for specific amount of time before returning + def open_connection(self, ip, port, delay): + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(delay) + s.connect((ip, port)) + time.sleep(delay) + + def run(self): + try: + self.open_connection(self.ip, self.port, self.delay) + except socket.timeout as e: + self.exc = e + + def join(self, timeout=None): + threading.Thread.join(self) + if self.exc: + raise self.exc + + # When running on local machine execute the following before running this script # > make app bootloader # > make print_flash_cmd | tail -n 1 > build/download.config @@ -100,5 +131,44 @@ def test_examples_protocol_http_server_simple(env, extra_data): dut1.expect("Found URL query => " + query, timeout=30) +@ttfw_idf.idf_example_test(env_tag="Example_WIFI") +def test_examples_protocol_http_server_lru_purge_enable(env, extra_data): + # Acquire DUT + dut1 = env.get_dut("http_server", "examples/protocols/http_server/simple", dut_class=ttfw_idf.ESP32DUT) + + # Get binary file + binary_file = os.path.join(dut1.app.binary_path, "simple.bin") + bin_size = os.path.getsize(binary_file) + ttfw_idf.log_performance("http_server_bin_size", "{}KB".format(bin_size // 1024)) + + # Upload binary and start testing + Utility.console_log("Starting http_server simple test app") + dut1.start_app() + + # Parse IP address of STA + Utility.console_log("Waiting to connect with AP") + got_ip = dut1.expect(re.compile(r"(?:[\s\S]*)IPv4 address: (\d+.\d+.\d+.\d+)"), timeout=30)[0] + got_port = dut1.expect(re.compile(r"(?:[\s\S]*)Starting server on port: '(\d+)'"), timeout=30)[0] + + Utility.console_log("Got IP : " + got_ip) + Utility.console_log("Got Port : " + got_port) + + # Expected Logs + dut1.expect("Registering URI handlers", timeout=30) + threads = [] + # Open 20 sockets, one from each thread + for _ in range(20): + try: + thread = http_client_thread(got_ip, (int(got_port)), 20) + thread.start() + threads.append(thread) + except OSError as err: + Utility.console_log("Error: unable to start thread, " + err) + + for t in threads: + t.join() + + if __name__ == '__main__': test_examples_protocol_http_server_simple() + test_examples_protocol_http_server_lru_purge_enable() diff --git a/examples/protocols/http_server/simple/main/main.c b/examples/protocols/http_server/simple/main/main.c index aa4738375a..3aea3e3da0 100644 --- a/examples/protocols/http_server/simple/main/main.c +++ b/examples/protocols/http_server/simple/main/main.c @@ -223,6 +223,7 @@ static httpd_handle_t start_webserver(void) { httpd_handle_t server = NULL; httpd_config_t config = HTTPD_DEFAULT_CONFIG(); + config.lru_purge_enable = true; // Start the httpd server ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);