diff --git a/app/modules/enduser_setup.c b/app/modules/enduser_setup.c
index 7ba50b5d..afe6441c 100644
--- a/app/modules/enduser_setup.c
+++ b/app/modules/enduser_setup.c
@@ -43,6 +43,8 @@
#include "ctype.h"
#include "user_interface.h"
#include "espconn.h"
+#include "lwip/tcp.h"
+#include "lwip/pbuf.h"
#include "flash_fs.h"
#include "task/task.h"
@@ -227,17 +229,17 @@ static const char http_header_500[] = "HTTP/1.1 500 Internal Error\r\n\r\n";
static const char http_html_backup[] =
"
WiFi Login";
+
typedef struct scan_listener
{
+ struct tcp_pcb *conn;
struct scan_listener *next;
- int remote_port;
- uint8 remote_ip[4];
} scan_listener_t;
typedef struct
{
struct espconn *espconn_dns_udp;
- struct espconn *espconn_http_tcp;
+ struct tcp_pcb *http_pcb;
char *http_payload_data;
uint32_t http_payload_len;
os_timer_t check_station_timer;
@@ -627,34 +629,67 @@ static int enduser_setup_http_handle_credentials(char *data, unsigned short data
*
* @return - return 0 iff html was served successfully
*/
-static int enduser_setup_http_serve_header(struct espconn *http_client, const char *header, uint32_t header_len)
+static int enduser_setup_http_serve_header(struct tcp_pcb *http_client, const char *header, uint32_t header_len)
{
ENDUSER_SETUP_DEBUG("enduser_setup_http_serve_header");
- int8_t err = espconn_send(http_client, (char *)header, header_len);
- if (err == ESPCONN_MEM)
+ err_t err = tcp_write (http_client, header, header_len, TCP_WRITE_FLAG_COPY);
+ if (err != ERR_OK)
{
- ENDUSER_SETUP_ERROR("http_serve_header failed. espconn_send out of memory", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_NONFATAL);
- }
- else if (err == ESPCONN_ARG)
- {
- ENDUSER_SETUP_ERROR("http_serve_header failed. espconn_send can't find network transmission", ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND, ENDUSER_SETUP_ERR_NONFATAL);
- }
- else if (err != 0)
- {
- ENDUSER_SETUP_ERROR("http_serve_header failed. espconn_send failed", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL);
+ tcp_close (http_client);
+ ENDUSER_SETUP_ERROR("http_serve_header failed on tcp_write", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL);
}
return 0;
}
+static err_t streamout_sent (void *arg, struct tcp_pcb *pcb, u16_t len)
+{
+ (void)len;
+
+ unsigned offs = (unsigned)arg;
+
+ if (!state || !state->http_payload_data)
+ {
+ tcp_abort (pcb);
+ return ERR_ABRT;
+ }
+
+ unsigned wanted_len = state->http_payload_len - offs;
+ unsigned buf_free = tcp_sndbuf (pcb);
+ if (buf_free < wanted_len)
+ wanted_len = buf_free;
+
+ /* no-copy write */
+ err_t err = tcp_write (pcb, state->http_payload_data + offs, wanted_len, 0);
+ if (err != ERR_OK)
+ {
+ ENDUSER_SETUP_DEBUG("streaming out html failed");
+ tcp_abort (pcb);
+ return ERR_ABRT;
+ }
+
+ offs += wanted_len;
+
+ if (offs >= state->http_payload_len)
+ {
+ tcp_sent (pcb, 0);
+ tcp_close (pcb);
+ }
+ else
+ tcp_arg (pcb, (void *)offs);
+
+ return ERR_OK;
+}
+
+
/**
* Serve HTML
*
* @return - return 0 iff html was served successfully
*/
-static int enduser_setup_http_serve_html(struct espconn *http_client)
+static int enduser_setup_http_serve_html(struct tcp_pcb *http_client)
{
ENDUSER_SETUP_DEBUG("enduser_setup_http_serve_html");
@@ -663,25 +698,22 @@ static int enduser_setup_http_serve_html(struct espconn *http_client)
enduser_setup_http_load_payload();
}
- int8_t err = espconn_send(http_client, state->http_payload_data, state->http_payload_len);
- if (err == ESPCONN_MEM)
+ unsigned chunklen = tcp_sndbuf (http_client);
+ tcp_arg (http_client, (void *)chunklen);
+ tcp_recv (http_client, 0); /* avoid confusion about the tcp_arg */
+ tcp_sent (http_client, streamout_sent);
+ /* Begin the no-copy stream-out here */
+ err_t err = tcp_write (http_client, state->http_payload_data, chunklen, 0);
+ if (err != 0)
{
- ENDUSER_SETUP_ERROR("http_serve_html failed. espconn_send out of memory", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_NONFATAL);
- }
- else if (err == ESPCONN_ARG)
- {
- ENDUSER_SETUP_ERROR("http_serve_html failed. espconn_send can't find network transmission", ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND, ENDUSER_SETUP_ERR_NONFATAL);
- }
- else if (err != 0)
- {
- ENDUSER_SETUP_ERROR("http_serve_html failed. espconn_send failed", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL);
+ ENDUSER_SETUP_ERROR("http_serve_html failed. tcp_write failed", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL);
}
return 0;
}
-static void serve_status(struct espconn *conn)
+static void serve_status(struct tcp_pcb *conn)
{
ENDUSER_SETUP_DEBUG("enduser_setup_serve_status");
@@ -756,22 +788,6 @@ static void serve_status(struct espconn *conn)
}
-/**
- * Disconnect HTTP client
- *
- * End TCP connection and free up resources.
- */
-static void enduser_setup_http_disconnect(struct espconn *espconn)
-{
- ENDUSER_SETUP_DEBUG("enduser_setup_http_disconnect");
- /*TODO: Implement early TCP disconnections.
- * The espconn * here is frequently a temporary lwIP internal struct (when it is passed in to an API callback),
- * and is not valid after the callback returns. The SDK doc isn't sufficiently explicit about
- * (but see "2C" 1.5.1, p193 for the hint). To do the disconnects safely, you'll need to stash away
- * the remote_ip and remote_port like what is done in the scan_listeners.
- */
-}
-
/* --- WiFi AP scanning support -------------------------------------------- */
@@ -793,25 +809,15 @@ static void free_scan_listeners (void)
}
-static inline bool same_remote (uint8 addr1[4], int port1, uint8 addr2[4], int port2)
+static void remove_scan_listener (scan_listener_t *l)
{
- return (port1 == port2) && (c_memcmp (addr1, addr2, 4) == 0);
-}
-
-
-static void on_disconnect (void *arg)
-{
- struct espconn *conn = arg;
- if (conn->type == ESPCONN_TCP && state && state->scan_listeners)
+ if (state)
{
scan_listener_t **sl = &state->scan_listeners;
while (*sl)
{
/* Remove any and all references to the closed conn from the scan list */
- scan_listener_t *l = *sl;
- if (same_remote (
- l->remote_ip, l->remote_port,
- conn->proto.tcp->remote_ip, conn->proto.tcp->remote_port))
+ if (*sl == l)
{
*sl = l->next;
c_free (l);
@@ -844,24 +850,17 @@ static void notify_scan_listeners (const char *payload, size_t sz)
{
return;
}
- if (!state->espconn_http_tcp)
- {
- goto cleanup;
- }
- struct espconn *conn = state->espconn_http_tcp;
for (scan_listener_t *l = state->scan_listeners; l; l = l->next)
{
- c_memcpy (conn->proto.tcp->remote_ip, l->remote_ip, 4);
- conn->proto.tcp->remote_port = l->remote_port;
- if (espconn_send(conn, (char *)payload, sz) != ESPCONN_OK)
+ if (tcp_write (l->conn, payload, sz, TCP_WRITE_FLAG_COPY) != ERR_OK)
{
ENDUSER_SETUP_DEBUG("failed to send wifi list");
}
- enduser_setup_http_disconnect(conn);
+ tcp_close (l->conn);
+ l->conn = 0;
}
-cleanup:
free_scan_listeners ();
}
@@ -941,23 +940,48 @@ serve_500:
/* ---- end WiFi AP scan support ------------------------------------------- */
-static void enduser_setup_http_recvcb(void *arg, char *data, unsigned short data_len)
+static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, struct pbuf *p, err_t err)
{
ENDUSER_SETUP_DEBUG("enduser_setup_http_recvcb");
- if (!state)
+ if (!state || err != ERR_OK)
{
- ENDUSER_SETUP_DEBUG("ignoring received data while stopped");
- return;
+ if (!state)
+ ENDUSER_SETUP_DEBUG("ignoring received data while stopped");
+ tcp_abort (http_client);
+ return ERR_ABRT;
}
- struct espconn *http_client = (struct espconn *) arg;
+ if (!p) /* remote side closed, close our end too */
+ {
+ ENDUSER_SETUP_DEBUG("connection closed");
+ scan_listener_t *l = arg; /* if it's waiting for scan, we have a ptr here */
+ if (l)
+ remove_scan_listener (l);
+
+ tcp_close (http_client);
+ return ERR_OK;
+ }
+
+ char *data = os_zalloc (p->tot_len + 1);
+ if (!data)
+ return ERR_MEM;
+
+ unsigned data_len = pbuf_copy_partial (p, data, p->tot_len, 0);
+ tcp_recved (http_client, p->tot_len);
+ pbuf_free (p);
+
if (c_strncmp(data, "GET ", 4) == 0)
{
if (c_strncmp(data + 4, "/ ", 2) == 0)
{
if (enduser_setup_http_serve_html(http_client) != 0)
{
- ENDUSER_SETUP_ERROR_VOID("http_recvcb failed. Unable to send HTML.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL);
+ ENDUSER_SETUP_ERROR("http_recvcb failed. Unable to send HTML.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL);
+ }
+ else
+ {
+ os_free (data);
+ return ERR_OK; /* streaming now in progress */
}
}
else if (c_strncmp(data + 4, "/aplist ", 8) == 0)
@@ -965,14 +989,14 @@ static void enduser_setup_http_recvcb(void *arg, char *data, unsigned short data
scan_listener_t *l = os_malloc (sizeof (scan_listener_t));
if (!l)
{
- ENDUSER_SETUP_ERROR_VOID("out of memory", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_NONFATAL);
+ ENDUSER_SETUP_ERROR("out of memory", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_NONFATAL);
}
- c_memcpy(l->remote_ip, http_client->proto.tcp->remote_ip, 4);
- l->remote_port = http_client->proto.tcp->remote_port;
-
bool already = (state->scan_listeners != NULL);
+ tcp_arg (http_client, l);
+ /* TODO: check if also need a tcp_err() cb, or if recv() is enough */
+ l->conn = http_client;
l->next = state->scan_listeners;
state->scan_listeners = l;
@@ -981,10 +1005,13 @@ static void enduser_setup_http_recvcb(void *arg, char *data, unsigned short data
if (!wifi_station_scan(NULL, on_scan_done))
{
enduser_setup_http_serve_header(http_client, http_header_500, LITLEN(http_header_500));
+ tcp_close (l->conn);
+ l->conn = 0;
free_scan_listeners();
}
}
- return;
+ os_free (data);
+ return ERR_OK;
}
else if (c_strncmp(data + 4, "/status ", 8) == 0)
{
@@ -1001,7 +1028,7 @@ static void enduser_setup_http_recvcb(void *arg, char *data, unsigned short data
enduser_setup_http_serve_header(http_client, http_header_401, LITLEN(http_header_401));
break;
default:
- ENDUSER_SETUP_ERROR_VOID("http_recvcb failed. Failed to handle wifi credentials.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL);
+ ENDUSER_SETUP_ERROR("http_recvcb failed. Failed to handle wifi credentials.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL);
break;
}
}
@@ -1022,79 +1049,67 @@ static void enduser_setup_http_recvcb(void *arg, char *data, unsigned short data
enduser_setup_http_serve_header(http_client, http_header_401, LITLEN(http_header_401));
}
- enduser_setup_http_disconnect (http_client);
+ os_free (data);
+ #if 0
+ /* being nice apparently costs us a lot of pcbs in time_wait state :( */
+ tcp_close (http_client);
+ return ERR_OK;
+ #else
+ tcp_abort (http_client);
+ return ERR_ABRT;
+ #endif
}
-static void enduser_setup_http_connectcb(void *arg)
+static err_t enduser_setup_http_connectcb(void *arg, struct tcp_pcb *pcb, err_t err)
{
ENDUSER_SETUP_DEBUG("enduser_setup_http_connectcb");
- struct espconn *callback_espconn = (struct espconn *) arg;
-
- int8_t err = 0;
- err |= espconn_regist_recvcb(callback_espconn, enduser_setup_http_recvcb);
- err |= espconn_regist_disconcb(callback_espconn, on_disconnect);
-
- if (err != 0)
+ if (!state)
{
- ENDUSER_SETUP_ERROR_VOID("http_connectcb failed. Callback registration failed.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL);
+ ENDUSER_SETUP_DEBUG("connect callback but no state?!");
+ tcp_abort (pcb);
+ return ERR_ABRT;
}
+
+ tcp_accepted (state->http_pcb);
+ tcp_recv (pcb, enduser_setup_http_recvcb);
+ return ERR_OK;
}
static int enduser_setup_http_start(void)
{
ENDUSER_SETUP_DEBUG("enduser_setup_http_start");
- state->espconn_http_tcp = (struct espconn *) c_malloc(sizeof(struct espconn));
- if (state->espconn_http_tcp == NULL)
+ struct tcp_pcb *pcb = tcp_new ();
+ if (pcb == NULL)
{
- ENDUSER_SETUP_ERROR("http_start failed. Memory allocation failed (espconn_http_tcp == NULL).", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_FATAL);
- }
- esp_tcp *esp_tcp_data = (esp_tcp *) c_malloc(sizeof(esp_tcp));
- if (esp_tcp_data == NULL)
- {
- ENDUSER_SETUP_ERROR("http_start failed. Memory allocation failed (esp_udp == NULL).", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_FATAL);
+ ENDUSER_SETUP_ERROR("http_start failed. Memory allocation failed (http_pcb == NULL).", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_FATAL);
}
- c_memset(state->espconn_http_tcp, 0, sizeof(struct espconn));
- c_memset(esp_tcp_data, 0, sizeof(esp_tcp));
- state->espconn_http_tcp->proto.tcp = esp_tcp_data;
- state->espconn_http_tcp->type = ESPCONN_TCP;
- state->espconn_http_tcp->state = ESPCONN_NONE;
- esp_tcp_data->local_port = 80;
-
- int8_t err;
- err = espconn_regist_connectcb(state->espconn_http_tcp, enduser_setup_http_connectcb);
- if (err != 0)
+ if (tcp_bind (pcb, IP_ADDR_ANY, 80) != ERR_OK)
{
- ENDUSER_SETUP_ERROR("http_start failed. Couldn't add receive callback.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL);
+ ENDUSER_SETUP_ERROR("http_start bind failed", ENDUSER_SETUP_ERR_SOCKET_ALREADY_OPEN, ENDUSER_SETUP_ERR_FATAL);
}
- err = espconn_accept(state->espconn_http_tcp);
- if (err == ESPCONN_ISCONN)
+ state->http_pcb = tcp_listen (pcb);
+ if (!state->http_pcb)
{
- ENDUSER_SETUP_ERROR("http_start failed. Couldn't create connection, already listening for that connection.", ENDUSER_SETUP_ERR_SOCKET_ALREADY_OPEN, ENDUSER_SETUP_ERR_FATAL);
- }
- else if (err == ESPCONN_MEM)
- {
- ENDUSER_SETUP_ERROR("http_start failed. Couldn't create connection, out of memory.", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_FATAL);
- }
- else if (err == ESPCONN_ARG)
- {
- ENDUSER_SETUP_ERROR("http_start failed. Can't find connection from espconn argument", ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND, ENDUSER_SETUP_ERR_FATAL);
- }
- else if (err != 0)
- {
- ENDUSER_SETUP_ERROR("http_start failed. Unknown error", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL);
+ tcp_abort(pcb); /* original wasn't freed for us */
+ ENDUSER_SETUP_ERROR("http_start listen failed", ENDUSER_SETUP_ERR_SOCKET_ALREADY_OPEN, ENDUSER_SETUP_ERR_FATAL);
}
+ tcp_accept (state->http_pcb, enduser_setup_http_connectcb);
+
+ /* TODO: check lwip tcp timeouts */
+#if 0
err = espconn_regist_time(state->espconn_http_tcp, 2, 0);
if (err == ESPCONN_ARG)
{
ENDUSER_SETUP_ERROR("http_start failed. Unable to set TCP timeout.", ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND, ENDUSER_SETUP_ERR_NONFATAL | ENDUSER_SETUP_ERR_NO_RETURN);
}
+#endif
- err = enduser_setup_http_load_payload();
+ int err = enduser_setup_http_load_payload();
if (err == 1)
{
ENDUSER_SETUP_DEBUG("enduser_setup_http_start info. Loaded backup HTML.");
@@ -1112,9 +1127,10 @@ static void enduser_setup_http_stop(void)
{
ENDUSER_SETUP_DEBUG("enduser_setup_http_stop");
- if (state != NULL && state->espconn_http_tcp != NULL)
+ if (state && state->http_pcb)
{
- espconn_delete(state->espconn_http_tcp);
+ tcp_close (state->http_pcb); /* cannot fail for listening sockets */
+ state->http_pcb = 0;
}
}
@@ -1246,14 +1262,6 @@ static void enduser_setup_free(void)
c_free(state->espconn_dns_udp);
}
- if (state->espconn_http_tcp != NULL)
- {
- if (state->espconn_http_tcp->proto.tcp != NULL)
- {
- c_free(state->espconn_http_tcp->proto.tcp);
- }
- c_free(state->espconn_http_tcp);
- }
c_free(state->http_payload_data);
free_scan_listeners ();