espressif / ESP8266_RTOS_SDK

Latest ESP8266 SDK based on FreeRTOS, esp-idf style.
http://bbs.espressif.com
Apache License 2.0
3.32k stars 1.56k forks source link

HTTP server stop and start is not working properly (GIT8266O-755) #1177

Open klew opened 2 years ago

klew commented 2 years ago

Environment

Problem Description

HTTP web server stop and start again is not working properly when there was some connection established to that web server before it was stopped. Wi-Fi connection has to be maintained without interruption.

Problem seems to be related to SO_REUSEADDR socket setting. By default it is enabled in sdkconfig, however it seems to be not taken into account on ESP8266 RTOS. The same testing procedure works as expected on standard ESP-IDF SDK on any ESP32 board. Also if there is Wi-Fi disconnect, then this problem doesn't appear (it seems that Wi-Fi disconnect causes some cleanup on used sockets).

It seems to be the same problem as this issue: https://github.com/espressif/esp-idf/issues/3381 Applying correction from patch file from there is solving this problem.

Expected Behavior

It should be possible to restart web server.

Actual Behavior

When trying to start server again, following error is generated: E (22562) httpd: httpd_server_init: error in bind (112)

Steps to repropduce

Problematic scenario:

  1. Start http server (httpd_start(...) )
  2. Open some page from that server (actually anything that establish connection with created web server)
  3. Stop http server (httpd_stop(...) )
  4. Start http server again -> error

Code to reproduce this issue

I reporoduced this problem on standard http_server simple example https://github.com/espressif/ESP8266_RTOS_SDK/blob/master/examples/protocols/http_server/simple/main/main.c with modified app_main method, so it will try to restart web server every 10 s.

void app_main()
{
    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    ESP_ERROR_CHECK(example_connect());

    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &connect_handler, &server));
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disconnect_handler, &server));

    server = start_webserver();
    while (1) {
      vTaskDelay(1000); // 10 s delay - please open any page from this webserver
                        // before this time elapses
      stop_webserver(server);
      server = start_webserver();
    }
}

Debug Logs

E (22562) httpd: httpd_server_init: error in bind (112)

ESP-YJM commented 2 years ago

@klew I think you can add https://github.com/espressif/esp-idf/blob/master/components/esp_http_server/src/httpd_main.c#L316-L321 in ESP8266 https://github.com/espressif/ESP8266_RTOS_SDK/blob/master/components/esp_http_server/src/httpd_main.c#L265 before bind operation.

klew commented 2 years ago

Thanks. I already mentioned in issue that this part from ESP-IDF is fixing this problem. I'm not looking for a workaround here, but for a correction in SDK :)

ESP-YJM commented 2 years ago

Thanks. I already mentioned in issue that this part from ESP-IDF is fixing this problem. I'm not looking for a workaround here, but for a correction in SDK :)

Thanks for pointing out this issue and offer the correct method.

SalahSoliman commented 3 months ago

Hello, I'm facing the same issue.

I noticed this weird way of handling the thread:

static void httpd_thread(void *arg)
{
    int ret;
    struct httpd_data *hd = (struct httpd_data *) arg;
    hd->hd_td.status = THREAD_RUNNING;

    ESP_LOGD(TAG, LOG_FMT("web server started"));
    while (1) {
        ret = httpd_server(hd);
        if (ret != ESP_OK) {
            break;
        }
    }

    ESP_LOGD(TAG, LOG_FMT("web server exiting"));
    close(hd->msg_fd);
    cs_free_ctrl_sock(hd->ctrl_fd);
    httpd_close_all_sessions(hd);
    close(hd->listen_fd);
    hd->hd_td.status = THREAD_STOPPED;
    httpd_os_thread_delete();
}

So as you can see http_thread will only allow the thread to be closed if an error occured.

while the httpd_stop depends on the state of thread to be stopped:

esp_err_t httpd_stop(httpd_handle_t handle)
{
    struct httpd_data *hd = (struct httpd_data *) handle;
    if (hd == NULL) {
        return ESP_ERR_INVALID_ARG;
    }

    struct httpd_ctrl_data msg;
    memset(&msg, 0, sizeof(msg));
    msg.hc_msg = HTTPD_CTRL_SHUTDOWN;
    cs_send_to_ctrl_sock(hd->msg_fd, hd->config.ctrl_port, &msg, sizeof(msg));

    ESP_LOGD(TAG, LOG_FMT("sent control msg to stop server"));
    while (hd->hd_td.status != THREAD_STOPPED) {
        httpd_os_thread_sleep(100);
    }
    .
    .
    .
}

So this is a deadlock scenario, the httpd_stop will run forever until an error is received, so is there a plan to fix this?

(EDIT) The call to select is blocking.

ESP-YJM commented 3 months ago

@SalahSoliman We are sorry to inform you that there is no plan to fix these issues in ESP8266. You can refer to the relevant changes in IDF and make manual changes yourself.