add malloc instrumentation

master
Ondřej Hruška 1 year ago
parent 384c22297b
commit 5b343dd687
  1. 1
      Makefile
  2. 5
      demo/server_demo.c
  3. 10
      fstool/httpd-heap.h
  4. 11
      fstool/main.c
  5. 22
      spritehttpd/include/httpd-heap.h
  6. 2
      spritehttpd/include/httpd-logging.h
  7. 8
      spritehttpd/include/httpd-platform.h
  8. 14
      spritehttpd/include/httpd-types.h
  9. 2
      spritehttpd/include/httpd.h
  10. 7
      spritehttpd/lib/espfs/espfs.c
  11. 27
      spritehttpd/lib/heatshrink/heatshrink_config.h
  12. 10
      spritehttpd/src/cgi-espfs.c
  13. 5
      spritehttpd/src/cgi-redirects.c
  14. 13
      spritehttpd/src/cgi-websocket.c
  15. 27
      spritehttpd/src/httpd-heap.c
  16. 2
      spritehttpd/src/httpd-loop.c
  17. 223
      spritehttpd/src/httpd.c
  18. 6
      spritehttpd/src/port/httpd-posix.c

@ -76,6 +76,7 @@ LIB_SOURCES = \
src/httpd-auth.c \
src/httpd-utils.c \
src/httpd-loop.c \
src/httpd-heap.c \
src/cgi-espfs.c \
src/cgi-redirects.c \
src/cgi-websocket.c

