rearrange type definitions, make post static part of ConnData

master
Ondřej Hruška 2 years ago
parent 4204e9cfc3
commit 384c22297b
  1. 53
      spritehttpd/include/httpd-config.h
  2. 80
      spritehttpd/include/httpd-types.h
  3. 48
      spritehttpd/include/httpd.h
  4. 3
      spritehttpd/src/cgi-espfs.c
  5. 106
      spritehttpd/src/httpd.c

@ -0,0 +1,53 @@
/**
* HTTPD config defines
*/
#pragma once
#ifndef GIT_HASH
#define GIT_HASH "unknown"
#endif
// we must not use this macro outside the library, as the git hash is not defined there
#define HTTPDVER "0.4+MightyPork/libesphttpd#" GIT_HASH
// default servername
#ifndef HTTPD_SERVERNAME
#define HTTPD_SERVERNAME "SpriteHTTPD " HTTPDVER
#endif
//Max length of request head. This is statically allocated for each connection.
#ifndef HTTPD_MAX_HEAD_LEN
#define HTTPD_MAX_HEAD_LEN 1024
#endif
//Max post buffer len. This is dynamically malloc'ed if needed.
#ifndef HTTPD_MAX_POST_LEN
#define HTTPD_MAX_POST_LEN 2048
#endif
//Max send buffer len. This is allocated on the stack.
#ifndef HTTPD_MAX_SENDBUFF_LEN
#define HTTPD_MAX_SENDBUFF_LEN 2048
#endif
//Receive buffer
#ifndef HTTPD_RECV_BUF_LEN
#define HTTPD_RECV_BUF_LEN 2048
#endif
//If some data can't be sent because the underlaying socket doesn't accept the data (like the nonos
//layer is prone to do), we put it in a backlog that is dynamically malloc'ed. This defines the max
//size of the backlog.
#ifndef HTTPD_MAX_BACKLOG_SIZE
#define HTTPD_MAX_BACKLOG_SIZE (4*1024)
#endif
//Max len of CORS token. This is allocated in each connection
#ifndef HTTPD_MAX_CORS_TOKEN_LEN
#define HTTPD_MAX_CORS_TOKEN_LEN 256
#endif
#ifndef HTTPD_MAX_CONNECTIONS
#define HTTPD_MAX_CONNECTIONS 4
#endif

