Page 1 of 1

HTTP Server stop function can cause endless loop.

Posted: Wed Oct 11, 2023 5:50 am
by NotMyRealName
I'm using IDF4.4.5 on ESP32 and I've come across what I think is a bug or maybe an oversight in the way the HTTP server is implemented.

I have a commercial project on a few different hardware platforms and I run multiple network interfaces simultaneously (e.g. STA, AP, ETH, PPP), and they all start-up and shut down independently (without reboot) in response to user actions etc.

I might not be understanding this perfectly, but as far as I can see:

The HTTP server code uses a select statement with an infinite timeout to block on a loopback socket for the control messages that are used to signal a shutdown to the httpd thread. The shutdown function blocks the calling task until the thread sets a shutdown flag.

However, If the interface that is acting as loopback (e.g STA) goes down the loopback socket seems to die and the signal never reaches the task and it is impossible to restart the HTTP server. LWIP says that if there is a valid interface this will act as the loopback rather than creating a loopback only dummy interface.

(Haven't checked if this is the docs for the exact same version of LWIP!).
https://www.nongnu.org/lwip/2_0_x/group ... 91e7ea58c5

So what seems to be happening is this:
1. Device boots, STA enabled.
2. Web server starts, and uses STA netif for loopback.
3. STA disconnects (on purpose, or even by accident). NETIF Still exists, but link is down.
4. I try to call httpd_stop()
5. Select statement never unblocks because loopback socket is dead.
6. The calling task is stuck in a loop pending (indirectly) on the select.

I see the master branch has something different here in the shutdown function for httpd, and it will return an error code in this situation if it can't signal the httpd thread, but I still don't think that helps unless you can restart (And reconnect) the wifi. It stops the caller blocking, but doesn't help the http server thread.

Does anyone have any ideas what I should do here? I can't guarantee I can get a clean shutdown of wifi in situations like an abrupt disconnection. I'd get stuck in a loop until the watchdog catches it with this code.

Any help would be appreciated!

Re: HTTP Server stop function can cause endless loop.

Posted: Mon Oct 16, 2023 6:48 am
by hmalpani
Hello @NotMyRealName

I tried simple HTTP server example present in ESP-IDF. I tried to turn off the AP. This made the server to shut down. When I restarted the AP, the server went up again. I was able to do HTTP request using cURL in both the cases. Can you please try the example and let me know if you are facing the issue with example as well. Also can you please share the application to reproduce this issue?

Re: HTTP Server stop function can cause endless loop.

Posted: Mon Oct 16, 2023 9:34 pm
by NotMyRealName
Thanks for having a look hmalpani.

You are right, the example seems to work. I'll keep digging and report back. I'm still pretty sure it is getting stuck in this function, but must be some other mechanism.

Thanks.

Re: HTTP Server stop function can cause endless loop.

Posted: Tue Oct 17, 2023 2:47 am
by NotMyRealName
I think I have found my issue.

Amongst the numerous other things I have going on... I have manually overridden the IP4 route src hook functions. I do this to be able to force a default interface based on internet connectivity rather than solely on link up status. E.g. It will prefer WiFi if it has WiFi and Cell, but fallback to cell if the WiFi has no interent (even if connected). I based my implementation on this discussion if anyone is interested (https://github.com/espressif/esp-idf/issues/3581)

The fix (which will only apply to my custom code) was to check for loopback addresses in the routing function.

Code: Select all

struct netif * ip4_route_src_hook_local(const ip4_addr_t *src,const ip4_addr_t *dest)
{
    // Return NULL (default route) if source/destination is a loopback address. This is needed for http server to function correctly. 
    if((src != NULL) && ip4_addr_isloopback(src)){
        return NULL;
    }
    if((dest != NULL) && ip4_addr_isloopback(dest)){
        return NULL;
    }
    
    
    // Other routing stuff goes here...
        
}
Thanks again hmalpani for steering me the right way!