|
|
@ -28,22 +28,6 @@ _Static_assert(HTTPD_MAX_CONNECTIONS < 256, "HTTPD_MAX_CONNECTIONS must be at mo |
|
|
|
static const HttpdBuiltInUrl *s_builtInUrls; |
|
|
|
static const HttpdBuiltInUrl *s_builtInUrls; |
|
|
|
static const char *s_serverName = HTTPD_SERVERNAME; |
|
|
|
static const char *s_serverName = HTTPD_SERVERNAME; |
|
|
|
|
|
|
|
|
|
|
|
struct HttpdQueuedHeader; |
|
|
|
|
|
|
|
typedef struct HttpdQueuedHeader { |
|
|
|
|
|
|
|
/// Pointer to the next queued header
|
|
|
|
|
|
|
|
struct HttpdQueuedHeader *next; |
|
|
|
|
|
|
|
/// Text of the header, including CRLF
|
|
|
|
|
|
|
|
char headerLine[]; |
|
|
|
|
|
|
|
} HttpdQueuedHeader; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct HttpSendBacklogItem HttpSendBacklogItem; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct HttpSendBacklogItem { |
|
|
|
|
|
|
|
size_t len; |
|
|
|
|
|
|
|
HttpSendBacklogItem *next; |
|
|
|
|
|
|
|
uint8_t data[]; |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Flags (1 byte)
|
|
|
|
//Flags (1 byte)
|
|
|
|
#define HFL_HTTP11 (1<<0) |
|
|
|
#define HFL_HTTP11 (1<<0) |
|
|
|
#define HFL_CHUNKED (1<<1) |
|
|
|
#define HFL_CHUNKED (1<<1) |
|
|
@ -52,19 +36,6 @@ struct HttpSendBacklogItem { |
|
|
|
#define HFL_NOCONNECTIONSTR (1<<4) |
|
|
|
#define HFL_NOCONNECTIONSTR (1<<4) |
|
|
|
#define HFL_NOCORS (1<<5) |
|
|
|
#define HFL_NOCORS (1<<5) |
|
|
|
|
|
|
|
|
|
|
|
//Private data for http connection
|
|
|
|
|
|
|
|
struct HttpdPriv { |
|
|
|
|
|
|
|
char head[HTTPD_MAX_HEAD_LEN]; |
|
|
|
|
|
|
|
char corsToken[HTTPD_MAX_CORS_TOKEN_LEN]; |
|
|
|
|
|
|
|
size_t headPos; |
|
|
|
|
|
|
|
uint8_t *sendBuff; |
|
|
|
|
|
|
|
size_t sendBuffLen; |
|
|
|
|
|
|
|
char *chunkHdr; |
|
|
|
|
|
|
|
HttpSendBacklogItem *sendBacklog; |
|
|
|
|
|
|
|
HttpdQueuedHeader *headersToSend; // Linked list of headers to send with the response. Will be freed with the request.
|
|
|
|
|
|
|
|
size_t sendBacklogSize; |
|
|
|
|
|
|
|
uint8_t flags; |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Connection pool
|
|
|
|
//Connection pool
|
|
|
@ -160,9 +131,14 @@ static void httpdRetireConn(HttpdConnData *hconn) |
|
|
|
httpdPlatFree(j); |
|
|
|
httpdPlatFree(j); |
|
|
|
} while (i != NULL); |
|
|
|
} while (i != NULL); |
|
|
|
} |
|
|
|
} |
|
|
|
if (hconn->post->buff != NULL) { httpdPlatFree(hconn->post->buff); } |
|
|
|
if (hconn->post.buff != NULL) { |
|
|
|
if (hconn->post != NULL) { httpdPlatFree(hconn->post); } |
|
|
|
httpdPlatFree(hconn->post.buff); |
|
|
|
if (hconn->priv != NULL) { httpdPlatFree(hconn->priv); } |
|
|
|
hconn->post.buff = NULL; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (hconn->priv != NULL) { |
|
|
|
|
|
|
|
httpdPlatFree(hconn->priv); |
|
|
|
|
|
|
|
hconn->priv = NULL; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Unlink from the connection list
|
|
|
|
// Unlink from the connection list
|
|
|
|
s_connData[hconn->slot] = NULL; |
|
|
|
s_connData[hconn->slot] = NULL; |
|
|
@ -486,12 +462,14 @@ static void httpdCgiIsDone(HttpdConnData *conn) |
|
|
|
httpdFlushSendBuffer(conn); |
|
|
|
httpdFlushSendBuffer(conn); |
|
|
|
//Note: Do not clean up sendBacklog, it may still contain data at this point.
|
|
|
|
//Note: Do not clean up sendBacklog, it may still contain data at this point.
|
|
|
|
conn->priv->headPos = 0; |
|
|
|
conn->priv->headPos = 0; |
|
|
|
conn->post->len = -1; |
|
|
|
conn->post.len = -1; |
|
|
|
conn->priv->flags = 0; |
|
|
|
conn->priv->flags = 0; |
|
|
|
if (conn->post->buff) { httpdPlatFree(conn->post->buff); } |
|
|
|
if (conn->post.buff) { |
|
|
|
conn->post->buff = NULL; |
|
|
|
httpdPlatFree(conn->post.buff); |
|
|
|
conn->post->buffLen = 0; |
|
|
|
conn->post.buff = NULL; |
|
|
|
conn->post->received = 0; |
|
|
|
} |
|
|
|
|
|
|
|
conn->post.buffLen = 0; |
|
|
|
|
|
|
|
conn->post.received = 0; |
|
|
|
conn->hostName = NULL; |
|
|
|
conn->hostName = NULL; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
//Cannot re-use this connection. Mark to get it killed after all data is sent.
|
|
|
|
//Cannot re-use this connection. Mark to get it killed after all data is sent.
|
|
|
@ -715,31 +693,31 @@ static void httpdParseHeader(char *h, HttpdConnData *conn) |
|
|
|
//Skip trailing spaces
|
|
|
|
//Skip trailing spaces
|
|
|
|
while (h[i] == ' ') { i++; } |
|
|
|
while (h[i] == ' ') { i++; } |
|
|
|
//Get POST data length
|
|
|
|
//Get POST data length
|
|
|
|
conn->post->len = (int) strtol(h + i, NULL, 10); |
|
|
|
conn->post.len = (int) strtol(h + i, NULL, 10); |
|
|
|
|
|
|
|
|
|
|
|
// Allocate the buffer
|
|
|
|
// Allocate the buffer
|
|
|
|
if (conn->post->len > HTTPD_MAX_POST_LEN) { |
|
|
|
if (conn->post.len > HTTPD_MAX_POST_LEN) { |
|
|
|
// we'll stream this in in chunks
|
|
|
|
// we'll stream this in in chunks
|
|
|
|
conn->post->buffSize = HTTPD_MAX_POST_LEN; |
|
|
|
conn->post.buffSize = HTTPD_MAX_POST_LEN; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
conn->post->buffSize = (size_t) conn->post->len; |
|
|
|
conn->post.buffSize = (size_t) conn->post.len; |
|
|
|
} |
|
|
|
} |
|
|
|
http_dbg("Mallocced buffer for %d + 1 bytes of post data.", (int) conn->post->buffSize); |
|
|
|
http_dbg("Mallocced buffer for %d + 1 bytes of post data.", (int) conn->post.buffSize); |
|
|
|
conn->post->buff = (char *) httpdPlatMalloc(conn->post->buffSize + 1); |
|
|
|
conn->post.buff = (char *) httpdPlatMalloc(conn->post.buffSize + 1); |
|
|
|
if (conn->post->buff == NULL) { |
|
|
|
if (conn->post.buff == NULL) { |
|
|
|
http_error("post buf alloc failed"); |
|
|
|
http_error("post buf alloc failed"); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
conn->post->buffLen = 0; |
|
|
|
conn->post.buffLen = 0; |
|
|
|
} else if (strstarts(h, "Content-Type:")) { |
|
|
|
} else if (strstarts(h, "Content-Type:")) { |
|
|
|
if (strstr(h, "multipart/form-data")) { |
|
|
|
if (strstr(h, "multipart/form-data")) { |
|
|
|
// It's multipart form data so let's pull out the boundary for future use
|
|
|
|
// It's multipart form data so let's pull out the boundary for future use
|
|
|
|
char *b; |
|
|
|
char *b; |
|
|
|
if ((b = strstr(h, "boundary=")) != NULL) { |
|
|
|
if ((b = strstr(h, "boundary=")) != NULL) { |
|
|
|
conn->post->multipartBoundary = b + 7; // move the pointer 2 chars before boundary then fill them with dashes
|
|
|
|
conn->post.multipartBoundary = b + 7; // move the pointer 2 chars before boundary then fill them with dashes
|
|
|
|
conn->post->multipartBoundary[0] = '-'; |
|
|
|
conn->post.multipartBoundary[0] = '-'; |
|
|
|
conn->post->multipartBoundary[1] = '-'; |
|
|
|
conn->post.multipartBoundary[1] = '-'; |
|
|
|
http_dbg("boundary = %s", conn->post->multipartBoundary); |
|
|
|
http_dbg("boundary = %s", conn->post.multipartBoundary); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} else if (strstarts(h, "Access-Control-Request-Headers: ")) { |
|
|
|
} else if (strstarts(h, "Access-Control-Request-Headers: ")) { |
|
|
@ -806,7 +784,7 @@ void httpdRecvCb(ConnTypePtr rconn, httpd_ipaddr_t remIp, uint16_t remPort, uint |
|
|
|
//ToDo: See if we can use something more elegant for this.
|
|
|
|
//ToDo: See if we can use something more elegant for this.
|
|
|
|
|
|
|
|
|
|
|
|
for (x = 0; x < len; x++) { |
|
|
|
for (x = 0; x < len; x++) { |
|
|
|
if (conn->post->len < 0) { |
|
|
|
if (conn->post.len < 0) { |
|
|
|
//This byte is a header byte.
|
|
|
|
//This byte is a header byte.
|
|
|
|
if (data[x] == '\n') { |
|
|
|
if (data[x] == '\n') { |
|
|
|
//Compatibility with clients that send \n only: fake a \r in front of this.
|
|
|
|
//Compatibility with clients that send \n only: fake a \r in front of this.
|
|
|
@ -820,7 +798,7 @@ void httpdRecvCb(ConnTypePtr rconn, httpd_ipaddr_t remIp, uint16_t remPort, uint |
|
|
|
//Scan for /r/n/r/n. Receiving this indicates the headers end.
|
|
|
|
//Scan for /r/n/r/n. Receiving this indicates the headers end.
|
|
|
|
if (data[x] == '\n' && strstr(conn->priv->head, "\r\n\r\n") != NULL) { |
|
|
|
if (data[x] == '\n' && strstr(conn->priv->head, "\r\n\r\n") != NULL) { |
|
|
|
//Indicate we're done with the headers.
|
|
|
|
//Indicate we're done with the headers.
|
|
|
|
conn->post->len = 0; |
|
|
|
conn->post.len = 0; |
|
|
|
//Reset url data
|
|
|
|
//Reset url data
|
|
|
|
conn->url = NULL; |
|
|
|
conn->url = NULL; |
|
|
|
//Iterate over all received headers and parse them.
|
|
|
|
//Iterate over all received headers and parse them.
|
|
|
@ -833,18 +811,18 @@ void httpdRecvCb(ConnTypePtr rconn, httpd_ipaddr_t remIp, uint16_t remPort, uint |
|
|
|
p = e + 2; //Skip /r/n (now /0/n)
|
|
|
|
p = e + 2; //Skip /r/n (now /0/n)
|
|
|
|
} |
|
|
|
} |
|
|
|
//If we don't need to receive post data, we can send the response now.
|
|
|
|
//If we don't need to receive post data, we can send the response now.
|
|
|
|
if (conn->post->len == 0) { |
|
|
|
if (conn->post.len == 0) { |
|
|
|
httpdProcessRequest(conn); |
|
|
|
httpdProcessRequest(conn); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} else if (conn->post->len != 0) { |
|
|
|
} else if (conn->post.len != 0) { |
|
|
|
//This byte is a POST byte.
|
|
|
|
//This byte is a POST byte.
|
|
|
|
conn->post->buff[conn->post->buffLen++] = (char) data[x]; |
|
|
|
conn->post.buff[conn->post.buffLen++] = (char) data[x]; |
|
|
|
conn->post->received++; |
|
|
|
conn->post.received++; |
|
|
|
conn->hostName = NULL; |
|
|
|
conn->hostName = NULL; |
|
|
|
if (conn->post->buffLen >= conn->post->buffSize || (int) conn->post->received == conn->post->len) { |
|
|
|
if (conn->post.buffLen >= conn->post.buffSize || (int) conn->post.received == conn->post.len) { |
|
|
|
//Received a chunk of post data
|
|
|
|
//Received a chunk of post data
|
|
|
|
conn->post->buff[conn->post->buffLen] = 0; //zero-terminate, in case the cgi handler knows it can use strings
|
|
|
|
conn->post.buff[conn->post.buffLen] = 0; //zero-terminate, in case the cgi handler knows it can use strings
|
|
|
|
//Process the data
|
|
|
|
//Process the data
|
|
|
|
if (conn->cgi) { |
|
|
|
if (conn->cgi) { |
|
|
|
r = conn->cgi(conn); |
|
|
|
r = conn->cgi(conn); |
|
|
@ -856,8 +834,8 @@ void httpdRecvCb(ConnTypePtr rconn, httpd_ipaddr_t remIp, uint16_t remPort, uint |
|
|
|
//call it the first time.
|
|
|
|
//call it the first time.
|
|
|
|
httpdProcessRequest(conn); |
|
|
|
httpdProcessRequest(conn); |
|
|
|
} |
|
|
|
} |
|
|
|
conn->post->buffLen = 0; |
|
|
|
conn->post.buffLen = 0; |
|
|
|
conn->post->len = 0; // this causes transfer to the recvHdl branch
|
|
|
|
conn->post.len = 0; // this causes transfer to the recvHdl branch
|
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
//Let cgi handle data if it registered a recvHdl callback. If not, ignore.
|
|
|
|
//Let cgi handle data if it registered a recvHdl callback. If not, ignore.
|
|
|
@ -947,6 +925,7 @@ int httpdConnectCb(ConnTypePtr conn, httpd_ipaddr_t remIp, uint16_t remPort) |
|
|
|
s_connData[ci]->slot = ci; |
|
|
|
s_connData[ci]->slot = ci; |
|
|
|
s_connData[ci]->remote_ip = remIp; |
|
|
|
s_connData[ci]->remote_ip = remIp; |
|
|
|
s_connData[ci]->remote_port = remPort; |
|
|
|
s_connData[ci]->remote_port = remPort; |
|
|
|
|
|
|
|
s_connData[ci]->post.len = -1; |
|
|
|
|
|
|
|
|
|
|
|
s_connData[ci]->priv = httpdPlatMalloc(sizeof(HttpdPriv)); |
|
|
|
s_connData[ci]->priv = httpdPlatMalloc(sizeof(HttpdPriv)); |
|
|
|
if (s_connData[ci]->priv == NULL) { |
|
|
|
if (s_connData[ci]->priv == NULL) { |
|
|
@ -956,15 +935,6 @@ int httpdConnectCb(ConnTypePtr conn, httpd_ipaddr_t remIp, uint16_t remPort) |
|
|
|
} |
|
|
|
} |
|
|
|
memset(s_connData[ci]->priv, 0, sizeof(HttpdPriv)); |
|
|
|
memset(s_connData[ci]->priv, 0, sizeof(HttpdPriv)); |
|
|
|
|
|
|
|
|
|
|
|
s_connData[ci]->post = httpdPlatMalloc(sizeof(HttpdPostData)); |
|
|
|
|
|
|
|
if (s_connData[ci]->post == NULL) { |
|
|
|
|
|
|
|
http_error("Out of memory allocating connData post struct!"); |
|
|
|
|
|
|
|
httpdPlatUnlock(); |
|
|
|
|
|
|
|
return 0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
memset(s_connData[ci]->post, 0, sizeof(HttpdPostData)); |
|
|
|
|
|
|
|
s_connData[ci]->post->len = -1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
httpdPlatUnlock(); |
|
|
|
httpdPlatUnlock(); |
|
|
|
return 1; |
|
|
|
return 1; |
|
|
|
} |
|
|
|
} |
|
|
|