@ -6,6 +6,7 @@
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include "httpd-config.h"
// opaque conn type struct // opaque conn type struct
struct HttpdConnType; struct HttpdConnType;
@ -57,31 +58,77 @@ typedef enum {
HTTPD_TRANSFER_NONE = 2, HTTPD_TRANSFER_NONE = 2,
} httpd_transfer_opt; } httpd_transfer_opt;
typedef struct HttpdPriv HttpdPriv;
typedef struct HttpdConnData HttpdConnData;
typedef struct HttpdPostData HttpdPostData;
typedef httpd_cgi_state (* cgiSendCallback)(HttpdConnData *connData); /* init options */
typedef httpd_cgi_state (* cgiRecvHandler)(HttpdConnData *connData, uint8_t *data, size_t len);
struct httpd_options {
uint16_t port;
};
/* Private types - exposed to allow static alloc */
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[];
};
//Private data for http connection
typedef struct HttpdPriv HttpdPriv;
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;
};
/// Callback type that releases the user data attached to the connection. /// Callback type that releases the user data attached to the connection.
/// The format is compatible with regular "free()" or /// The format is compatible with regular "free()" or
typedef void (* httpdConnUserDataCleanupCb)(void *userData); typedef void (* httpdUserDataCleanupCb)(void *userData);
struct httpd_options {
uint16_t port; //A struct describing the POST data sent inside the http connection. This is used by the CGI functions
typedef struct HttpdPostData HttpdPostData;
struct HttpdPostData {
// FIXME len can be negative due to a stupid hack at `src/httpd.c:923`
int len; // POST Content-Length
size_t buffSize; // The maximum length of the post buffer
size_t buffLen; // The amount of bytes in the current post buffer
size_t received; // The total amount of bytes received so far
char *buff; // Actual POST data buffer
char *multipartBoundary; //Text of the multipart boundary, if any
}; };
typedef struct HttpdConnData HttpdConnData;
typedef httpd_cgi_state (* cgiSendCallback)(HttpdConnData *connData);
typedef httpd_cgi_state (* cgiRecvHandler)(HttpdConnData *connData, uint8_t *data, size_t len);
//A struct describing a http connection. This gets passed to cgi functions. //A struct describing a http connection. This gets passed to cgi functions.
struct HttpdConnData { struct HttpdConnData {
ConnTypePtr conn; // The TCP connection. Exact type depends on the platform. ConnTypePtr conn; // The TCP connection. Exact type depends on the platform.
HttpdPriv *priv; // Opaque pointer to data for internal httpd housekeeping HttpdPriv *priv; // Internal httpd state for the connection
httpd_method requestType; // One of the HTTPD_METHOD_* values httpd_method requestType; // One of the HTTPD_METHOD_* values
char *hostName; // Host name field of request char *hostName; // Host name field of request
char *url; // The URL requested, without hostname or GET arguments char *url; // The URL requested, without hostname or GET arguments
char *getArgs; // The GET arguments for this request, if any. char *getArgs; // The GET arguments for this request, if any.
HttpdPostData *post; // POST data structure HttpdPostData post; // POST data structure
cgiSendCallback cgi; // CGI function pointer cgiSendCallback cgi; // CGI function pointer
cgiRecvHandler recvHdl; // Handler for data received after headers, if any cgiRecvHandler recvHdl; // Handler for data received after headers, if any
@ -94,21 +141,10 @@ struct HttpdConnData {
/// If userData is not NULL when a connection is finalized, this function (if set) is called to handle the cleanup. /// If userData is not NULL when a connection is finalized, this function (if set) is called to handle the cleanup.
/// This mechanism is useful when the user data is allocated by an auth CGI and the request can end in any number /// This mechanism is useful when the user data is allocated by an auth CGI and the request can end in any number
/// of other routes, perhaps even with a static file - then the cleanup code doesn't need to be repeated everywhere. /// of other routes, perhaps even with a static file - then the cleanup code doesn't need to be repeated everywhere.
httpdConnUserDataCleanupCb userDataCleanupCb; httpdUserDataCleanupCb userDataCleanupCb;
// this should be at the end because of padding // this should be at the end because of padding
uint16_t remote_port; // Remote TCP port uint16_t remote_port; // Remote TCP port
httpd_ipaddr_t remote_ip; // IP address of client httpd_ipaddr_t remote_ip; // IP address of client
uint8_t slot; // Slot ID - index in s_connData uint8_t slot; // Slot ID - index in s_connData
}; };
//A struct describing the POST data sent inside the http connection. This is used by the CGI functions
struct HttpdPostData {
// FIXME len can be negative due to a stupid hack at `src/httpd.c:923`
int len; // POST Content-Length
size_t buffSize; // The maximum length of the post buffer
size_t buffLen; // The amount of bytes in the current post buffer
size_t received; // The total amount of bytes received so far
char *buff; // Actual POST data buffer
char *multipartBoundary; //Text of the multipart boundary, if any
};

@ -8,54 +8,8 @@
#include "httpd-platform.h" #include "httpd-platform.h"
#include "httpd-types.h" #include "httpd-types.h"
#include "httpd-routes.h" #include "httpd-routes.h"
#include "httpd-config.h"
#ifndef GIT_HASH
#define GIT_HASH "unknown"
#endif
// we must not use this macro outside the library, as the git hash is not defined there
#define HTTPDVER "0.4+MightyPork/libesphttpd#" GIT_HASH
// default servername
#ifndef HTTPD_SERVERNAME
#define HTTPD_SERVERNAME "SpriteHTTPD " HTTPDVER
#endif
//Max length of request head. This is statically allocated for each connection.
#ifndef HTTPD_MAX_HEAD_LEN
#define HTTPD_MAX_HEAD_LEN 1024
#endif
//Max post buffer len. This is dynamically malloc'ed if needed.
#ifndef HTTPD_MAX_POST_LEN
#define HTTPD_MAX_POST_LEN 2048
#endif
//Max send buffer len. This is allocated on the stack.
#ifndef HTTPD_MAX_SENDBUFF_LEN
#define HTTPD_MAX_SENDBUFF_LEN 2048
#endif
//Receive buffer
#ifndef HTTPD_RECV_BUF_LEN
#define HTTPD_RECV_BUF_LEN 2048
#endif
//If some data can't be sent because the underlaying socket doesn't accept the data (like the nonos
//layer is prone to do), we put it in a backlog that is dynamically malloc'ed. This defines the max
//size of the backlog.
#ifndef HTTPD_MAX_BACKLOG_SIZE
#define HTTPD_MAX_BACKLOG_SIZE (4*1024)
#endif
//Max len of CORS token. This is allocated in each connection
#ifndef HTTPD_MAX_CORS_TOKEN_LEN
#define HTTPD_MAX_CORS_TOKEN_LEN 256
#endif
#ifndef HTTPD_MAX_CONNECTIONS
#define HTTPD_MAX_CONNECTIONS 4
#endif
/** /**
* Get the server version string * Get the server version string

@ -86,6 +86,7 @@ static httpd_cgi_state serveStaticFile(HttpdConnData *hconn, const char *filepat
if (hconn->conn == NULL) { if (hconn->conn == NULL) {
//Connection aborted. Clean up. //Connection aborted. Clean up.
espFsClose(file); espFsClose(file);
hconn->cgiData = NULL;
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
@ -122,6 +123,7 @@ static httpd_cgi_state serveStaticFile(HttpdConnData *hconn, const char *filepat
//No Accept-Encoding: gzip header present //No Accept-Encoding: gzip header present
httpdSendStr(hconn, gzipNonSupportedMessage); httpdSendStr(hconn, gzipNonSupportedMessage);
espFsClose(file); espFsClose(file);
hconn->cgiData = NULL;
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
} }
@ -146,6 +148,7 @@ static httpd_cgi_state serveStaticFile(HttpdConnData *hconn, const char *filepat
if (len != ESPFS_FILE_CHUNK_LEN) { if (len != ESPFS_FILE_CHUNK_LEN) {
//We're done. //We're done.
espFsClose(file); espFsClose(file);
hconn->cgiData = NULL;
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} else { } else {
//Ok, till next time. //Ok, till next time.

@ -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;
} }

Loading…
Cancel
Save