Stopping server inside handler using httpd_stop(*server);

zazas321
Posts: 231
Joined: Mon Feb 01, 2021 9:41 am

Stopping server inside handler using httpd_stop(*server);

Postby zazas321 » Mon Mar 21, 2022 4:25 pm

Hello. I use file_serving example from http_server.

I would like to achieve a very simple logic: When the client connects to the AP, start the webserver and serve client webpage. As soon as the last client disconnects from the AP (no more clients connected), I want to stop the webserver and stop the wifi interface.

My functions:

WIFI INIT function:

Code: Select all

void WIFI_INIT_AP_STA(void)
{
    
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_t *ap_netif = esp_netif_create_default_wifi_ap();
    assert(ap_netif);
    esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
    assert(sta_netif);

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    wifi_config_t wifi_config = {
        .ap = {
            .ssid = EXAMPLE_ESP_WIFI_SSID_AP,
            .ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID_AP),
            .channel = EXAMPLE_ESP_WIFI_CHANNEL,
            .password = EXAMPLE_ESP_WIFI_PASS_AP,
            .max_connection = EXAMPLE_MAX_STA_CONN,
            .authmode = WIFI_AUTH_WPA_WPA2_PSK
        },
    };
    if (strlen(EXAMPLE_ESP_WIFI_PASS_AP) == 0) {
        wifi_config.ap.authmode = WIFI_AUTH_OPEN;
    }
    

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI("WIFI AP", "wifi_init_softap finished. SSID:%s password:%s channel:%d",
             EXAMPLE_ESP_WIFI_SSID_AP, EXAMPLE_ESP_WIFI_PASS_AP, EXAMPLE_ESP_WIFI_CHANNEL);

    return ESP_OK;


}







START/STOP WEBSERVER FUNCTION:

Code: Select all

esp_err_t start_file_server(const char *base_path)
{
    static struct file_server_data *server_data = NULL;
    /* Validate file storage base path */
    if (!base_path || strcmp(base_path, "/spiffs") != 0) {
        ESP_LOGE("START_SERVER", "File server presently supports only '/spiffs' as base path");
        return ESP_ERR_INVALID_ARG;
    }

    if (server_data) {
        ESP_LOGE("START_SERVER", "File server already started");
        return ESP_ERR_INVALID_STATE;
    }

    /* Allocate memory for server data */
    server_data = calloc(1, sizeof(struct file_server_data));
    if (!server_data) {
        ESP_LOGE("START_SERVER", "Failed to allocate memory for server data");
        return ESP_ERR_NO_MEM;
    }
    strlcpy(server_data->base_path, base_path,
            sizeof(server_data->base_path));



    httpd_handle_t server = NULL;
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    /* Use the URI wildcard matching function in order to
     * allow the same handler to respond to multiple different
     * target URIs which match the wildcard scheme */
    config.uri_match_fn = httpd_uri_match_wildcard;

    //ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,ESP_EVENT_ANY_ID,&wifi_event_handler,NULL,NULL));
    


    ESP_LOGI("START_SERVER", "Starting HTTP Server on port: '%d'", config.server_port);
    if (httpd_start(&server, &config) != ESP_OK) {
        ESP_LOGE("START_SERVER", "Failed to start file server!");
        return ESP_FAIL;
    }
    

    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, &server));

    //URI handler for getting uploaded files 
    httpd_uri_t file_download = {
        .uri       = "/*",  // Match all URIs of type /path/to/file
        .method    = HTTP_GET,
        .handler   = download_get_handler,
       .user_ctx  = server_data    // Pass server data as context
    };
    httpd_register_uri_handler(server, &file_download);


    return ESP_OK;
}

void stop_webserver(httpd_handle_t server)
{
    ESP_LOGI(TAG_WEBSERVER, "Stopping webserver");
    httpd_stop(server);
}

Notice in the start webserver I create a wifi_event_handler and pass httpd_handle_t server as a parameter
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, &server));


WIFI EVENT HANDLER:

Code: Select all

