|
|
@ -29,14 +29,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; |
|
|
|
|
|
|
|
|
|
|
|
//Flags (1 byte)
|
|
|
|
|
|
|
|
#define HFL_HTTP11 (1<<0) |
|
|
|
|
|
|
|
#define HFL_CHUNKED (1<<1) |
|
|
|
|
|
|
|
#define HFL_SENDINGBODY (1<<2) |
|
|
|
|
|
|
|
#define HFL_DISCONAFTERSENT (1<<3) |
|
|
|
|
|
|
|
#define HFL_NOCONNECTIONSTR (1<<4) |
|
|
|
|
|
|
|
#define HFL_NOCORS (1<<5) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Connection pool
|
|
|
|
//Connection pool
|
|
|
@ -198,6 +190,23 @@ void httpdQueueHeader(HttpdConnData *conn, const char *header, const char *value |
|
|
|
strcat(queEntry->headerLine, value); |
|
|
|
strcat(queEntry->headerLine, value); |
|
|
|
strcat(queEntry->headerLine, "\r\n"); |
|
|
|
strcat(queEntry->headerLine, "\r\n"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!httpdQueueHeaderRaw(conn, queEntry)) { |
|
|
|
|
|
|
|
httpdFree(queEntry); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool httpdQueueHeaderRaw(HttpdConnData *conn, HttpdQueuedHeader *queEntry) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (!conn || !queEntry) { |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (conn->priv.flags & HFL_SENDINGBODY) { |
|
|
|
|
|
|
|
http_error("Headers already sent."); |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
queEntry->next = NULL; |
|
|
|
|
|
|
|
|
|
|
|
if (!conn->priv.headersToSend) { |
|
|
|
if (!conn->priv.headersToSend) { |
|
|
|
conn->priv.headersToSend = queEntry; |
|
|
|
conn->priv.headersToSend = queEntry; |
|
|
|
} else { |
|
|
|
} else { |
|
|
@ -208,8 +217,133 @@ void httpdQueueHeader(HttpdConnData *conn, const char *header, const char *value |
|
|
|
} |
|
|
|
} |
|
|
|
ph->next = queEntry; |
|
|
|
ph->next = queEntry; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool httpdSetCookie(HttpdConnData *conn, const SetCookie *parm) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (!conn || !parm) { |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (!parm->name || !parm->value) { |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (conn->priv.flags & HFL_SENDINGBODY) { |
|
|
|
|
|
|
|
http_error("Headers already sent."); |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
Set-Cookie: <cookie-name>=<cookie-value> |
|
|
|
|
|
|
|
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value> |
|
|
|
|
|
|
|
Set-Cookie: <cookie-name>=<cookie-value>; Expires=<date> |
|
|
|
|
|
|
|
Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly |
|
|
|
|
|
|
|
Set-Cookie: <cookie-name>=<cookie-value>; Max-Age=<number> |
|
|
|
|
|
|
|
Set-Cookie: <cookie-name>=<cookie-value>; Partitioned |
|
|
|
|
|
|
|
Set-Cookie: <cookie-name>=<cookie-value>; Path=<path-value> |
|
|
|
|
|
|
|
Set-Cookie: <cookie-name>=<cookie-value>; Secure |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Set-Cookie: <cookie-name>=<cookie-value>; SameSite=Strict |
|
|
|
|
|
|
|
Set-Cookie: <cookie-name>=<cookie-value>; SameSite=Lax |
|
|
|
|
|
|
|
Set-Cookie: <cookie-name>=<cookie-value>; SameSite=None; Secure |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Multiple attributes are also possible, for example:
|
|
|
|
|
|
|
|
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>; Secure; HttpOnly |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define COOKIE_PART_SET_COOKIE "Set-Cookie: " |
|
|
|
|
|
|
|
#define COOKIE_PART_EXPIRES "; Expires=" |
|
|
|
|
|
|
|
#define COOKIE_PART_MAX_AGE "; Max-Age=" |
|
|
|
|
|
|
|
#define COOKIE_PART_DOMAIN "; Domain=" |
|
|
|
|
|
|
|
#define COOKIE_PART_PATH "; Path=" |
|
|
|
|
|
|
|
#define COOKIE_PART_SAME_SITE "; SameSite=" |
|
|
|
|
|
|
|
#define COOKIE_PART_SECURE "; Secure" |
|
|
|
|
|
|
|
#define COOKIE_PART_HTTP_ONLY "; HttpOnly" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// "Set-Cookie: "
|
|
|
|
|
|
|
|
size_t buflen = strlen(COOKIE_PART_SET_COOKIE) |
|
|
|
|
|
|
|
+ strlen(parm->name) + 1 + strlen(parm->value) |
|
|
|
|
|
|
|
+ 5; // cr lf + nul + some spare space
|
|
|
|
|
|
|
|
if (parm->expires) { |
|
|
|
|
|
|
|
buflen += strlen(COOKIE_PART_EXPIRES) + strlen(parm->expires); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (parm->maxAge != 0) { |
|
|
|
|
|
|
|
// 10 chars are needed for 32bit integer
|
|
|
|
|
|
|
|
buflen += strlen(COOKIE_PART_MAX_AGE) + 10; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (parm->domain) { |
|
|
|
|
|
|
|
buflen += strlen(COOKIE_PART_DOMAIN) + strlen(parm->domain); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (parm->path) { |
|
|
|
|
|
|
|
buflen += strlen(COOKIE_PART_PATH) + strlen(parm->path); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (parm->sameSite) { |
|
|
|
|
|
|
|
// Strict, Lax, None
|
|
|
|
|
|
|
|
buflen += strlen(COOKIE_PART_SAME_SITE) + strlen(parm->sameSite); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (parm->secure) { |
|
|
|
|
|
|
|
buflen += strlen(COOKIE_PART_SECURE); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (parm->httponly) { |
|
|
|
|
|
|
|
buflen += strlen(COOKIE_PART_HTTP_ONLY); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HttpdQueuedHeader *queEntry = httpdMalloc(buflen); |
|
|
|
|
|
|
|
if (!queEntry) { |
|
|
|
|
|
|
|
http_error("httpdSetCookie - no mem"); |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
queEntry->next = NULL; |
|
|
|
|
|
|
|
queEntry->headerLine[0] = 0; |
|
|
|
|
|
|
|
strcat(queEntry->headerLine, COOKIE_PART_SET_COOKIE); |
|
|
|
|
|
|
|
strcat(queEntry->headerLine, parm->name); |
|
|
|
|
|
|
|
strcat(queEntry->headerLine, "="); |
|
|
|
|
|
|
|
strcat(queEntry->headerLine, parm->value); |
|
|
|
|
|
|
|
if (parm->expires) { |
|
|
|
|
|
|
|
strcat(queEntry->headerLine, COOKIE_PART_EXPIRES); |
|
|
|
|
|
|
|
strcat(queEntry->headerLine, parm->expires); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (parm->maxAge != 0) { |
|
|
|
|
|
|
|
strcat(queEntry->headerLine, COOKIE_PART_MAX_AGE); |
|
|
|
|
|
|
|
sprintf(queEntry->headerLine + strlen(queEntry->headerLine), "%d", parm->maxAge); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (parm->domain) { |
|
|
|
|
|
|
|
strcat(queEntry->headerLine, COOKIE_PART_DOMAIN); |
|
|
|
|
|
|
|
strcat(queEntry->headerLine, parm->domain); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (parm->path) { |
|
|
|
|
|
|
|
strcat(queEntry->headerLine, COOKIE_PART_PATH); |
|
|
|
|
|
|
|
strcat(queEntry->headerLine, parm->path); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (parm->sameSite) { |
|
|
|
|
|
|
|
strcat(queEntry->headerLine, COOKIE_PART_SAME_SITE); |
|
|
|
|
|
|
|
strcat(queEntry->headerLine, parm->sameSite); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (parm->secure) { |
|
|
|
|
|
|
|
strcat(queEntry->headerLine, COOKIE_PART_SECURE); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (parm->httponly) { |
|
|
|
|
|
|
|
strcat(queEntry->headerLine, COOKIE_PART_HTTP_ONLY); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
strcat(queEntry->headerLine, "\r\n"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!httpdQueueHeaderRaw(conn, queEntry)) { |
|
|
|
|
|
|
|
// should not be possible
|
|
|
|
|
|
|
|
httpdFree(queEntry); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void httdSetTransferMode(HttpdConnData *conn, httpd_transfer_opt mode) |
|
|
|
void httdSetTransferMode(HttpdConnData *conn, httpd_transfer_opt mode) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (mode == HTTPD_TRANSFER_CLOSE) { |
|
|
|
if (mode == HTTPD_TRANSFER_CLOSE) { |
|
|
|