diff --git a/demo/server_demo.c b/demo/server_demo.c index 98b2127..253479c 100644 --- a/demo/server_demo.c +++ b/demo/server_demo.c @@ -75,26 +75,24 @@ httpd_cgi_state templateReplacer(HttpdConnData *conn, const char *token) return HTTPD_CGI_DONE; } -// -//int httpdGetCookie(HttpdConnData *conn, const char *name, char *buff, size_t buffLen) -//{ -// httpdGetHeader(conn, "Cookie", ) -//} - - httpd_cgi_state cgiStartSession(HttpdConnData *conn) { httpdQueueHeader(conn, "X-Foo", "FOO"); httpdQueueHeader(conn, "X-Bar", "Bar"); + char cbuf[100] = {}; + httpdGetCookie(conn, "testCookie", cbuf, 100); + + printf("Cookie val = %s\n", cbuf); + httpdSetCookie(conn, &(SetCookie) { .name = "testCookie", .value = "lala-lele", // .domain = "localhost", // .expires = "BABABABAx", // .httponly = true, - .maxAge = -1, + .maxAge = 0, // .path = "foo/bar/baz", // .sameSite = COOKIE_SAMESITE_LAX, // .secure = true, diff --git a/spritehttpd/include/httpd-config.h b/spritehttpd/include/httpd-config.h index 424e175..871875e 100644 --- a/spritehttpd/include/httpd-config.h +++ b/spritehttpd/include/httpd-config.h @@ -58,5 +58,3 @@ #define ESPFS_FILENAME_LEN 100 /// EspFs CGI file chunk buffer size #define ESPFS_FILE_CHUNK_LEN 512 -/// EspFs CGI header buffer (used to detect gzip) -#define ESPFS_HEADER_LEN 64 diff --git a/spritehttpd/include/httpd.h b/spritehttpd/include/httpd.h index 9155d92..c05a9a5 100644 --- a/spritehttpd/include/httpd.h +++ b/spritehttpd/include/httpd.h @@ -102,6 +102,18 @@ void httpdEndHeaders(HttpdConnData *conn); */ int httpdGetHeader(HttpdConnData *conn, const char *header, char *buff, size_t buffLen); +/** + * Get pointer to a request header's value, if found. This is a way to read headers without + * copying or being limited by the copy buffer length. + * + * The returned string is a view into the headers buffer. The header is 0-terminated. + * + * @param conn + * @param header - name + * @return pointer to the header value, or NULL + */ +const char * httpdGetHeaderPtr(HttpdConnData *conn, const char *header); + /** * Queue a header to be sent with the response. * @@ -125,7 +137,7 @@ void httpdQueueHeader(HttpdConnData *conn, const char *header, const char *value bool httpdQueueHeaderRaw(HttpdConnData *conn, HttpdQueuedHeader *queEntry); /** - * Set cookie. This queues the cookie header. + * Set cookie. This queues the cookie header - can only be called before headers are sent. * * @param conn * @param parm @@ -133,6 +145,17 @@ bool httpdQueueHeaderRaw(HttpdConnData *conn, HttpdQueuedHeader *queEntry); */ bool httpdSetCookie(HttpdConnData *conn, const SetCookie *parm); +/** + * Get cookie value, if present + * + * @param conn + * @param name - cookie name + * @param buff - copy buf + * @param buffLen - buf len + * @return 1 = OK + */ +bool httpdGetCookie(HttpdConnData *conn, const char *name, char *buff, size_t buffLen); + /** * Send binary data * diff --git a/spritehttpd/src/cgi-espfs.c b/spritehttpd/src/cgi-espfs.c index 9ca515c..e0d9701 100644 --- a/spritehttpd/src/cgi-espfs.c +++ b/spritehttpd/src/cgi-espfs.c @@ -75,7 +75,6 @@ static httpd_cgi_state serveStaticFile(HttpdConnData *hconn, const char *filepat EspFsFile *file = hconn->cgiData; size_t len; uint8_t buff[ESPFS_FILE_CHUNK_LEN + 1]; - char acceptEncodingBuffer[ESPFS_HEADER_LEN + 1]; if (hconn->conn == NULL) { //Connection aborted. Clean up. @@ -112,8 +111,8 @@ static httpd_cgi_state serveStaticFile(HttpdConnData *hconn, const char *filepat if (isGzip) { // Check the browser's "Accept-Encoding" header. If the client does not // advertise that he accepts GZIP send a warning message (telnet users for e.g.) - httpdGetHeader(hconn, "Accept-Encoding", acceptEncodingBuffer, ESPFS_HEADER_LEN); - if (strstr(acceptEncodingBuffer, "gzip") == NULL) { + const char *phdr = httpdGetHeaderPtr(hconn, "Accept-Encoding"); + if (phdr && strstr(phdr, "gzip") == NULL) { //No Accept-Encoding: gzip header present httpdSendStr(hconn, gzipNonSupportedMessage); espFsClose(file); diff --git a/spritehttpd/src/httpd.c b/spritehttpd/src/httpd.c index 06cdb30..c5eb196 100644 --- a/spritehttpd/src/httpd.c +++ b/spritehttpd/src/httpd.c @@ -29,8 +29,6 @@ _Static_assert(HTTPD_MAX_CONNECTIONS < 256, "HTTPD_MAX_CONNECTIONS must be at mo static const HttpdBuiltInUrl *s_builtInUrls; static const char *s_serverName = HTTPD_SERVERNAME; - - //Connection pool HttpdConnData s_connData[HTTPD_MAX_CONNECTIONS]; @@ -140,6 +138,23 @@ static void httpdRetireConn(HttpdConnData *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) +{ + const char *p = httpdGetHeaderPtr(conn, header); + if (!p) { + return 0; + } + + while (*p != 0 && buffLen > 1) { // && *p != '\r' && *p != '\n' + *buff++ = *p++; + buffLen--; + } + //Zero-terminate string + *buff = 0; + //All done :) + return 1; +} + +const char * httpdGetHeaderPtr(HttpdConnData *conn, const char *header) { char *p = conn->priv.head; p = p + strlen(p) + 1; //skip GET/POST part @@ -152,19 +167,11 @@ int httpdGetHeader(HttpdConnData *conn, const char *header, char *buff, size_t b p = p + strlen(header) + 1; //Skip past spaces after the colon while (*p == ' ') { p++; } - //Copy from p to end - while (*p != 0 && *p != '\r' && *p != '\n' && buffLen > 1) { - *buff++ = *p++; - buffLen--; - } - //Zero-terminate string - *buff = 0; - //All done :) - return 1; + return p; } p += strlen(p) + 1; //Skip past end of string and \0 terminator } - return 0; + return NULL; } void httpdQueueHeader(HttpdConnData *conn, const char *header, const char *value) @@ -234,24 +241,6 @@ bool httpdSetCookie(HttpdConnData *conn, const SetCookie *parm) return false; } - /* - Set-Cookie: = - Set-Cookie: =; Domain= - Set-Cookie: =; Expires= - Set-Cookie: =; HttpOnly - Set-Cookie: =; Max-Age= - Set-Cookie: =; Partitioned - Set-Cookie: =; Path= - Set-Cookie: =; Secure - - Set-Cookie: =; SameSite=Strict - Set-Cookie: =; SameSite=Lax - Set-Cookie: =; SameSite=None; Secure - - // Multiple attributes are also possible, for example: - Set-Cookie: =; Domain=; Secure; HttpOnly - */ - #define COOKIE_PART_SET_COOKIE "Set-Cookie: " #define COOKIE_PART_EXPIRES "; Expires=" #define COOKIE_PART_MAX_AGE "; Max-Age=" @@ -295,7 +284,7 @@ bool httpdSetCookie(HttpdConnData *conn, const SetCookie *parm) buflen += strlen(COOKIE_PART_HTTP_ONLY); } - HttpdQueuedHeader *queEntry = httpdMalloc(buflen); + HttpdQueuedHeader *queEntry = httpdMalloc(sizeof(void*) + buflen); if (!queEntry) { http_error("httpdSetCookie - no mem"); return false; @@ -344,6 +333,41 @@ bool httpdSetCookie(HttpdConnData *conn, const SetCookie *parm) } +bool httpdGetCookie(HttpdConnData *conn, const char *name, char *buff, size_t buffLen) +{ + const char *p = httpdGetHeaderPtr(conn, "Cookie"); + if (!p) return 0; + + size_t namelen = strlen(name); + while (*p != 0 && *p != '\r' && *p != '\n') { + if (*p == ' ') { // skip whitespace + p++; + continue; + } + + const char *next_semi = strchr(p, ';'); + if (!next_semi) { + next_semi = p + strlen(p); // end of string + } + + if (strstarts(p, name) && p[namelen] == '=') { + const char * valuestart = p + namelen + 1; + + size_t valuelen = (size_t) (next_semi - valuestart); + if (valuelen >= buffLen - 1) { + valuelen = buffLen - 1; + } + strncpy(buff, valuestart, valuelen); + return true; + } + + p = next_semi + 1; + } + + return false; +} + + void httdSetTransferMode(HttpdConnData *conn, httpd_transfer_opt mode) { if (mode == HTTPD_TRANSFER_CLOSE) {