static void wifi_event_handler(void* arg, esp_event_base_t event_base,int32_t event_id, void* event_data)
{
    if (event_id == WIFI_EVENT_AP_STACONNECTED) {
        wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
        ESP_LOGI(TAG_WEBSERVER, "station "MACSTR" join, AID=%d",MAC2STR(event->mac), event->aid);
        wifi_connected_clients += 1;
        printf("connected clients = %u \n",wifi_connected_clients);
        httpd_handle_t* server = (httpd_handle_t*) arg;
        if (server == NULL) {
            ESP_LOGI(TAG_WEBSERVER, "Starting webserver");
            start_file_server("/spiffs");
        }
    } 
    else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) {
        wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
        ESP_LOGI(TAG_WEBSERVER, "station "MACSTR" leave, AID=%d",MAC2STR(event->mac), event->aid);
        wifi_connected_clients -= 1;
        printf("connected clients = %u \n",wifi_connected_clients);
        if(wifi_connected_clients == 0){
        httpd_handle_t* server = (httpd_handle_t*) arg;
            if (*server) {
                ESP_LOGI(TAG_WEBSERVER, "Stopping webserver");
                stop_webserver(*server);
                *server = NULL;
                STOP_WIFI_AP_STA();
            }
        }
    }
}
Inside the wifi_event handler I keep track of connected and disconnect clients. When there are no more connected clients (wifi_connected_clients = 0), I would like to stop the webserver by calling stop_webserver(*server). However, the program immediately crashes:

Code: Select all

connected clients = 0
I (14800) webserver: Stopping webserver
I (14800) webserver: Stopping webserver
Guru Meditation Error: Core  0 panic'ed (LoadProhibited). Exception was unhandled.

Core  0 register dump:
PC      : 0x401083f3  PS      : 0x00060530  A0      : 0x800daab4  A1      : 0x3ffb9d70  
0x401083f3: httpd_stop at C:/Users/petrikas.lu/esp/esp-idf/components/esp_http_server/src/httpd_main.c:453

A2      : 0x000002f7  A3      : 0x3f41b974  A4      : 0x3f41b980  A5      : 0x000039d0
A6      : 0x3f41b974  A7      : 0x0000000c  A8      : 0x00000000  A9      : 0x3ffb9d20
A10     : 0x4014e3c8  A11     : 0x3f41b974  A12     : 0x3ffb9d70  A13     : 0x0000000c
0x4014e3c8: vprintf at /builds/idf/crosstool-NG/.build/HOST-x86_64-w64-mingw32/xtensa-esp32-elf/src/newlib/newlib/libc/stdio/vprintf.c:30
A14     : 0x3ffb9d60  A15     : 0x0000000c  SAR     : 0x00000004  EXCCAUSE: 0x0000001c
EXCVADDR: 0x00000305  LBEG    : 0x400014fd  LEND    : 0x4000150d  LCOUNT  : 0xfffffffd  


Backtrace:0x401083f0:0x3ffb9d700x400daab1:0x3ffb9da0 0x400dab96:0x3ffb9dc0 0x40157ea1:0x3ffb9e00 0x40157a06:0x3ffb9e30 0x40157ad9:0x3ffb9e70 0x40089251:0x3ffb9e90
0x401083f0: httpd_stop at C:/Users/petrikas.lu/esp/esp-idf/components/esp_http_server/src/httpd_main.c:453

0x400daab1: stop_webserver at C:\Users\petrikas.lu\Work\Projects\test\test\build/../components/WIFI/WIFI.c:333

0x400dab96: wifi_event_handler at C:\Users\petrikas.lu\Work\Projects\test\test\build/../components/WIFI/WIFI.c:1023 (discriminator 13)

0x40157ea1: handler_execute at C:/Users/petrikas.lu/esp/esp-idf/components/esp_event/esp_event.c:145

0x40157a06: esp_event_loop_run at C:/Users/petrikas.lu/esp/esp-idf/components/esp_event/esp_event.c:590 (discriminator 3)

0x40157ad9: esp_event_loop_run_task at C:/Users/petrikas.lu/esp/esp-idf/components/esp_event/esp_event.c:115 (discriminator 15)

0x40089251: vPortTaskWrapper at C:/Users/petrikas.lu/esp/esp-idf/components/freertos/port/xtensa/port.c:131





