Skip to content

Commit

Permalink
fix crash due to weird espconn switcharoo
Browse files Browse the repository at this point in the history
  • Loading branch information
tve committed Jun 20, 2015
1 parent 1dfb379 commit 228a3ff
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 42 deletions.
109 changes: 67 additions & 42 deletions httpd/httpd.c
Expand Up @@ -82,27 +82,52 @@ const char ICACHE_FLASH_ATTR *httpdGetMimetype(char *url) {
return mimeTypes[i].mimetype;
}

// debug string to identify connection (ip address & port)
// a static string works because callbacks don't get interrupted...
static char connStr[24];

static void debugConn(void *arg, char *what) {
struct espconn *espconn = arg;
esp_tcp *tcp = espconn->proto.tcp;
os_sprintf(connStr, "%d.%d.%d.%d:%d",
tcp->remote_ip[0], tcp->remote_ip[1], tcp->remote_ip[2], tcp->remote_ip[3],
tcp->remote_port);
//os_printf("%s %s\n", connStr, what);
}

//Looks up the connData info for a specific esp connection
static HttpdConnData ICACHE_FLASH_ATTR *httpdFindConnData(void *arg) {
int i;
for (i=0; i<MAX_CONN; i++) {
if (connData[i].conn==(struct espconn *)arg) return &connData[i];
struct espconn *espconn = arg;
for (int i=0; i<MAX_CONN; i++) {
if (connData[i].remote_port == espconn->proto.tcp->remote_port &&
os_memcmp(connData[i].remote_ip, espconn->proto.tcp->remote_ip, 4) == 0)
{
#if 0
os_printf("FindConn: 0x%p->0x%p", arg, &connData[i]);
if (arg == connData[i].conn) os_printf("\n");
else os_printf(" *** was 0x%p\n", connData[i].conn);
#endif
if (arg != connData[i].conn) connData[i].conn = arg; // yes, this happens!?
return &connData[i];
}
}
//Shouldn't happen.
os_printf("FindConnData: Huh? Couldn't find connection for %p\n", arg);
os_printf("%s *** Unknown connection 0x%p\n", connStr, arg);
return NULL;
}

//Retires a connection for re-use
static void ICACHE_FLASH_ATTR httpdRetireConn(HttpdConnData *conn) {
uint32 dt = conn->startTime;
if (dt > 0) dt = (system_get_time() - dt)/1000;
os_printf("Closed %p, took %ums, heap=%ld\n", conn->conn, dt,
os_printf("%s Closed, took %ums, heap=%ld\n", connStr, dt,
(unsigned long)system_get_free_heap_size());
if (conn->post->buff!=NULL) os_free(conn->post->buff);
conn->post->buff=NULL;
conn->cgi=NULL;
conn->conn=NULL;
conn->remote_port = 0;
conn->remote_ip[0] = 0;
}

//Stupid li'l helper function that returns the value of a hex char.
Expand Down Expand Up @@ -243,7 +268,8 @@ int ICACHE_FLASH_ATTR cgiRedirect(HttpdConnData *connData) {
int ICACHE_FLASH_ATTR httpdSend(HttpdConnData *conn, const char *data, int len) {
if (len<0) len=strlen(data);
if (conn->priv->sendBuffLen+len>MAX_SENDBUFF_LEN) {
os_printf("ERROR! httpdSend full (%d of %d)\n", conn->priv->sendBuffLen, MAX_SENDBUFF_LEN);
os_printf("%s ERROR! httpdSend full (%d of %d)\n",
connStr, conn->priv->sendBuffLen, MAX_SENDBUFF_LEN);
return 0;
}
os_memcpy(conn->priv->sendBuff+conn->priv->sendBuffLen, data, len);
Expand All @@ -256,15 +282,15 @@ static void ICACHE_FLASH_ATTR xmitSendBuff(HttpdConnData *conn) {
if (conn->priv->sendBuffLen!=0) {
sint8 status = espconn_sent(conn->conn, (uint8_t*)conn->priv->sendBuff, conn->priv->sendBuffLen);
if (status != 0) {
os_printf("ERROR! espconn_sent returned %d\n", status);
os_printf("%s ERROR! espconn_sent returned %d\n", connStr, status);
}
conn->priv->sendBuffLen=0;
}
}

//Callback called when the data on a socket has been successfully
//sent.
//Callback called when the data on a socket has been successfully sent.
static void ICACHE_FLASH_ATTR httpdSentCb(void *arg) {
debugConn(arg, "httpdSentCb");
int r;
HttpdConnData *conn=httpdFindConnData(arg);
char sendBuff[MAX_SENDBUFF_LEN];
Expand All @@ -274,9 +300,9 @@ static void ICACHE_FLASH_ATTR httpdSentCb(void *arg) {
conn->priv->sendBuffLen=0;

if (conn->cgi==NULL) { //Marked for destruction?
//os_printf("Conn %p is done. Closing.\n", conn->conn);
//os_printf("Closing 0x%p/0x%p->0x%p\n", arg, conn->conn, conn);
espconn_disconnect(conn->conn);
httpdRetireConn(conn);
//httpdRetireConn(conn); // can't call this, we will get a diconnect callback!
return; //No need to call xmitSendBuff.
}

Expand All @@ -285,7 +311,7 @@ static void ICACHE_FLASH_ATTR httpdSentCb(void *arg) {
conn->cgi=NULL; //mark for destruction.
}
if (r==HTTPD_CGI_NOTFOUND || r==HTTPD_CGI_AUTHENTICATED) {
os_printf("ERROR! CGI fn returns code %d after sending data! Bad CGI!\n", r);
os_printf("%s ERROR! CGI fn returns code %d after sending data! Bad CGI!\n", connStr, r);
conn->cgi=NULL; //mark for destruction.
}
xmitSendBuff(conn);
Expand All @@ -301,7 +327,7 @@ static void ICACHE_FLASH_ATTR httpdProcessRequest(HttpdConnData *conn) {
int r;
int i=0;
if (conn->url==NULL) {
os_printf("WtF? url = NULL\n");
os_printf("%s WtF? url = NULL\n", connStr);
return; //Shouldn't happen
}
//See if we can find a CGI that's happy to handle the request.
Expand All @@ -326,7 +352,7 @@ static void ICACHE_FLASH_ATTR httpdProcessRequest(HttpdConnData *conn) {
if (builtInUrls[i].url==NULL) {
//Drat, we're at the end of the URL table. This usually shouldn't happen. Well, just
//generate a built-in 404 to handle this.
os_printf("%s not found. 404!\n", conn->url);
os_printf("%s %s not found. 404!\n", connStr, conn->url);
httpdSend(conn, httpNotFoundHeader, -1);
xmitSendBuff(conn);
conn->cgi=NULL; //mark for destruction
Expand Down Expand Up @@ -379,18 +405,14 @@ static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) {
if (e==NULL) return; //wtf?
*e=0; //terminate url part

os_printf("%s %s (%p) %d.%d.%d.%d:%d\n",
conn->requestType == HTTPD_METHOD_GET ? "GET" : "POST",
conn->url, conn->conn,
conn->conn->proto.tcp->remote_ip[0], conn->conn->proto.tcp->remote_ip[1],
conn->conn->proto.tcp->remote_ip[2], conn->conn->proto.tcp->remote_ip[3],
conn->conn->proto.tcp->remote_port);
os_printf("%s %s %s\n", connStr,
conn->requestType == HTTPD_METHOD_GET ? "GET" : "POST", conn->url);
//Parse out the URL part before the GET parameters.
conn->getArgs=(char*)os_strstr(conn->url, "?");
if (conn->getArgs!=0) {
*conn->getArgs=0;
conn->getArgs++;
os_printf("GET args = %s\n", conn->getArgs);
os_printf("%s GET args = %s\n", connStr, conn->getArgs);
} else {
conn->getArgs=NULL;
}
Expand All @@ -409,7 +431,7 @@ static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) {
} else {
conn->post->buffSize = conn->post->len;
}
os_printf("Mallocced buffer for %d + 1 bytes of post data.\n", conn->post->buffSize);
//os_printf("Mallocced buffer for %d + 1 bytes of post data.\n", conn->post->buffSize);
conn->post->buff=(char*)os_malloc(conn->post->buffSize + 1);
conn->post->buffLen=0;
} else if (os_strncmp(h, "Content-Type: ", 14)==0) {
Expand All @@ -420,7 +442,7 @@ static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) {
conn->post->multipartBoundary = b + 7; // move the pointer 2 chars before boundary then fill them with dashes
conn->post->multipartBoundary[0] = '-';
conn->post->multipartBoundary[1] = '-';
os_printf("boundary = %s\n", conn->post->multipartBoundary);
//os_printf("boundary = %s\n", conn->post->multipartBoundary);
}
}
}
Expand All @@ -429,6 +451,7 @@ static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) {

//Callback called when there's data available on a socket.
static void ICACHE_FLASH_ATTR httpdRecvCb(void *arg, char *data, unsigned short len) {
debugConn(arg, "httpdRecvCb");
int x;
char *p, *e;
char sendBuff[MAX_SENDBUFF_LEN];
Expand Down Expand Up @@ -484,45 +507,47 @@ static void ICACHE_FLASH_ATTR httpdRecvCb(void *arg, char *data, unsigned short
}

static void ICACHE_FLASH_ATTR httpdDisconCb(void *arg) {
//The esp sdk passes through wrong arg here, namely the one of the *listening* socket.
//That is why we can't use (HttpdConnData)arg->sock here.
//Just look at all the sockets and kill the slot if needed.
int i;
for (i=0; i<MAX_CONN; i++) {
if (connData[i].conn!=NULL) {
//Why the >=ESPCONN_CLOSE and not ==? Well, seems the stack sometimes de-allocates
//espconns under our noses, especially when connections are interrupted. The memory
//is then used for something else, and we can use that to capture *most* of the
//disconnect cases.
if (connData[i].conn->state==ESPCONN_NONE || connData[i].conn->state>=ESPCONN_CLOSE) {
if (connData[i].cgi!=NULL) connData[i].cgi(&connData[i]); //flush cgi data
httpdRetireConn(&connData[i]);
}
}
}
debugConn(arg, "httpdDisconCb");
HttpdConnData *conn = httpdFindConnData(arg);
if (conn == NULL) return;
if (conn->cgi != NULL) conn->cgi(conn); // free cgi data
httpdRetireConn(conn);
}

// Callback indicating a failure in the connection. "Recon" is probably intended in the sense
// of "you need to reconnect". Sigh...
static void ICACHE_FLASH_ATTR httpdReconCb(void *arg, sint8 err) {
os_printf("Connection %p died, err=%d\n", arg, err);
httpdDisconCb(arg); // no different from close...
debugConn(arg, "httpdReconCb");
HttpdConnData *conn = httpdFindConnData(arg);
os_printf("%s reset, err=%d\n", connStr, err);
if (conn == NULL) return;
conn->conn = NULL; // don't tr to send anything, the SDK crashes...
if (conn->cgi != NULL) conn->cgi(conn); // free cgi data
httpdRetireConn(conn);
}


static void ICACHE_FLASH_ATTR httpdConnectCb(void *arg) {
debugConn(arg, "httpdConnectCb");
struct espconn *conn=arg;
int i;
//Find empty conndata in pool
for (i=0; i<MAX_CONN; i++) if (connData[i].conn==NULL) break;
//os_printf("Con req, conn=%p, pool slot %d\n", conn, i);
if (i==MAX_CONN) {
os_printf("Aiee, conn pool overflow!\n");
os_printf("%s Aiee, conn pool overflow!\n", connStr);
espconn_disconnect(conn);
return;
}

int num = 0;
for (int j=0; j<MAX_CONN; j++) if (connData[j].conn != NULL) num++;
os_printf("%s Connect (%d open)\n", connStr, num+1);

connData[i].priv=&connPrivData[i];
connData[i].conn=conn;
connData[i].remote_port = conn->proto.tcp->remote_port;
os_memcpy(connData[i].remote_ip, conn->proto.tcp->remote_ip, 4);
connData[i].priv->headPos=0;
connData[i].post=&connPostData[i];
connData[i].post->buff=NULL;
Expand Down
3 changes: 3 additions & 0 deletions httpd/httpd.h
Expand Up @@ -21,6 +21,9 @@ typedef int (* cgiSendCallback)(HttpdConnData *connData);
//A struct describing a http connection. This gets passed to cgi functions.
struct HttpdConnData {
struct espconn *conn;
int remote_port;
uint8 remote_ip[4];

uint32 startTime;
char requestType;
char *url;
Expand Down
1 change: 1 addition & 0 deletions user/cgiflash.c
Expand Up @@ -92,6 +92,7 @@ int ICACHE_FLASH_ATTR cgiUploadFirmware(HttpdConnData *connData) {
int code = 400;

// check overall size
//os_printf("FW: %d (max %d)\n", connData->post->len, FIRMWARE_SIZE);
if (connData->post->len > FIRMWARE_SIZE) err = "Firmware image too large";

// check that data starts with an appropriate header
Expand Down
11 changes: 11 additions & 0 deletions user/user_main.c
Expand Up @@ -109,6 +109,10 @@ void user_rf_pre_init(void) {
// address of espfs binary blob
extern uint32_t _binary_espfs_img_start;

static char *rst_codes[] = {
"normal", "wdt reset", "exception", "soft wdt", "restart", "deep sleep", "???",
};

//Main routine. Initialize stdout, the I/O, filesystem and the webserver and we're done.
void user_init(void) {
// init gpio pins used to reset&reprogram attached microcontrollers
Expand Down Expand Up @@ -144,5 +148,12 @@ void user_init(void) {
os_timer_setfn(&prHeapTimer, prHeapTimerCb, NULL);
os_timer_arm(&prHeapTimer, 3000, 1);
#endif

struct rst_info *rst_info = system_get_rst_info();
os_printf("Reset cause: %d=%s\n", rst_info->reason, rst_codes[rst_info->reason]);
os_printf("exccause=%d epc1=0x%x epc2=0x%x epc3=0x%x excvaddr=0x%x depc=0x%x\n",
rst_info->exccause, rst_info->epc1, rst_info->epc2, rst_info->epc3,
rst_info->excvaddr, rst_info->depc);

os_printf("** esp-link ready\n");
}

0 comments on commit 228a3ff

Please sign in to comment.