@ -5,6 +5,7 @@
#include "httpd.h"
#include "httpd-utils.h"
#include "cgi-espfs.h"
#include "httpd-heap.h"
extern unsigned char espfs_image[];
extern unsigned int espfs_image_len;
@ -35,7 +36,7 @@ httpd_cgi_state templateReplacer(HttpdConnData *conn, const char *token)
}
if (!conn->userData) {
rd = httpdPlatMalloc(sizeof(struct RequestData));
rd = httpdMalloc(sizeof(struct RequestData));
conn->userData = rd;
rd->counter = 0;
@ -130,7 +131,7 @@ int main(void)
// prevent abort on sigpipe
sigaction(SIGPIPE, &(struct sigaction) {{sigpipe_handler},.sa_mask={}}, NULL);
struct httpd_options opts = {
struct httpd_init_options opts = {
.port = 8080,
};

@ -0,0 +1,10 @@
/**
* HTTPD malloc/free wrappers with debug instrumentation
*/
#pragma once
#include <malloc.h>
#define httpdMalloc(len) malloc((len))
#define httpdFree(ptr) free((ptr))

@ -38,17 +38,6 @@ static char **s_gzipExtensions = NULL;
/// Gzip all files
static bool s_gzipAll = false;
// impls to satisfy defs in the config header
void *httpdPlatMalloc(size_t len)
{
return malloc(len);
}
void httpdPlatFree(void *ptr)
{
free(ptr);
}
/**
* Compress a file using Heatshrink
*

@ -0,0 +1,22 @@
/**
* HTTPD malloc/free wrappers with debug instrumentation
*/
#pragma once
#include "httpd-logging.h"
#include "httpd-platform.h"
#if DEBUG_MALLOC
void* httpdMalloc_(size_t len, const char * filename, int lineno, const char * funcname);
void httpdFree_(void *ptr, const char * filename, int lineno, const char * funcname);
#define httpdMalloc(len_) httpdMalloc_((len_), __FILE__, __LINE__, __FUNCTION__)
#define httpdFree(ptr_) httpdFree_((ptr_), __FILE__, __LINE__, __FUNCTION__)
#else
#define httpdMalloc(len, purpose) httpdPlatMalloc((len))
#define httpdFree(ptr) httpdPlatFree((ptr))
#endif

@ -108,7 +108,7 @@
#endif
#ifndef DEBUG_MALLOC
#define DEBUG_MALLOC 0
#define DEBUG_MALLOC 1
#endif
// router (resolving urls to serve)

@ -22,7 +22,9 @@ void httpdPlatLock(void);
void httpdPlatUnlock(void);
/**
* Allocate memory
* Allocate memory.
*
* @attention Do not use directly - call httpdMalloc() instead !
*
* @param len - alloc size
* @return pointer to allocated data or NULL
@ -32,6 +34,8 @@ void *httpdPlatMalloc(size_t len);
/**
* Free allocated data
*
* @attention Do not use directly - call httpdFree() instead !
*
* @param ptr - data pointer
*/
void httpdPlatFree(void *ptr);
@ -83,7 +87,7 @@ void httpdPlatInit(void);
* @param opts - server options
* @return join handle
*/
httpd_thread_handle_t *httpdPlatStart(struct httpd_options *opts);
httpd_thread_handle_t *httpdPlatStart(struct httpd_init_options *opts);
/**
* Wait for the server to end

@ -16,7 +16,7 @@ typedef HttpdConnType* ConnTypePtr;
struct httpd_thread_handle;
typedef struct httpd_thread_handle httpd_thread_handle_t;
struct httpd_options;
struct httpd_init_options;
typedef uint32_t httpd_ipaddr_t;
@ -61,7 +61,7 @@ typedef enum {
/* init options */
struct httpd_options {
struct httpd_init_options {
uint16_t port;
};
@ -76,10 +76,10 @@ typedef struct HttpdQueuedHeader {
char headerLine[];
} HttpdQueuedHeader;
typedef struct HttpSendBacklogItem HttpSendBacklogItem;
struct HttpSendBacklogItem {
typedef struct HttpdSendBacklogItem HttpdSendBacklogItem;
struct HttpdSendBacklogItem {
size_t len;
HttpSendBacklogItem *next;
HttpdSendBacklogItem *next;
uint8_t data[];
};
@ -92,7 +92,7 @@ struct HttpdPriv {
uint8_t *sendBuff;
size_t sendBuffLen;
char *chunkHdr;
HttpSendBacklogItem *sendBacklog;
HttpdSendBacklogItem *sendBacklog;
HttpdQueuedHeader *headersToSend; // Linked list of headers to send with the response. Will be freed with the request.
size_t sendBacklogSize;
uint8_t flags;
@ -122,7 +122,7 @@ typedef httpd_cgi_state (* cgiRecvHandler)(HttpdConnData *connData, uint8_t *dat
//A struct describing a http connection. This gets passed to cgi functions.
struct HttpdConnData {
ConnTypePtr conn; // The TCP connection. Exact type depends on the platform.
HttpdPriv *priv; // Internal httpd state for the connection
HttpdPriv priv; // Internal httpd state for the connection
httpd_method requestType; // One of the HTTPD_METHOD_* values
char *hostName; // Host name field of request

@ -42,7 +42,7 @@ void httpdRedirect(HttpdConnData *conn, const char *newUrl);
* @param options - server options
* @return server thread handle or NULL on error
*/
httpd_thread_handle_t *httpdStart(const HttpdBuiltInUrl *fixedUrls, struct httpd_options *options);
httpd_thread_handle_t *httpdStart(const HttpdBuiltInUrl *fixedUrls, struct httpd_init_options *options);
/**
* Shutdown the server & wait for the thread to end.

@ -23,6 +23,7 @@ It's written for use with httpd, but doesn't need to be used as such.
#include "espfs.h"
#include "httpd-logging.h"
#include "httpd-heap.h"
// internal fields
struct EspFsFile {
@ -135,7 +136,7 @@ EspFsFile *espFsOpenFromHeader(EspFsHeader *h, uint32_t hpos)
return NULL;
}
EspFsFile *r = (EspFsFile *) httpdPlatMalloc(sizeof(EspFsFile)); //Alloc file desc mem
EspFsFile *r = (EspFsFile *) httpdMalloc(sizeof(EspFsFile)); //Alloc file desc mem
if (r == NULL) { return NULL; }
r->headerPos = hpos;
r->decompressor = h->compression;
@ -166,7 +167,7 @@ EspFsFile *espFsOpenFromHeader(EspFsHeader *h, uint32_t hpos)
return r;
} else {
espfs_error("[EspFS] Invalid compression: %d", h->compression);
httpdPlatFree(r);
httpdFree(r);
return NULL;
}
return NULL;
@ -339,5 +340,5 @@ void espFsClose(EspFsFile *fh)
heatshrink_decoder *dec = (heatshrink_decoder *) fh->decompData;
heatshrink_decoder_free(dec);
}
httpdPlatFree(fh);
httpdFree(fh);
}

@ -5,29 +5,10 @@
#define HEATSHRINK_DYNAMIC_ALLOC 1
#endif
#if HEATSHRINK_DYNAMIC_ALLOC
// forward declare - needed when building the heatshrink compressor
void *httpdPlatMalloc(size_t len);
void httpdPlatFree(void *ptr);
/* Optional replacement of malloc/free */
#define HEATSHRINK_MALLOC(SZ) httpdPlatMalloc(SZ)
#define HEATSHRINK_FREE(P, SZ) httpdPlatFree(P)
#else
/* Required parameters for static configuration */
#ifndef HEATSHRINK_STATIC_INPUT_BUFFER_SIZE
#define HEATSHRINK_STATIC_INPUT_BUFFER_SIZE 32
#endif
#ifndef HEATSHRINK_STATIC_WINDOW_BITS
#define HEATSHRINK_STATIC_WINDOW_BITS 8
#endif
#ifndef HEATSHRINK_STATIC_LOOKAHEAD_BITS
#define HEATSHRINK_STATIC_LOOKAHEAD_BITS 4
#endif
#endif
/* Optional replacement of malloc/free */
#include "httpd-heap.h"
#define HEATSHRINK_MALLOC(SZ) httpdMalloc((SZ))
#define HEATSHRINK_FREE(P, SZ) httpdFree((P))
/* Turn on logging for debugging. */
#ifndef HEATSHRINK_DEBUGGING_LOGS

@ -226,14 +226,14 @@ httpd_cgi_state cgiEspFsTemplate(HttpdConnData *conn)
}
espFsClose(tdi->file);
httpdPlatFree(tdi);
httpdFree(tdi);
}
return HTTPD_CGI_DONE;
}
if (tdi == NULL) {
//First call to this cgi. Open the file so we can read it.
tdi = (TplDataInternal *) httpdPlatMalloc(sizeof(TplDataInternal));
tdi = (TplDataInternal *) httpdMalloc(sizeof(TplDataInternal));
if (tdi == NULL) {
espfs_error("Failed to malloc tpl struct");
return HTTPD_CGI_NOTFOUND;
@ -255,7 +255,7 @@ httpd_cgi_state cgiEspFsTemplate(HttpdConnData *conn)
tdi->file = tryOpenIndex(filepath);
if (tdi->file == NULL) {
espfs_error("cgiEspFsTemplate: Couldn't find template for path: %s", conn->url);
httpdPlatFree(tdi);
httpdFree(tdi);
return HTTPD_CGI_NOTFOUND;
}
}
@ -263,7 +263,7 @@ httpd_cgi_state cgiEspFsTemplate(HttpdConnData *conn)
if (espFsFlags(tdi->file) & EFS_FLAG_GZIP) {
espfs_error("cgiEspFsTemplate: Trying to use gzip-compressed file %s as template!", conn->url);
espFsClose(tdi->file);
httpdPlatFree(tdi);
httpdFree(tdi);
return HTTPD_CGI_NOTFOUND;
}
@ -418,7 +418,7 @@ httpd_cgi_state cgiEspFsTemplate(HttpdConnData *conn)
espfs_info("Template sent.");
espFsClose(tdi->file);
httpdPlatFree(tdi);
httpdFree(tdi);
return HTTPD_CGI_DONE;
} else {
//Ok, till next time.

@ -1,5 +1,6 @@
#include "httpd.h"
#include "httpd-logging.h"
#include "httpd-heap.h"
//Use this as a cgi function to redirect one url to another.
@ -53,7 +54,7 @@ httpd_cgi_state cgiRedirectToHostname(HttpdConnData *connData)
//Check hostname; pass on if the same
if (strcmp(connData->hostName, (char *) connData->cgiArg) == 0) { return HTTPD_CGI_NOTFOUND; }
//Not the same. Redirect to real hostname.
buff = httpdPlatMalloc(strlen((char *) connData->cgiArg) + sizeof(hostFmt));
buff = httpdMalloc(strlen((char *) connData->cgiArg) + sizeof(hostFmt));
if (buff == NULL) {
//Bail out
return HTTPD_CGI_DONE;
@ -61,6 +62,6 @@ httpd_cgi_state cgiRedirectToHostname(HttpdConnData *connData)
sprintf(buff, hostFmt, (char *) connData->cgiArg);
http_info("Redirecting to hostname url %s", buff);
httpdRedirect(connData, buff);
httpdPlatFree(buff);
httpdFree(buff);
return HTTPD_CGI_DONE;
}

@ -19,6 +19,7 @@ Websocket support for esphttpd. Inspired by https://github.com/dangrie158/ESP-82
#include "utils/base64.h"
#include "cgi-websocket.h"
#include "httpd-logging.h"
#include "httpd-heap.h"
#define WS_KEY_IDENTIFIER "Sec-WebSocket-Key: "
#define WS_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
@ -228,7 +229,7 @@ static void websockFree(Websock *ws)
}
if (ws->priv) {
httpdPlatFree(ws->priv);
httpdFree(ws->priv);
}
}
@ -377,7 +378,7 @@ httpd_cgi_state cgiWebSocketRecv(HttpdConnData *connData, uint8_t *data, size_t
//We're going to tell the main webserver we're done. The webserver expects us to clean up by ourselves
//we're chosing to be done. Do so.
websockFree(ws);
httpdPlatFree(connData->cgiData);
httpdFree(connData->cgiData);
connData->cgiData = NULL;
}
return r;
@ -395,7 +396,7 @@ httpd_cgi_state cgiWebsocket(HttpdConnData *connData)
if (connData->cgiData) {
Websock *ws = (Websock *) connData->cgiData;
websockFree(ws);
httpdPlatFree(connData->cgiData);
httpdFree(connData->cgiData);
connData->cgiData = NULL;
}
return HTTPD_CGI_DONE;
@ -418,7 +419,7 @@ httpd_cgi_state cgiWebsocket(HttpdConnData *connData)
// httpd_printf("WS: Key: %s\n", buff);
//Seems like a WebSocket connection.
// Alloc structs
connData->cgiData = httpdPlatMalloc(sizeof(Websock));
connData->cgiData = httpdMalloc(sizeof(Websock));
if (connData->cgiData == NULL) {
ws_error("Can't allocate mem for websocket");
return HTTPD_CGI_DONE;
@ -427,10 +428,10 @@ httpd_cgi_state cgiWebsocket(HttpdConnData *connData)
Websock *ws = (Websock *) connData->cgiData;
ws->priv = httpdPlatMalloc(sizeof(WebsockPriv));
ws->priv = httpdMalloc(sizeof(WebsockPriv));
if (ws->priv == NULL) {
ws_error("Can't allocate mem for websocket priv");
httpdPlatFree(connData->cgiData);
httpdFree(connData->cgiData);
connData->cgiData = NULL;
return HTTPD_CGI_DONE;
}

@ -0,0 +1,27 @@
/**
* Malloc instrumentation
*/
#include "httpd-heap.h"
#include "httpd-logging.h"
#include <stddef.h>
#if DEBUG_MALLOC
void* httpdMalloc_(size_t len, const char * filename, int lineno, const char * funcname)
{
void *ptr = httpdPlatMalloc(len);
mem_dbg("%s:%d %s - malloc [%lu] @ %p",
filename, lineno, funcname,
len, ptr);
return ptr;
}
void httpdFree_(void *ptr, const char * filename, int lineno, const char * funcname)
{
mem_dbg("%s:%d %s - free @ %p",
filename, lineno, funcname, ptr);
httpdPlatFree(ptr);
}
#endif

@ -61,7 +61,7 @@ void httpdServerTask(void *pvParameters)
s_shutdown_requested = false;
const struct httpd_options *options = pvParameters;
const struct httpd_init_options *options = pvParameters;
if (options == NULL) {
httpPort = 80;
} else {

@ -17,6 +17,7 @@ Esp8266 http server - core routines
#include "httpd-platform.h"
#include "httpd-utils.h"
#include "httpd-logging.h"
#include "httpd-heap.h"
static void cleanupCgiAndUserData(HttpdConnData *hconn);
@ -84,7 +85,7 @@ const char *httpdGetVersion(void)
size_t httpGetBacklogSize(const HttpdConnData *conn)
{
HttpSendBacklogItem *bl = conn->priv->sendBacklog;
HttpdSendBacklogItem *bl = conn->priv.sendBacklog;
if (!bl) { return 0; }
size_t bytes = 0;
while (bl != NULL) {
@ -122,38 +123,34 @@ static void httpdRetireConn(HttpdConnData *hconn)
cleanupCgiAndUserData(hconn);
// Free any memory allocated for backlog - walk the linked list
if (hconn->priv->sendBacklog != NULL) {
HttpSendBacklogItem *i, *j;
i = hconn->priv->sendBacklog;
if (hconn->priv.sendBacklog != NULL) {
HttpdSendBacklogItem *i, *j;
i = hconn->priv.sendBacklog;
do {
j = i;
i = i->next;
httpdPlatFree(j);
httpdFree(j);
} while (i != NULL);
}
if (hconn->post.buff != NULL) {
httpdPlatFree(hconn->post.buff);
httpdFree(hconn->post.buff);
hconn->post.buff = NULL;
}
if (hconn->priv != NULL) {
httpdPlatFree(hconn->priv);
hconn->priv = NULL;
}
// Unlink from the connection list
s_connData[hconn->slot] = NULL;
// release memory
httpdPlatFree(hconn);
httpdFree(hconn);
}
//Get the value of a certain header in the HTTP client head
//Returns true when found, false when not found.
int httpdGetHeader(HttpdConnData *conn, const char *header, char *buff, size_t buffLen)
{
char *p = conn->priv->head;
char *p = conn->priv.head;
p = p + strlen(p) + 1; //skip GET/POST part
p = p + strlen(p) + 1; //skip HTTP part
while (p < (conn->priv->head + conn->priv->headPos)) {
while (p < (conn->priv.head + conn->priv.headPos)) {
while (*p <= 32 && *p != 0) { p++; } //skip crap at start
//See if this is the header
if (strstarts(p, header) && p[strlen(header)] == ':') {
@ -181,12 +178,12 @@ void httpdQueueHeader(HttpdConnData *conn, const char *header, const char *value
if (!conn || !header || !value) {
return;
}
if (conn->priv->flags & HFL_SENDINGBODY) {
if (conn->priv.flags & HFL_SENDINGBODY) {
http_error("Headers already sent.");
return;
}
HttpdQueuedHeader *queEntry = httpdPlatMalloc(sizeof(void *) + strlen(header) + strlen(value) + 5);
HttpdQueuedHeader *queEntry = httpdMalloc(sizeof(void *) + strlen(header) + strlen(value) + 5);
if (!queEntry) {
http_error("httpdQueueHeader - no mem");
return;
@ -199,10 +196,10 @@ void httpdQueueHeader(HttpdConnData *conn, const char *header, const char *value
strcat(queEntry->headerLine, value);
strcat(queEntry->headerLine, "\r\n");
if (!conn->priv->headersToSend) {
conn->priv->headersToSend = queEntry;
if (!conn->priv.headersToSend) {
conn->priv.headersToSend = queEntry;
} else {
HttpdQueuedHeader *ph = conn->priv->headersToSend;
HttpdQueuedHeader *ph = conn->priv.headersToSend;
// Go to the end of the linked list
while (ph->next) {
ph = ph->next;
@ -214,20 +211,20 @@ void httpdQueueHeader(HttpdConnData *conn, const char *header, const char *value
void httdSetTransferMode(HttpdConnData *conn, httpd_transfer_opt mode)
{
if (mode == HTTPD_TRANSFER_CLOSE) {
conn->priv->flags &= (uint8_t) ~HFL_CHUNKED;
conn->priv->flags &= (uint8_t) ~HFL_NOCONNECTIONSTR;
conn->priv.flags &= (uint8_t) ~HFL_CHUNKED;
conn->priv.flags &= (uint8_t) ~HFL_NOCONNECTIONSTR;
} else if (mode == HTTPD_TRANSFER_CHUNKED) {
conn->priv->flags |= HFL_CHUNKED;
conn->priv->flags &= (uint8_t) ~HFL_NOCONNECTIONSTR;
conn->priv.flags |= HFL_CHUNKED;
conn->priv.flags &= (uint8_t) ~HFL_NOCONNECTIONSTR;
} else if (mode == HTTPD_TRANSFER_NONE) {
conn->priv->flags &= (uint8_t) ~HFL_CHUNKED;
conn->priv->flags |= HFL_NOCONNECTIONSTR;
conn->priv.flags &= (uint8_t) ~HFL_CHUNKED;
conn->priv.flags |= HFL_NOCONNECTIONSTR;
}
}
void httdResponseOptions(HttpdConnData *conn, int cors)
{
if (cors == 0) { conn->priv->flags |= HFL_NOCORS; }
if (cors == 0) { conn->priv.flags |= HFL_NOCORS; }
}
//Start the response headers.
@ -236,8 +233,8 @@ void httpdStartResponse(HttpdConnData *conn, int code)
char buff[256];
const char *connStr = "";
if (!(conn->priv->flags & HFL_NOCONNECTIONSTR)) {
if (conn->priv->flags & HFL_CHUNKED) {
if (!(conn->priv.flags & HFL_NOCONNECTIONSTR)) {
if (conn->priv.flags & HFL_CHUNKED) {
connStr = "Transfer-Encoding: chunked\r\n";
} else {
connStr = "Connection: close\r\n";
@ -245,7 +242,7 @@ void httpdStartResponse(HttpdConnData *conn, int code)
}
size_t l = (size_t) sprintf(buff, "HTTP/1.%d %d %s\r\nServer: %s\r\n%s",
((conn->priv->flags & HFL_HTTP11) ? 1 : 0),
((conn->priv.flags & HFL_HTTP11) ? 1 : 0),
code,
httpdStatusName(code),
s_serverName,
@ -253,7 +250,7 @@ void httpdStartResponse(HttpdConnData *conn, int code)
httpdSendStrN(conn, buff, l);
if (!(conn->priv->flags & HFL_NOCORS)) {
if (!(conn->priv.flags & HFL_NOCORS)) {
// CORS headers
httpdSendStr(conn, "Access-Control-Allow-Origin: *\r\n");
httpdSendStr(conn, "Access-Control-Allow-Methods: GET,POST,OPTIONS\r\n");
@ -273,17 +270,17 @@ void httpdHeader(HttpdConnData *conn, const char *field, const char *val)
void httpdEndHeaders(HttpdConnData *conn)
{
// Add queued headers & dealloc the struct
HttpdQueuedHeader *qh = conn->priv->headersToSend;
conn->priv->headersToSend = NULL;
HttpdQueuedHeader *qh = conn->priv.headersToSend;
conn->priv.headersToSend = NULL;
while (qh) {
httpdSendStr(conn, qh->headerLine);
HttpdQueuedHeader *next = qh->next;
httpdPlatFree(qh);
httpdFree(qh);
qh = next;
}
httpdSendStr(conn, "\r\n");
conn->priv->flags |= HFL_SENDINGBODY;
conn->priv.flags |= HFL_SENDINGBODY;
}
//Redirect to the given URL.
@ -305,16 +302,16 @@ int httpdSend(HttpdConnData *conn, const uint8_t *data, size_t len)
{
if (conn->conn == NULL) { return 0; }
if (len == 0) { return 0; }
if (conn->priv->flags & HFL_CHUNKED && conn->priv->flags & HFL_SENDINGBODY && conn->priv->chunkHdr == NULL) {
if (conn->priv->sendBuffLen + len + 6 > HTTPD_MAX_SENDBUFF_LEN) { return 0; }
if (conn->priv.flags & HFL_CHUNKED && conn->priv.flags & HFL_SENDINGBODY && conn->priv.chunkHdr == NULL) {
if (conn->priv.sendBuffLen + len + 6 > HTTPD_MAX_SENDBUFF_LEN) { return 0; }
//Establish start of chunk
conn->priv->chunkHdr = (char *) &conn->priv->sendBuff[conn->priv->sendBuffLen];
strcpy(conn->priv->chunkHdr, "0000\r\n");
conn->priv->sendBuffLen += 6;
conn->priv.chunkHdr = (char *) &conn->priv.sendBuff[conn->priv.sendBuffLen];
strcpy(conn->priv.chunkHdr, "0000\r\n");
conn->priv.sendBuffLen += 6;
}
if (conn->priv->sendBuffLen + len > HTTPD_MAX_SENDBUFF_LEN) { return 0; }
memcpy(conn->priv->sendBuff + conn->priv->sendBuffLen, data, len);
conn->priv->sendBuffLen += len;
if (conn->priv.sendBuffLen + len > HTTPD_MAX_SENDBUFF_LEN) { return 0; }
memcpy(conn->priv.sendBuff + conn->priv.sendBuffLen, data, len);
conn->priv.sendBuffLen += len;
return 1;
}
@ -395,7 +392,7 @@ int httpdSend_js(HttpdConnData *conn, const char *data, ssize_t len)
return 1;
}
//Function to send any data in conn->priv->sendBuff. Do not use in CGIs unless you know what you
//Function to send any data in conn->priv.sendBuff. Do not use in CGIs unless you know what you
//are doing! Also, if you do set conn->cgi to NULL to indicate the connection is closed, do it BEFORE
//calling this.
//Returns false if data could not be sent nor put in backlog.
@ -404,52 +401,52 @@ bool httpdFlushSendBuffer(HttpdConnData *conn)
int r;
size_t len;
if (conn->conn == NULL) { return false; }
if (conn->priv->chunkHdr != NULL) {
if (conn->priv.chunkHdr != NULL) {
//We're sending chunked data, and the chunk needs fixing up.
//Finish chunk with cr/lf
httpdSendStr(conn, "\r\n");
//Calculate length of chunk
len = (size_t) ((char *) (&conn->priv->sendBuff[conn->priv->sendBuffLen]) - conn->priv->chunkHdr) - 8;
len = (size_t) ((char *) (&conn->priv.sendBuff[conn->priv.sendBuffLen]) - conn->priv.chunkHdr) - 8;
//Fix up chunk header to correct value
conn->priv->chunkHdr[0] = httpdHexNibble((uint8_t) (len >> 12));
conn->priv->chunkHdr[1] = httpdHexNibble((uint8_t) (len >> 8));
conn->priv->chunkHdr[2] = httpdHexNibble((uint8_t) (len >> 4));
conn->priv->chunkHdr[3] = httpdHexNibble((uint8_t) (len >> 0));
conn->priv.chunkHdr[0] = httpdHexNibble((uint8_t) (len >> 12));
conn->priv.chunkHdr[1] = httpdHexNibble((uint8_t) (len >> 8));
conn->priv.chunkHdr[2] = httpdHexNibble((uint8_t) (len >> 4));
conn->priv.chunkHdr[3] = httpdHexNibble((uint8_t) (len >> 0));
//Reset chunk hdr for next call
conn->priv->chunkHdr = NULL;
conn->priv.chunkHdr = NULL;
}
if (conn->priv->flags & HFL_CHUNKED && conn->priv->flags & HFL_SENDINGBODY && conn->cgi == NULL) {
if (conn->priv.flags & HFL_CHUNKED && conn->priv.flags & HFL_SENDINGBODY && conn->cgi == NULL) {
//Connection finished sending whatever needs to be sent. Add NULL chunk to indicate this.
strcpy((char *) &conn->priv->sendBuff[conn->priv->sendBuffLen], "0\r\n\r\n");
conn->priv->sendBuffLen += 5;
strcpy((char *) &conn->priv.sendBuff[conn->priv.sendBuffLen], "0\r\n\r\n");
conn->priv.sendBuffLen += 5;
}
if (conn->priv->sendBuffLen != 0) {
r = httpdConnSendData(conn->conn, conn->priv->sendBuff, conn->priv->sendBuffLen);
if (conn->priv.sendBuffLen != 0) {
r = httpdConnSendData(conn->conn, conn->priv.sendBuff, conn->priv.sendBuffLen);
if (!r) {
//Can't send this for some reason. Dump packet in backlog, we can send it later.
if (conn->priv->sendBacklogSize + conn->priv->sendBuffLen > HTTPD_MAX_BACKLOG_SIZE) {
http_error("Httpd: Backlog overrun, dropped %dB", (int) conn->priv->sendBuffLen);
conn->priv->sendBuffLen = 0;
if (conn->priv.sendBacklogSize + conn->priv.sendBuffLen > HTTPD_MAX_BACKLOG_SIZE) {
http_error("Httpd: Backlog overrun, dropped %dB", (int) conn->priv.sendBuffLen);
conn->priv.sendBuffLen = 0;
return false;
}
HttpSendBacklogItem *i = httpdPlatMalloc(sizeof(HttpSendBacklogItem) + conn->priv->sendBuffLen);
if (i == NULL) {
HttpdSendBacklogItem *backlogItem = httpdMalloc(sizeof(HttpdSendBacklogItem) + conn->priv.sendBuffLen);
if (backlogItem == NULL) {
http_error("Httpd: Backlog: malloc failed, out of memory!");
return false;
}
memcpy(i->data, conn->priv->sendBuff, conn->priv->sendBuffLen);
i->len = conn->priv->sendBuffLen;
i->next = NULL;
if (conn->priv->sendBacklog == NULL) {
conn->priv->sendBacklog = i;
memcpy(backlogItem->data, conn->priv.sendBuff, conn->priv.sendBuffLen);
backlogItem->len = conn->priv.sendBuffLen;
backlogItem->next = NULL;
if (conn->priv.sendBacklog == NULL) {
conn->priv.sendBacklog = backlogItem;
} else {
HttpSendBacklogItem *e = conn->priv->sendBacklog;
HttpdSendBacklogItem *e = conn->priv.sendBacklog;
while (e->next != NULL) { e = e->next; }
e->next = i;
e->next = backlogItem;
}
conn->priv->sendBacklogSize += conn->priv->sendBuffLen;
conn->priv.sendBacklogSize += conn->priv.sendBuffLen;
}
conn->priv->sendBuffLen = 0;
conn->priv.sendBuffLen = 0;
}
return true;
}
@ -457,15 +454,15 @@ bool httpdFlushSendBuffer(HttpdConnData *conn)
static void httpdCgiIsDone(HttpdConnData *conn)
{
conn->cgi = NULL; //no need to call this anymore
if (conn->priv->flags & HFL_CHUNKED) {
if (conn->priv.flags & HFL_CHUNKED) {
http_dbg("Pool slot %d is done. Cleaning up for next req", conn->slot);
httpdFlushSendBuffer(conn);
//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->priv->flags = 0;
conn->priv.flags = 0;
if (conn->post.buff) {
httpdPlatFree(conn->post.buff);
httpdFree(conn->post.buff);
conn->post.buff = NULL;
}
conn->post.buffLen = 0;
@ -473,7 +470,7 @@ static void httpdCgiIsDone(HttpdConnData *conn)
conn->hostName = NULL;
} else {
//Cannot re-use this connection. Mark to get it killed after all data is sent.
conn->priv->flags |= HFL_DISCONAFTERSENT;
conn->priv.flags |= HFL_DISCONAFTERSENT;
}
}
@ -495,18 +492,18 @@ void httpdContinue(HttpdConnData *conn)
if (conn == NULL) { return; }
if (conn->priv->sendBacklog != NULL) {
if (conn->priv.sendBacklog != NULL) {
//We have some backlog to send first.
HttpSendBacklogItem *next = conn->priv->sendBacklog->next;
httpdConnSendData(conn->conn, (uint8_t *) conn->priv->sendBacklog->data, conn->priv->sendBacklog->len);
conn->priv->sendBacklogSize -= conn->priv->sendBacklog->len;
httpdPlatFree(conn->priv->sendBacklog);
conn->priv->sendBacklog = next;
HttpdSendBacklogItem *next = conn->priv.sendBacklog->next;
httpdConnSendData(conn->conn, (uint8_t *) conn->priv.sendBacklog->data, conn->priv.sendBacklog->len);
conn->priv.sendBacklogSize -= conn->priv.sendBacklog->len;
httpdFree(conn->priv.sendBacklog);
conn->priv.sendBacklog = next;
httpdPlatUnlock();
return;
}
if (conn->priv->flags & HFL_DISCONAFTERSENT) { //Marked for destruction?
if (conn->priv.flags & HFL_DISCONAFTERSENT) { //Marked for destruction?
http_dbg("Pool slot %d is done. Closing.", conn->slot);
httpdConnDisconnect(conn->conn);
httpdPlatUnlock();
@ -519,14 +516,14 @@ void httpdContinue(HttpdConnData *conn)
return;
}
sendBuff = httpdPlatMalloc(HTTPD_MAX_SENDBUFF_LEN);
sendBuff = httpdMalloc(HTTPD_MAX_SENDBUFF_LEN);
if (sendBuff == NULL) {
http_error("Malloc of sendBuff failed!");
httpdPlatUnlock();
return;
}
conn->priv->sendBuff = sendBuff;
conn->priv->sendBuffLen = 0;
conn->priv.sendBuff = sendBuff;
conn->priv.sendBuffLen = 0;
httpd_cgi_state r = conn->cgi(conn); //Execute cgi fn.
if (r == HTTPD_CGI_DONE) {
httpdCgiIsDone(conn);
@ -536,7 +533,7 @@ void httpdContinue(HttpdConnData *conn)
httpdCgiIsDone(conn);
}
httpdFlushSendBuffer(conn);
httpdPlatFree(sendBuff);
httpdFree(sendBuff);
httpdPlatUnlock();
}
@ -555,7 +552,7 @@ static void httpdProcessRequest(HttpdConnData *conn)
// CORS preflight, allow the token we received before
if (conn->requestType == HTTPD_METHOD_OPTIONS) {
httpdStartResponse(conn, 200);
httpdHeader(conn, "Access-Control-Allow-Headers", conn->priv->corsToken);
httpdHeader(conn, "Access-Control-Allow-Headers", conn->priv.corsToken);
httpdEndHeaders(conn);
httpdCgiIsDone(conn);
@ -670,7 +667,7 @@ static void httpdParseHeader(char *h, HttpdConnData *conn)
while (*e == ' ') { e++; } //Skip spaces.
//If HTTP/1.1, note that and set chunked encoding
if (strcasecmp(e, "HTTP/1.1") == 0) {
conn->priv->flags |= HFL_HTTP11 | HFL_CHUNKED;
conn->priv.flags |= HFL_HTTP11 | HFL_CHUNKED;
}
http_info("URL = %s", conn->url);
@ -687,7 +684,7 @@ static void httpdParseHeader(char *h, HttpdConnData *conn)
i = 11;
//Skip trailing spaces
while (h[i] == ' ') { i++; }
if (strstarts(&h[i], "close")) { conn->priv->flags &= (uint8_t) ~HFL_CHUNKED; } //Don't use chunked conn
if (strstarts(&h[i], "close")) { conn->priv.flags &= (uint8_t) ~HFL_CHUNKED; } //Don't use chunked conn
} else if (strstarts(h, "Content-Length:")) {
i = 15;
//Skip trailing spaces
@ -703,7 +700,7 @@ static void httpdParseHeader(char *h, HttpdConnData *conn)
conn->post.buffSize = (size_t) conn->post.len;
}
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 *) httpdMalloc(conn->post.buffSize + 1);
if (conn->post.buff == NULL) {
http_error("post buf alloc failed");
return;
@ -725,7 +722,7 @@ static void httpdParseHeader(char *h, HttpdConnData *conn)
http_info("CORS preflight request.");
strncpy(conn->priv->corsToken, h + strlen("Access-Control-Request-Headers: "), HTTPD_MAX_CORS_TOKEN_LEN);
strncpy(conn->priv.corsToken, h + strlen("Access-Control-Request-Headers: "), HTTPD_MAX_CORS_TOKEN_LEN);
}
}
@ -735,20 +732,21 @@ static void httpdParseHeader(char *h, HttpdConnData *conn)
void httpdConnSendStart(HttpdConnData *conn)
{
httpdPlatLock();
uint8_t *sendBuff = httpdPlatMalloc(HTTPD_MAX_SENDBUFF_LEN);
uint8_t *sendBuff = httpdMalloc(HTTPD_MAX_SENDBUFF_LEN);
if (sendBuff == NULL) {
http_error("Malloc sendBuff failed!");
return;
}
conn->priv->sendBuff = sendBuff;
conn->priv->sendBuffLen = 0;
conn->priv.sendBuff = sendBuff;
conn->priv.sendBuffLen = 0;
}
//Finish the live-ness of a connection. Always call this after httpdConnStart
void httpdConnSendFinish(HttpdConnData *conn)
{
if (conn->conn) { httpdFlushSendBuffer(conn); }
httpdPlatFree(conn->priv->sendBuff);
httpdFree(conn->priv.sendBuff);
conn->priv.sendBuff = NULL;
httpdPlatUnlock();
}
@ -766,16 +764,16 @@ void httpdRecvCb(ConnTypePtr rconn, httpd_ipaddr_t remIp, uint16_t remPort, uint
return;
}
uint8_t *sendBuff = httpdPlatMalloc(HTTPD_MAX_SENDBUFF_LEN);
uint8_t *sendBuff = httpdMalloc(HTTPD_MAX_SENDBUFF_LEN);
if (sendBuff == NULL) {
http_error("Malloc sendBuff failed!");
httpdPlatUnlock();
return;
}
conn->priv->sendBuff = sendBuff;
conn->priv->sendBuffLen = 0;
conn->priv->corsToken[0] = 0;
conn->priv.sendBuff = sendBuff;
conn->priv.sendBuffLen = 0;
conn->priv.corsToken[0] = 0;
//This is slightly evil/dirty: we abuse conn->post->len as a state variable for where in the http communications we are:
//<0 (-1): Post len unknown because we're still receiving headers
@ -788,22 +786,22 @@ void httpdRecvCb(ConnTypePtr rconn, httpd_ipaddr_t remIp, uint16_t remPort, uint
//This byte is a header byte.
if (data[x] == '\n') {
//Compatibility with clients that send \n only: fake a \r in front of this.
if (conn->priv->headPos != 0 && conn->priv->head[conn->priv->headPos - 1] != '\r') {
conn->priv->head[conn->priv->headPos++] = '\r';
if (conn->priv.headPos != 0 && conn->priv.head[conn->priv.headPos - 1] != '\r') {
conn->priv.head[conn->priv.headPos++] = '\r';
}
}
//ToDo: return http error code 431 (request header too long) if this happens
if (conn->priv->headPos != HTTPD_MAX_HEAD_LEN) { conn->priv->head[conn->priv->headPos++] = (char) data[x]; }
conn->priv->head[conn->priv->headPos] = 0;
if (conn->priv.headPos != HTTPD_MAX_HEAD_LEN) { conn->priv.head[conn->priv.headPos++] = (char) data[x]; }
conn->priv.head[conn->priv.headPos] = 0;
//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.
conn->post.len = 0;
//Reset url data
conn->url = NULL;
//Iterate over all received headers and parse them.
p = conn->priv->head;
while (p < (&conn->priv->head[conn->priv->headPos - 4])) {
p = conn->priv.head;
while (p < (&conn->priv.head[conn->priv.headPos - 4])) {
e = strstr(p, "\r\n"); //Find end of header line
if (e == NULL) { break; } //Shouldn't happen.
e[0] = 0; //Zero-terminate header
@ -855,7 +853,7 @@ void httpdRecvCb(ConnTypePtr rconn, httpd_ipaddr_t remIp, uint16_t remPort, uint
if (conn->conn) {
httpdFlushSendBuffer(conn);
}
httpdPlatFree(sendBuff);
httpdFree(sendBuff);
httpdPlatUnlock();
}
@ -913,7 +911,7 @@ int httpdConnectCb(ConnTypePtr conn, httpd_ipaddr_t remIp, uint16_t remPort)
return 0;
}
s_connData[ci] = httpdPlatMalloc(sizeof(HttpdConnData));
s_connData[ci] = httpdMalloc(sizeof(HttpdConnData));
if (s_connData[ci] == NULL) {
http_warn("Out of memory allocating connData!");
httpdPlatUnlock();
@ -921,26 +919,21 @@ int httpdConnectCb(ConnTypePtr conn, httpd_ipaddr_t remIp, uint16_t remPort)
}
memset(s_connData[ci], 0, sizeof(HttpdConnData));
memset(&s_connData[ci]->post, 0, sizeof(HttpdPostData));
memset(&s_connData[ci]->priv, 0, sizeof(HttpdPriv));
s_connData[ci]->conn = conn;
s_connData[ci]->slot = ci;
s_connData[ci]->remote_ip = remIp;
s_connData[ci]->remote_port = remPort;
s_connData[ci]->post.len = -1;
s_connData[ci]->priv = httpdPlatMalloc(sizeof(HttpdPriv));
if (s_connData[ci]->priv == NULL) {
http_error("Out of memory allocating connData priv struct!");
httpdPlatUnlock();
return 0;
}
memset(s_connData[ci]->priv, 0, sizeof(HttpdPriv));
httpdPlatUnlock();
return 1;
}
//Httpd initialization routine. Call this to kick off webserver functionality.
httpd_thread_handle_t *httpdStart(const HttpdBuiltInUrl *fixedUrls, struct httpd_options *options)
httpd_thread_handle_t *httpdStart(const HttpdBuiltInUrl *fixedUrls, struct httpd_init_options *options)
{
int i;

@ -1,4 +1,6 @@
#include "httpd-platform.h"
#include "httpd-logging.h"
#include "httpd-heap.h"
#include <pthread.h>
#include <unistd.h>
#include <malloc.h>
@ -52,9 +54,9 @@ void* httpdServerTaskPosix(void *pvParameters)
}
//Initialize listening socket, do general initialization
httpd_thread_handle_t *httpdPlatStart(struct httpd_options *opts)
httpd_thread_handle_t *httpdPlatStart(struct httpd_init_options *opts)
{
struct httpd_thread_handle* handle = httpdPlatMalloc(sizeof(struct httpd_thread_handle));
struct httpd_thread_handle* handle = httpdMalloc(sizeof(struct httpd_thread_handle));
if (!handle) {
return NULL;
}

Loading…
Cancel
Save