ELF file SHA256: b79e6e2195ab2e5b

Rebooting...
ets Jul 29 2019 12:21:46

rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0030,len:6604
ho 0 tail 12 room 4
load:0x40078000,len:14808
ho 0 tail 12 room 4
load:0x40080400,len:3792
0x40080400: _init at ??:?

entry 0x40080694
I (30) boot: ESP-IDF v4.4 2nd stage bootloader
I (31) boot: compile time 14:43:07
I (31) boot: chip revision: 3
I (33) boot_comm: chip revision: 3, min. bootloader chip revision: 0
I (40) boot.esp32: SPI Speed      : 80MHz
I (45) boot.esp32: SPI Mode       : DIO
I (50) boot.esp32: SPI Flash Size : 8MB
I (54) boot: Enabling RNG early entropy source...
I (60) boot: Partition Table:
I (63) boot: ## Label            Usage          Type ST Offset   Length
I (71) boot:  0 nvs              WiFi data        01 02 00009000 00004000
I (78) boot:  1 otadata          OTA data         01 00 0000d000 00002000
I (85) boot:  2 phy_init         RF data          01 01 0000f000 00001000
I (93) boot:  3 factory          factory app      00 00 00010000 00190000
I (100) boot:  4 ota_0            OTA app          00 10 001a0000 00190000
I (108) boot:  5 ota_1            OTA app          00 11 00330000 00190000
I (115) boot:  6 storage          Unknown data     01 82 004c0000 00190000



Could someone help me understand what could cause this?
Am I not accessing the httpd_handle_t variable correctly through the handle? Is below not corrrect?:
httpd_handle_t* server = (httpd_handle_t*) arg; ?


Any ideas are appreciated

newcomer777
Posts: 3
Joined: Tue Apr 11, 2023 6:57 am

Re: Stopping server inside handler using httpd_stop(*server);

Postby newcomer777 » Tue Apr 11, 2023 7:15 am

It is pity that nobody replied. I am incountered httpd_stop() issue also, using the same example. I simply added the button Close to the web page generated by ESP32, added correspondent handler etc. To be sure I do operate with the same server descriptor I simply made it global. All works until server stopping handler is inwoked. Then the control never returns from httpd_stop(server) function, keep hanging the control somewhere inside of this function. The last message I see is "Closing the server".

Code: Select all

/* Handler for "/close" */
esp_err_t close_handler(httpd_req_t *req)
{
    httpd_resp_set_type(req, HTTPD_TYPE_TEXT);
    httpd_resp_send(req, "Closing server...", strlen("Closing server..."));
    /* Close server */
    ESP_LOGI(TAG, "Closing the server");
    //httpd_handle_t server = (httpd_handle_t) req->user_ctx;
    esp_err_t ret = httpd_stop(server);
    if (server) {
        ESP_LOGE(TAG, "Server handle is not null after stopping server!");
    } else {
        ESP_LOGI(TAG, "Server handle is null after stopping server");
    }
    if (ret == ESP_OK) {
        ESP_LOGI(TAG, "Server stopped successfully");
    } else {
        ESP_LOGE(TAG, "Error stopping the server: %s", esp_err_to_name(ret));
            }
    return ret;
}
A bit confused what to do with this. Any ideas are highly appreciated!

all50orbust
Posts: 2
Joined: Tue Apr 18, 2023 4:52 pm

Re: Stopping server inside handler using httpd_stop(*server);

Postby all50orbust » Tue Apr 18, 2023 5:18 pm

Hello zazas321,

Edit: I wanted to correct my response now that I've cleared up this similar issue for myself.

I had to change my stop_webserver definition from

Code: Select all

static void stop_webserver(httpd_handle_t server)
to

Code: Select all

static void stop_webserver(httpd_handle_t *server)
Not sure why this function was the way it was initially, I don't recall where I copied that code from, may have been an example from IDF or online code somewhere.
Then using if(*server) and *server = NULL; produce the desired results. I can check the handle directly from another function as if (!manage_httpd), and this makes sure that the handle hasn't already been initialized.
Also when calling the stop_webserver function, I use stop_webserver(&manage_httpd) instead of *manage_httpd. (Similar to calling start_httpd function.)

