|
|
@ -15,11 +15,14 @@ Esp8266 http server - core routines |
|
|
|
#include <stdlib.h> |
|
|
|
#include <stdlib.h> |
|
|
|
#include "httpd.h" |
|
|
|
#include "httpd.h" |
|
|
|
#include "httpd-platform.h" |
|
|
|
#include "httpd-platform.h" |
|
|
|
#include "logging.h" |
|
|
|
#include "httpd-utils.h" |
|
|
|
|
|
|
|
#include "httpd-logging.h" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void httpdRetireConn(HttpdConnData *conn); |
|
|
|
|
|
|
|
|
|
|
|
//This gets set at init time.
|
|
|
|
//This gets set at init time.
|
|
|
|
static const HttpdBuiltInUrl *builtInUrls; |
|
|
|
static const HttpdBuiltInUrl *s_builtInUrls; |
|
|
|
static const char *serverName = HTTPD_SERVERNAME; |
|
|
|
static const char *s_serverName = HTTPD_SERVERNAME; |
|
|
|
|
|
|
|
|
|
|
|
typedef struct HttpSendBacklogItem HttpSendBacklogItem; |
|
|
|
typedef struct HttpSendBacklogItem HttpSendBacklogItem; |
|
|
|
|
|
|
|
|
|
|
@ -54,105 +57,39 @@ struct HttpdPriv { |
|
|
|
//Connection pool
|
|
|
|
//Connection pool
|
|
|
|
HttpdConnData *s_connData[HTTPD_MAX_CONNECTIONS]; |
|
|
|
HttpdConnData *s_connData[HTTPD_MAX_CONNECTIONS]; |
|
|
|
|
|
|
|
|
|
|
|
//Struct to keep extension->mime data in
|
|
|
|
void httpdInternalCloseAllSockets() |
|
|
|
typedef struct { |
|
|
|
|
|
|
|
const char *ext; |
|
|
|
|
|
|
|
const char *mimetype; |
|
|
|
|
|
|
|
} MimeMap; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//#define RSTR(a) ((const char)(a))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//The mappings from file extensions to mime types. If you need an extra mime type,
|
|
|
|
|
|
|
|
//add it here.
|
|
|
|
|
|
|
|
static const MimeMap mimeTypes[] = { |
|
|
|
|
|
|
|
{"htm", "text/html"}, |
|
|
|
|
|
|
|
{"html", "text/html"}, |
|
|
|
|
|
|
|
{"css", "text/css"}, |
|
|
|
|
|
|
|
{"js", "text/javascript"}, |
|
|
|
|
|
|
|
{"txt", "text/plain"}, |
|
|
|
|
|
|
|
{"csv", "text/csv"}, |
|
|
|
|
|
|
|
{"ico", "image/x-icon"}, |
|
|
|
|
|
|
|
{"jpg", "image/jpeg"}, |
|
|
|
|
|
|
|
{"jpeg", "image/jpeg"}, |
|
|
|
|
|
|
|
{"png", "image/png"}, |
|
|
|
|
|
|
|
{"gif", "image/gif"}, |
|
|
|
|
|
|
|
{"bmp", "image/bmp"}, |
|
|
|
|
|
|
|
{"svg", "image/svg+xml"}, |
|
|
|
|
|
|
|
{"xml", "text/xml"}, |
|
|
|
|
|
|
|
{"json", "application/json"}, |
|
|
|
|
|
|
|
{NULL, "text/html"}, //default value
|
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Returns a static char* to a mime type for a given url to a file.
|
|
|
|
|
|
|
|
const char *httpdGetMimetype(const char *url) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
int i = 0; |
|
|
|
httpdPlatLock(); |
|
|
|
//Go find the extension
|
|
|
|
/*release data connection*/ |
|
|
|
const char *ext = url + (strlen(url) - 1); |
|
|
|
for (int i = 0; i < HTTPD_MAX_CONNECTIONS; i++) { |
|
|
|
while (ext != url && *ext != '.') { ext--; } |
|
|
|
//find all valid handle
|
|
|
|
if (*ext == '.') { ext++; } |
|
|
|
if (s_connData[i]->conn == NULL) { |
|
|
|
|
|
|
|
continue; |
|
|
|
while (mimeTypes[i].ext != NULL && strcasecmp(ext, mimeTypes[i].ext) != 0) { i++; } |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return mimeTypes[i].mimetype; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const char *httpdMethodName(httpd_method m) |
|
|
|
if (s_connData[i]->cgi != NULL) { |
|
|
|
{ |
|
|
|
//flush cgi data
|
|
|
|
switch (m) { |
|
|
|
s_connData[i]->cgi(s_connData[i]); |
|
|
|
default: |
|
|
|
s_connData[i]->cgi = NULL; |
|
|
|
case HTTPD_METHOD_GET: |
|
|
|
|
|
|
|
return "GET"; |
|
|
|
|
|
|
|
case HTTPD_METHOD_POST: |
|
|
|
|
|
|
|
return "POST"; |
|
|
|
|
|
|
|
case HTTPD_METHOD_OPTIONS: |
|
|
|
|
|
|
|
return "OPTIONS"; |
|
|
|
|
|
|
|
case HTTPD_METHOD_PUT: |
|
|
|
|
|
|
|
return "PUT"; |
|
|
|
|
|
|
|
case HTTPD_METHOD_DELETE: |
|
|
|
|
|
|
|
return "DELETE"; |
|
|
|
|
|
|
|
case HTTPD_METHOD_PATCH: |
|
|
|
|
|
|
|
return "PATCH"; |
|
|
|
|
|
|
|
case HTTPD_METHOD_HEAD: |
|
|
|
|
|
|
|
return "HEAD"; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const char *code2str(int code) |
|
|
|
httpdConnRelease(s_connData[i]->conn); |
|
|
|
{ |
|
|
|
httpdRetireConn(s_connData[i]); |
|
|
|
switch (code) { |
|
|
|
s_connData[i] = NULL; |
|
|
|
case 200: |
|
|
|
|
|
|
|
return "OK"; |
|
|
|
|
|
|
|
case 301: |
|
|
|
|
|
|
|
return "Moved Permanently"; |
|
|
|
|
|
|
|
case 302: |
|
|
|
|
|
|
|
return "Found"; |
|
|
|
|
|
|
|
case 403: |
|
|
|
|
|
|
|
return "Forbidden"; |
|
|
|
|
|
|
|
case 400: |
|
|
|
|
|
|
|
return "Bad Request"; |
|
|
|
|
|
|
|
case 404: |
|
|
|
|
|
|
|
return "Not Found"; |
|
|
|
|
|
|
|
default: |
|
|
|
|
|
|
|
if (code >= 500) { return "Server Error"; } |
|
|
|
|
|
|
|
if (code >= 400) { return "Client Error"; } |
|
|
|
|
|
|
|
return "OK"; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
httpdPlatUnlock(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Add sensible cache control headers to avoid needless asset reloading |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param connData |
|
|
|
|
|
|
|
* @param mime - mime type string |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
void httpdAddCacheHeaders(HttpdConnData *connData, const char *mime) |
|
|
|
void httpdAddCacheHeaders(HttpdConnData *connData, const char *mime) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (streq(mime, "text/html")) { return; } |
|
|
|
// TODO make this extensible
|
|
|
|
if (streq(mime, "text/plain")) { return; } |
|
|
|
if (streq(mime, "text/html") |
|
|
|
if (streq(mime, "text/csv")) { return; } |
|
|
|
|| streq(mime, "text/plain") |
|
|
|
if (streq(mime, "application/json")) { return; } |
|
|
|
|| streq(mime, "text/csv") |
|
|
|
|
|
|
|
|| streq(mime, "application/json") |
|
|
|
|
|
|
|
) { |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
httpdHeader(connData, "Cache-Control", "max-age=7200, public, must-revalidate"); |
|
|
|
httpdHeader(connData, "Cache-Control", "max-age=7200, public, must-revalidate"); |
|
|
|
} |
|
|
|
} |
|
|
@ -214,75 +151,9 @@ static void httpdRetireConn(HttpdConnData *conn) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//Stupid li'l helper function that returns the value of a hex char.
|
|
|
|
|
|
|
|
static char httpdHexVal(char c) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (c >= '0' && c <= '9') { return c - '0'; } |
|
|
|
|
|
|
|
if (c >= 'A' && c <= 'F') { return c - 'A' + 10; } |
|
|
|
|
|
|
|
if (c >= 'a' && c <= 'f') { return c - 'a' + 10; } |
|
|
|
|
|
|
|
return 0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Decode a percent-encoded value.
|
|
|
|
|
|
|
|
//Takes the valLen bytes stored in val, and converts it into at most retLen bytes that
|
|
|
|
|
|
|
|
//are stored in the ret buffer. Returns the actual amount of bytes used in ret. Also
|
|
|
|
|
|
|
|
//zero-terminates the ret buffer.
|
|
|
|
|
|
|
|
int httpdUrlDecode(const char *val, int valLen, char *ret, int retLen) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
int s = 0, d = 0; |
|
|
|
|
|
|
|
int esced = 0; |
|
|
|
|
|
|
|
char escVal = 0; |
|
|
|
|
|
|
|
while (s < valLen && d < retLen) { |
|
|
|
|
|
|
|
if (esced == 1) { |
|
|
|
|
|
|
|
escVal = httpdHexVal(val[s]) << 4; |
|
|
|
|
|
|
|
esced = 2; |
|
|
|
|
|
|
|
} else if (esced == 2) { |
|
|
|
|
|
|
|
escVal |= httpdHexVal(val[s]); |
|
|
|
|
|
|
|
ret[d++] = escVal; |
|
|
|
|
|
|
|
esced = 0; |
|
|
|
|
|
|
|
} else if (val[s] == '%') { |
|
|
|
|
|
|
|
esced = 1; |
|
|
|
|
|
|
|
} else if (val[s] == '+') { |
|
|
|
|
|
|
|
ret[d++] = ' '; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
ret[d++] = val[s]; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
s++; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (d < retLen) { ret[d] = 0; } |
|
|
|
|
|
|
|
return d; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Find a specific arg in a string of get- or post-data.
|
|
|
|
|
|
|
|
//Line is the string of post/get-data, arg is the name of the value to find. The
|
|
|
|
|
|
|
|
//zero-terminated result is written in buff, with at most buffLen bytes used. The
|
|
|
|
|
|
|
|
//function returns the length of the result, or -1 if the value wasn't found. The
|
|
|
|
|
|
|
|
//returned string will be urldecoded already.
|
|
|
|
|
|
|
|
int httpdFindArg(const char *line, const char *arg, char *buff, int buffLen) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
const char *p, *e; |
|
|
|
|
|
|
|
if (line == NULL) { return -1; } |
|
|
|
|
|
|
|
const int arglen = (int) strlen(arg); |
|
|
|
|
|
|
|
p = line; |
|
|
|
|
|
|
|
while (p != NULL && *p != '\n' && *p != '\r' && *p != 0) { |
|
|
|
|
|
|
|
router_dbg("findArg: %s", p); |
|
|
|
|
|
|
|
if (strstarts(p, arg) && p[arglen] == '=') { |
|
|
|
|
|
|
|
p += arglen + 1; //move p to start of value
|
|
|
|
|
|
|
|
e = strstr(p, "&"); |
|
|
|
|
|
|
|
if (e == NULL) { e = p + strlen(p); } |
|
|
|
|
|
|
|
router_dbg("findArg: val %s len %d", p, (int) (e - p)); |
|
|
|
|
|
|
|
return httpdUrlDecode(p, (int)(e - p), buff, buffLen); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
p = strstr(p, "&"); |
|
|
|
|
|
|
|
if (p != NULL) { p += 1; } |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
router_error("Finding arg %s in %s: Not found :/", arg, line); |
|
|
|
|
|
|
|
return -1; //not found
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Get the value of a certain header in the HTTP client head
|
|
|
|
//Get the value of a certain header in the HTTP client head
|
|
|
|
//Returns true when found, false when not found.
|
|
|
|
//Returns true when found, false when not found.
|
|
|
|
int httpdGetHeader(HttpdConnData *conn, const char *header, char *ret, int retLen) |
|
|
|
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 GET/POST part
|
|
|
@ -296,12 +167,12 @@ int httpdGetHeader(HttpdConnData *conn, const char *header, char *ret, int retLe |
|
|
|
//Skip past spaces after the colon
|
|
|
|
//Skip past spaces after the colon
|
|
|
|
while (*p == ' ') { p++; } |
|
|
|
while (*p == ' ') { p++; } |
|
|
|
//Copy from p to end
|
|
|
|
//Copy from p to end
|
|
|
|
while (*p != 0 && *p != '\r' && *p != '\n' && retLen > 1) { |
|
|
|
while (*p != 0 && *p != '\r' && *p != '\n' && buffLen > 1) { |
|
|
|
*ret++ = *p++; |
|
|
|
*buff++ = *p++; |
|
|
|
retLen--; |
|
|
|
buffLen--; |
|
|
|
} |
|
|
|
} |
|
|
|
//Zero-terminate string
|
|
|
|
//Zero-terminate string
|
|
|
|
*ret = 0; |
|
|
|
*buff = 0; |
|
|
|
//All done :)
|
|
|
|
//All done :)
|
|
|
|
return 1; |
|
|
|
return 1; |
|
|
|
} |
|
|
|
} |
|
|
@ -310,7 +181,7 @@ int httpdGetHeader(HttpdConnData *conn, const char *header, char *ret, int retLe |
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void httdSetTransferMode(HttpdConnData *conn, int mode) |
|
|
|
void httdSetTransferMode(HttpdConnData *conn, httpd_transfer_opt mode) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (mode == HTTPD_TRANSFER_CLOSE) { |
|
|
|
if (mode == HTTPD_TRANSFER_CLOSE) { |
|
|
|
conn->priv->flags &= ~HFL_CHUNKED; |
|
|
|
conn->priv->flags &= ~HFL_CHUNKED; |
|
|
@ -333,19 +204,27 @@ void httdResponseOptions(HttpdConnData *conn, int cors) |
|
|
|
void httpdStartResponse(HttpdConnData *conn, int code) |
|
|
|
void httpdStartResponse(HttpdConnData *conn, int code) |
|
|
|
{ |
|
|
|
{ |
|
|
|
char buff[256]; |
|
|
|
char buff[256]; |
|
|
|
int l; |
|
|
|
size_t l; |
|
|
|
const char *connStr = "Connection: close\r\n"; |
|
|
|
const char *connStr = ""; |
|
|
|
if (conn->priv->flags & HFL_CHUNKED) { connStr = "Transfer-Encoding: chunked\r\n"; } |
|
|
|
|
|
|
|
if (conn->priv->flags & HFL_NOCONNECTIONSTR) { connStr = ""; } |
|
|
|
if (!(conn->priv->flags & HFL_NOCONNECTIONSTR)) { |
|
|
|
|
|
|
|
if (conn->priv->flags & HFL_CHUNKED) { |
|
|
|
|
|
|
|
connStr = "Transfer-Encoding: chunked\r\n"; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
connStr = "Connection: close\r\n"; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
l = sprintf(buff, "HTTP/1.%d %d %s\r\nServer: %s\r\n%s", |
|
|
|
l = 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, |
|
|
|
code, |
|
|
|
code2str(code), |
|
|
|
httpdStatusName(code), |
|
|
|
serverName, |
|
|
|
s_serverName, |
|
|
|
connStr); |
|
|
|
connStr); |
|
|
|
|
|
|
|
|
|
|
|
httpdSendStrN(conn, buff, l); |
|
|
|
httpdSendStrN(conn, buff, l); |
|
|
|
|
|
|
|
|
|
|
|
if (0 == (conn->priv->flags & HFL_NOCORS)) { |
|
|
|
if (!(conn->priv->flags & HFL_NOCORS)) { |
|
|
|
// CORS headers
|
|
|
|
// CORS headers
|
|
|
|
httpdSendStr(conn, "Access-Control-Allow-Origin: *\r\n"); |
|
|
|
httpdSendStr(conn, "Access-Control-Allow-Origin: *\r\n"); |
|
|
|
httpdSendStr(conn, "Access-Control-Allow-Methods: GET,POST,OPTIONS\r\n"); |
|
|
|
httpdSendStr(conn, "Access-Control-Allow-Methods: GET,POST,OPTIONS\r\n"); |
|
|
@ -463,19 +342,12 @@ int httpdSend(HttpdConnData *conn, const uint8_t *data, size_t len) |
|
|
|
return 1; |
|
|
|
return 1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static char httpdHexNibble(int val) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
val &= 0xf; |
|
|
|
|
|
|
|
if (val < 10) { return (char) ('0' + val); } |
|
|
|
|
|
|
|
return (char) ('A' + (val - 10)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define httpdSend_orDie(conn, data, len) do { if (!httpdSend((conn), (const uint8_t *)(data), (len))) return false; } while (0) |
|
|
|
#define httpdSend_orDie(conn, data, len) do { if (!httpdSend((conn), (const uint8_t *)(data), (len))) return false; } while (0) |
|
|
|
|
|
|
|
|
|
|
|
#define httpdSendStr_orDie(conn, data) do { if (!httpdSendStr((conn), (data))) return false; } while (0) |
|
|
|
#define httpdSendStr_orDie(conn, data) do { if (!httpdSendStr((conn), (data))) return false; } while (0) |
|
|
|
|
|
|
|
|
|
|
|
/* encode for HTML. returns 0 or 1 - 1 = success */ |
|
|
|
/* encode for HTML. returns 0 or 1 - 1 = success */ |
|
|
|
int httpdSend_html(HttpdConnData *conn, const uint8_t *data, ssize_t len) |
|
|
|
int httpdSend_html(HttpdConnData *conn, const char *data, ssize_t len) |
|
|
|
{ |
|
|
|
{ |
|
|
|
int start = 0, end = 0; |
|
|
|
int start = 0, end = 0; |
|
|
|
uint8_t c; |
|
|
|
uint8_t c; |
|
|
@ -506,7 +378,7 @@ int httpdSend_html(HttpdConnData *conn, const uint8_t *data, ssize_t len) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* encode for JS. returns 0 or 1 - 1 = success */ |
|
|
|
/* encode for JS. returns 0 or 1 - 1 = success */ |
|
|
|
int httpdSend_js(HttpdConnData *conn, const uint8_t *data, ssize_t len) |
|
|
|
int httpdSend_js(HttpdConnData *conn, const char *data, ssize_t len) |
|
|
|
{ |
|
|
|
{ |
|
|
|
int start = 0, end = 0; |
|
|
|
int start = 0, end = 0; |
|
|
|
uint8_t c; |
|
|
|
uint8_t c; |
|
|
@ -710,9 +582,9 @@ static void httpdProcessRequest(HttpdConnData *conn) |
|
|
|
//See if we can find a CGI that's happy to handle the request.
|
|
|
|
//See if we can find a CGI that's happy to handle the request.
|
|
|
|
while (1) { |
|
|
|
while (1) { |
|
|
|
//Look up URL in the built-in URL table.
|
|
|
|
//Look up URL in the built-in URL table.
|
|
|
|
while (builtInUrls[i].url != NULL) { |
|
|
|
while (s_builtInUrls[i].url != NULL) { |
|
|
|
int match = 0; |
|
|
|
int match = 0; |
|
|
|
const char *route = builtInUrls[i].url; |
|
|
|
const char *route = s_builtInUrls[i].url; |
|
|
|
//See if there's a literal match
|
|
|
|
//See if there's a literal match
|
|
|
|
if (streq(route, conn->url)) { match = 1; } |
|
|
|
if (streq(route, conn->url)) { match = 1; } |
|
|
|
//See if there's a wildcard match (*)
|
|
|
|
//See if there's a wildcard match (*)
|
|
|
@ -729,14 +601,14 @@ static void httpdProcessRequest(HttpdConnData *conn) |
|
|
|
if (match) { |
|
|
|
if (match) { |
|
|
|
router_dbg("Matched route #%d, url=%s", i, route); |
|
|
|
router_dbg("Matched route #%d, url=%s", i, route); |
|
|
|
conn->cgiData = NULL; |
|
|
|
conn->cgiData = NULL; |
|
|
|
conn->cgi = builtInUrls[i].cgiCb; |
|
|
|
conn->cgi = s_builtInUrls[i].cgiCb; |
|
|
|
conn->cgiArg = builtInUrls[i].cgiArg; |
|
|
|
conn->cgiArg = s_builtInUrls[i].cgiArg; |
|
|
|
conn->cgiArg2 = builtInUrls[i].cgiArg2; |
|
|
|
conn->cgiArg2 = s_builtInUrls[i].cgiArg2; |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
i++; |
|
|
|
i++; |
|
|
|
} |
|
|
|
} |
|
|
|
if (builtInUrls[i].url == NULL) { |
|
|
|
if (s_builtInUrls[i].url == NULL) { |
|
|
|
//Drat, we're at the end of the URL table. This usually shouldn't happen. Well, just
|
|
|
|
//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.
|
|
|
|
//generate a built-in 404 to handle this.
|
|
|
|
router_warn("%s not found. 404!", conn->url); |
|
|
|
router_warn("%s not found. 404!", conn->url); |
|
|
@ -845,14 +717,14 @@ static void httpdParseHeader(char *h, HttpdConnData *conn) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
conn->post->buffSize = conn->post->len; |
|
|
|
conn->post->buffSize = conn->post->len; |
|
|
|
} |
|
|
|
} |
|
|
|
http_dbg("Mallocced buffer for %d + 1 bytes of post data.", 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("...failed!"); |
|
|
|
http_error("...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; |
|
|
@ -896,7 +768,7 @@ void httpdConnSendFinish(HttpdConnData *conn) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//Callback called when there's data available on a socket.
|
|
|
|
//Callback called when there's data available on a socket.
|
|
|
|
void httpdRecvCb(ConnTypePtr rconn, const char *remIp, int remPort, char *data, unsigned short len) |
|
|
|
void httpdRecvCb(ConnTypePtr rconn, const char *remIp, int remPort, uint8_t *data, unsigned short len) |
|
|
|
{ |
|
|
|
{ |
|
|
|
int x, r; |
|
|
|
int x, r; |
|
|
|
char *p, *e; |
|
|
|
char *p, *e; |
|
|
@ -937,7 +809,7 @@ void httpdRecvCb(ConnTypePtr rconn, const char *remIp, int remPort, char *data, |
|
|
|
//ToDo: return http error code 431 (request header too long) if this happens
|
|
|
|
//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++] = data[x]; } |
|
|
|
if (conn->priv->headPos != HTTPD_MAX_HEAD_LEN) { conn->priv->head[conn->priv->headPos++] = data[x]; } |
|
|
|
conn->priv->head[conn->priv->headPos] = 0; |
|
|
|
conn->priv->head[conn->priv->headPos] = 0; |
|
|
|
//Scan for /r/n/r/n. Receiving this indicate 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; |
|
|
@ -962,7 +834,7 @@ void httpdRecvCb(ConnTypePtr rconn, const char *remIp, int remPort, char *data, |
|
|
|
conn->post->buff[conn->post->buffLen++] = data[x]; |
|
|
|
conn->post->buff[conn->post->buffLen++] = data[x]; |
|
|
|
conn->post->received++; |
|
|
|
conn->post->received++; |
|
|
|
conn->hostName = NULL; |
|
|
|
conn->hostName = NULL; |
|
|
|
if (conn->post->buffLen >= conn->post->buffSize || 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
|
|
|
@ -994,7 +866,9 @@ void httpdRecvCb(ConnTypePtr rconn, const char *remIp, int remPort, char *data, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (conn->conn) { httpdFlushSendBuffer(conn); } |
|
|
|
if (conn->conn) { |
|
|
|
|
|
|
|
httpdFlushSendBuffer(conn); |
|
|
|
|
|
|
|
} |
|
|
|
httpdPlatFree(sendBuff); |
|
|
|
httpdPlatFree(sendBuff); |
|
|
|
httpdPlatUnlock(); |
|
|
|
httpdPlatUnlock(); |
|
|
|
} |
|
|
|
} |
|
|
@ -1022,13 +896,18 @@ int httpdConnectCb(ConnTypePtr conn, const char *remIp, int remPort) |
|
|
|
int i; |
|
|
|
int i; |
|
|
|
httpdPlatLock(); |
|
|
|
httpdPlatLock(); |
|
|
|
//Find empty conndata in pool
|
|
|
|
//Find empty conndata in pool
|
|
|
|
for (i = 0; i < HTTPD_MAX_CONNECTIONS; i++) { if (s_connData[i] == NULL) { break; }} |
|
|
|
for (i = 0; i < HTTPD_MAX_CONNECTIONS; i++) { |
|
|
|
http_info("Conn req from %d.%d.%d.%d:%d, using pool slot %d", remIp[0] & 0xff, remIp[1] & 0xff, remIp[2] & 0xff, remIp[3] & 0xff, remPort, i); |
|
|
|
if (s_connData[i] == NULL) { |
|
|
|
if (i == HTTPD_MAX_CONNECTIONS) { |
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
http_info("Conn req from %d.%d.%d.%d:%d, using pool slot %d", remIp[0], remIp[1], remIp[2], remIp[3], remPort, i); |
|
|
|
|
|
|
|
if (i >= HTTPD_MAX_CONNECTIONS) { |
|
|
|
http_error("Aiee, conn pool overflow!"); |
|
|
|
http_error("Aiee, conn pool overflow!"); |
|
|
|
httpdPlatUnlock(); |
|
|
|
httpdPlatUnlock(); |
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
s_connData[i] = httpdPlatMalloc(sizeof(HttpdConnData)); |
|
|
|
s_connData[i] = httpdPlatMalloc(sizeof(HttpdConnData)); |
|
|
|
if (s_connData[i] == NULL) { |
|
|
|
if (s_connData[i] == NULL) { |
|
|
|
http_warn("Out of memory allocating connData!"); |
|
|
|
http_warn("Out of memory allocating connData!"); |
|
|
@ -1063,14 +942,14 @@ int httpdConnectCb(ConnTypePtr conn, const char *remIp, int remPort) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//Httpd initialization routine. Call this to kick off webserver functionality.
|
|
|
|
//Httpd initialization routine. Call this to kick off webserver functionality.
|
|
|
|
httpd_thread_handle_t *httpdInit(const HttpdBuiltInUrl *fixedUrls, struct httpd_options *options) |
|
|
|
httpd_thread_handle_t *httpdStart(const HttpdBuiltInUrl *fixedUrls, struct httpd_options *options) |
|
|
|
{ |
|
|
|
{ |
|
|
|
int i; |
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < HTTPD_MAX_CONNECTIONS; i++) { |
|
|
|
for (i = 0; i < HTTPD_MAX_CONNECTIONS; i++) { |
|
|
|
s_connData[i] = NULL; |
|
|
|
s_connData[i] = NULL; |
|
|
|
} |
|
|
|
} |
|
|
|
builtInUrls = fixedUrls; |
|
|
|
s_builtInUrls = fixedUrls; |
|
|
|
|
|
|
|
|
|
|
|
httpdPlatInit(); |
|
|
|
httpdPlatInit(); |
|
|
|
|
|
|
|
|
|
|
@ -1081,14 +960,12 @@ httpd_thread_handle_t *httpdInit(const HttpdBuiltInUrl *fixedUrls, struct httpd_ |
|
|
|
|
|
|
|
|
|
|
|
void httpdJoin(httpd_thread_handle_t *handle) |
|
|
|
void httpdJoin(httpd_thread_handle_t *handle) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
if (handle) { |
|
|
|
httpdPlatJoin(handle); |
|
|
|
httpdPlatJoin(handle); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Set server name (must be constant / strdup) |
|
|
|
|
|
|
|
* @param name |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
void httpdSetName(const char *name) |
|
|
|
void httpdSetName(const char *name) |
|
|
|
{ |
|
|
|
{ |
|
|
|
serverName = name; |
|
|
|
s_serverName = name; |
|
|
|
} |
|
|
|
} |
|
|
|