The lwIP httpd has no way to stop: its listening PCB and callbacks
outlive the caller. With the shared lwIP runtime the server would keep
accepting connections while any other client polls the network.

Add httpd_deinit(), which aborts the open connections and closes the
listening PCB. Open connections are only tracked with
LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED.

With SO_REUSE, set SOF_REUSEADDR on the listening PCB: connections
closed by the server stay in TIME-WAIT and would make the next bind
fail.

Signed-off-by: Vitaliy Sochnev <[email protected]>
---
 lib/lwip/lwip/src/apps/http/httpd.c         | 51 ++++++++++++++++++++-
 lib/lwip/lwip/src/include/lwip/apps/httpd.h |  1 +
 2 files changed, 50 insertions(+), 2 deletions(-)

diff --git a/lib/lwip/lwip/src/apps/http/httpd.c 
b/lib/lwip/lwip/src/apps/http/httpd.c
index 9477e855e..745477272 100644
--- a/lib/lwip/lwip/src/apps/http/httpd.c
+++ b/lib/lwip/lwip/src/apps/http/httpd.c
@@ -98,6 +98,7 @@
 
 #include "lwip/altcp.h"
 #include "lwip/altcp_tcp.h"
+#include "lwip/tcp.h"
 #if HTTPD_ENABLE_HTTPS
 #include "lwip/altcp_tls.h"
 #endif
@@ -345,6 +346,9 @@ static char 
*http_cgi_params[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Params extracted
 static char *http_cgi_param_vals[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Values for 
each extracted param */
 #endif /* LWIP_HTTPD_CGI */
 
+/** listening PCB set up by httpd_init(), closed by httpd_deinit() */
+static struct altcp_pcb *httpd_listen_pcb;
+
 #if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
 /** global list of active HTTP connections, use to kill the oldest when
     running out of memory */
@@ -2658,7 +2662,7 @@ http_accept(void *arg, struct altcp_pcb *pcb, err_t err)
   return ERR_OK;
 }
 
-static void
+static struct altcp_pcb *
 httpd_init_pcb(struct altcp_pcb *pcb, u16_t port)
 {
   err_t err;
@@ -2673,6 +2677,7 @@ httpd_init_pcb(struct altcp_pcb *pcb, u16_t port)
     LWIP_ASSERT("httpd_init: tcp_listen failed", pcb != NULL);
     altcp_accept(pcb, http_accept);
   }
+  return pcb;
 }
 
 /**
@@ -2694,9 +2699,51 @@ httpd_init(void)
 
   /* LWIP_ASSERT_CORE_LOCKED(); is checked by tcp_new() */
 
+#if SO_REUSE
+  {
+    /* rebind while connections of a previous run are in TIME-WAIT */
+    struct tcp_pcb *tpcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
+    if (tpcb != NULL) {
+      ip_set_option(tpcb, SOF_REUSEADDR);
+#if LWIP_ALTCP
+      pcb = altcp_tcp_wrap(tpcb);
+      if (pcb == NULL) {
+        tcp_close(tpcb);
+      }
+#else /* LWIP_ALTCP */
+      pcb = tpcb;
+#endif /* LWIP_ALTCP */
+    } else {
+      pcb = NULL;
+    }
+  }
+#else /* SO_REUSE */
   pcb = altcp_tcp_new_ip_type(IPADDR_TYPE_ANY);
+#endif /* SO_REUSE */
   LWIP_ASSERT("httpd_init: tcp_new failed", pcb != NULL);
-  httpd_init_pcb(pcb, HTTPD_SERVER_PORT);
+  httpd_listen_pcb = httpd_init_pcb(pcb, HTTPD_SERVER_PORT);
+}
+
+/**
+ * @ingroup httpd
+ * Stop the httpd: close the listening PCB set up by httpd_init() and abort
+ * all open connections, so that no httpd callback runs afterwards.
+ * Open connections are only known with 
LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED.
+ */
+void
+httpd_deinit(void)
+{
+#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
+  /* this also unlinks the http_state from the list */
+  while (http_connections != NULL) {
+    http_close_or_abort_conn(http_connections->pcb, http_connections, 1);
+  }
+#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
+  if (httpd_listen_pcb != NULL) {
+    altcp_accept(httpd_listen_pcb, NULL);
+    altcp_close(httpd_listen_pcb);
+    httpd_listen_pcb = NULL;
+  }
 }
 
 #if HTTPD_ENABLE_HTTPS
diff --git a/lib/lwip/lwip/src/include/lwip/apps/httpd.h 
b/lib/lwip/lwip/src/include/lwip/apps/httpd.h
index 1ecdd743c..e6ddd023d 100644
--- a/lib/lwip/lwip/src/include/lwip/apps/httpd.h
+++ b/lib/lwip/lwip/src/include/lwip/apps/httpd.h
@@ -243,6 +243,7 @@ void httpd_post_data_recved(void *connection, u16_t 
recved_len);
 #endif /* LWIP_HTTPD_SUPPORT_POST */
 
 void httpd_init(void);
+void httpd_deinit(void);
 
 #if HTTPD_ENABLE_HTTPS
 struct altcp_tls_config;
-- 
2.55.0

Reply via email to