For completeness, manage_httpd is globally defined as,
static httpd_handle_t manage_httpd = NULL;

Code: Select all

static void stop_webserver(httpd_handle_t *server)
{	
	if (*server)
	{
		httpd_stop(*server);
	}
	*server = NULL;
}
End Edit

I hope you found the solution already, but I just wanted to point out what I believe was causing the problem for anyone else who happens upon this post.

The stop_webserver(httpd_handle_t server) takes in httpd_handle_t server vs httpd_handle_t *server. However, when you call the function, you use *server,
stop_webserver(*server);
*server = NULL;
I'm not entirely sure this is the intended use.
Inside the start_webserver function the handle is emptied like this: (This is how I ended up here.)
* // Empty handle to http_server
* httpd_handle_t server = NULL;

Personally, pointers still tend to confuse me from time to time even after years of programming on and off. Something that has helped me, if you're using VScode, you can hover your mouse over the function name, and it'll show you what argument types it accepts. It comes in handy when needing to typecast for log prints and other string-like (char *) operations.

zazas321 wrote:
Mon Mar 21, 2022 4:25 pm
Am I not accessing the httpd_handle_t variable correctly through the handle? Is below not corrrect?:
httpd_handle_t* server = (httpd_handle_t*) arg; ?
Base on some sample code for AP mode, httpd_handle_t* server = (httpd_handle_t*) arg; is correct.
Last edited by all50orbust on Wed Apr 19, 2023 6:21 pm, edited 1 time in total.

all50orbust
Posts: 2
Joined: Tue Apr 18, 2023 4:52 pm

Re: Stopping server inside handler using httpd_stop(*server);

Postby all50orbust » Tue Apr 18, 2023 5:26 pm

newcomer777 wrote:
Tue Apr 11, 2023 7:15 am
It is pity that nobody replied. I am incountered httpd_stop() issue also, using the same example. I simply added the button Close to the web page generated by ESP32, added correspondent handler etc. To be sure I do operate with the same server descriptor I simply made it global. All works until server stopping handler is inwoked. Then the control never returns from httpd_stop(server) function, keep hanging the control somewhere inside of this function. The last message I see is "Closing the server".

Code: Select all

/* Handler for "/close" */
esp_err_t close_handler(httpd_req_t *req)
{
    httpd_resp_set_type(req, HTTPD_TYPE_TEXT);
    httpd_resp_send(req, "Closing server...", strlen("Closing server..."));
    /* Close server */
    ESP_LOGI(TAG, "Closing the server");
    //httpd_handle_t server = (httpd_handle_t) req->user_ctx;
    esp_err_t ret = httpd_stop(server);
    if (server) {
        ESP_LOGE(TAG, "Server handle is not null after stopping server!");
    } else {
        ESP_LOGI(TAG, "Server handle is null after stopping server");
    }
    if (ret == ESP_OK) {
        ESP_LOGI(TAG, "Server stopped successfully");
    } else {
        ESP_LOGE(TAG, "Error stopping the server: %s", esp_err_to_name(ret));
            }
    return ret;
}
A bit confused what to do with this. Any ideas are highly appreciated!
I don't think there are enough details for me to provide accurate feedback, however, my best guess is that it's possible to call http_stop(server) even if server is not already initialized. Perhaps using the following may help avoid a crash.

Code: Select all

if (server) {
    esp_err_t ret = httpd_stop(server);
}
Of course this has some issues, as ret wont be defined, and you have some checks for that later in the function. So declaring ret first may be best.

Code: Select all

esp_err_t ret = ESP_FAIL; //May want to change to ESP_OK if you wish to avoid a failure if server is not yet initialized. 
if (server) {
    ret = httpd_stop(server);
}
Edit: Wanted to point out that the use of the global server with httpd_stop may be the problem. I recommend adding the stop_webserver function that I've added in my previous comment edit. You could also potentially try httpd_stop(&server); directly, as httpd_stop(*server); did not compile for me.

Who is online

Users browsing this forum: Google [Bot] and 119 guests