From 4e4a340b4c204e1d8dcc8af6685a35b6d4025b79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Fri, 27 Jan 2023 21:37:52 +0100 Subject: [PATCH] fixes in templates, better template api --- demo/Makefile | 3 +- demo/server_demo.c | 69 ++++++++++++++++++++++++----- demo/staticfiles/template.tpl.txt | 19 ++++++++ spritehttpd/Makefile | 3 +- spritehttpd/include/cgi-redirects.h | 33 ++++++++++++++ spritehttpd/include/httpd-logging.h | 4 +- spritehttpd/include/httpd-routes.h | 3 +- spritehttpd/include/httpd-utils.h | 19 +++++++- spritehttpd/include/httpd.h | 23 ++-------- spritehttpd/src/cgi-espfs.c | 14 +++++- spritehttpd/src/cgi-redirects.c | 66 +++++++++++++++++++++++++++ spritehttpd/src/httpd-utils.c | 2 +- spritehttpd/src/httpd.c | 63 -------------------------- 13 files changed, 220 insertions(+), 101 deletions(-) create mode 100644 demo/staticfiles/template.tpl.txt create mode 100644 spritehttpd/include/cgi-redirects.h create mode 100644 spritehttpd/src/cgi-redirects.c diff --git a/demo/Makefile b/demo/Makefile index 2867c1d..06f8c43 100644 --- a/demo/Makefile +++ b/demo/Makefile @@ -6,7 +6,8 @@ LIBFILE = ../spritehttpd/libspritehttpd.a STATIC_FILES = \ staticfiles/index.html \ - staticfiles/kocour.jpg + staticfiles/kocour.jpg \ + staticfiles/template.tpl.txt all: demo diff --git a/demo/server_demo.c b/demo/server_demo.c index b7bc59d..c77d335 100644 --- a/demo/server_demo.c +++ b/demo/server_demo.c @@ -4,24 +4,72 @@ #include "httpd.h" #include "httpd-utils.h" -#include "httpd-espfs.h" +#include "cgi-espfs.h" extern unsigned char espfs_image[]; extern unsigned int espfs_image_len; httpd_thread_handle_t *s_serverHandle = NULL; +struct SharedData { + int visits; +}; + +static struct SharedData shared = {}; + +struct RequestData { + int counter; +}; + /** "About" page */ -httpd_cgi_state tplIndex(HttpdConnData *connData, char *token, void **arg) +httpd_cgi_state templateReplacer(TplData *td) { - if (token == NULL) { return HTTPD_CGI_DONE; } - - else if (streq(token, "date")) { - tplSend(connData, __DATE__, -1); - } else if (streq(token, "time")) { - tplSend(connData, __TIME__, -1); - } else if (streq(token, "vers_httpd")) { - tplSend(connData, httpdGetVersion(), -1); + struct RequestData *rd; + + if (!td->conn) { + if (td->userData) { + httpdPlatFree(td->userData); + td->userData = NULL; + } + return HTTPD_CGI_DONE; + } + + if (!td->userData) { + rd = httpdPlatMalloc(sizeof(struct RequestData)); + td->userData = rd; + rd->counter = 0; + + shared.visits++; + } else { + rd = td->userData; + } + + char buf[100]; + + if (streq(td->token, "visits")) { + sprintf(buf, "%d", shared.visits); + tplSend(td, buf); + } + else if (streq(td->token, "html1")) { + tplSend(td, "foo"); + } + else if (streq(td->token, "html2")) { + tplSend(td, "bar"); + } + else if (streq(td->token, "json1")) { + tplSend(td, "\"hello\":\"foo'' and backslash\\ \" also crlf \r\n"); + } + else if (streq(td->token, "multipart")) { + if (rd->counter < 100) { + sprintf(buf, "HELLO %d, ", rd->counter); + tplSend(td, buf); + rd->counter++; + return HTTPD_CGI_MORE; + } + tplSend(td, "and that's it."); + } + else { + return HTTPD_CGI_NOTFOUND; } return HTTPD_CGI_DONE; @@ -37,6 +85,7 @@ const HttpdBuiltInUrl routes[] = { // --- Web pages --- // ROUTE_TPL_FILE("/", tplIndex, "/index.tpl"), ROUTE_FILE("/", "index.html"), + ROUTE_TPL_FILE("/tpl", templateReplacer, "template.tpl.txt"), ROUTE_FILESYSTEM(), ROUTE_END(), diff --git a/demo/staticfiles/template.tpl.txt b/demo/staticfiles/template.tpl.txt new file mode 100644 index 0000000..2694822 --- /dev/null +++ b/demo/staticfiles/template.tpl.txt @@ -0,0 +1,19 @@ +Template test + +Visits: [%visits%] + +JSON: [%j:json1%] + +HTML: [%h:html1%] +HTML: [%html:html2%] + +this is a single percent: %% + +%multipart% + +And here is a bad token %meow% + +and token that is too long to parse %../espfsbuilder/mkespfsimage -c1 -g jpg --strip-path staticfiles -o staticfiles.bin staticfiles/index.html staticfiles/kocour.jpg + +Bye +. diff --git a/spritehttpd/Makefile b/spritehttpd/Makefile index 04fe2eb..afec00a 100644 --- a/spritehttpd/Makefile +++ b/spritehttpd/Makefile @@ -21,10 +21,11 @@ LIB_SOURCES = ${PORT_SOURCES} \ src/utils/base64.c \ src/utils/sha1.c \ src/httpd.c \ - src/cgi-espfs.c \ src/httpd-auth.c \ src/httpd-utils.c \ src/httpd-loop.c \ + src/cgi-espfs.c \ + src/cgi-redirects.c \ src/cgi-websocket.c LIB_OBJS = $(LIB_SOURCES:.c=.o) diff --git a/spritehttpd/include/cgi-redirects.h b/spritehttpd/include/cgi-redirects.h new file mode 100644 index 0000000..98c0cf8 --- /dev/null +++ b/spritehttpd/include/cgi-redirects.h @@ -0,0 +1,33 @@ +/** + * General purpose CGI redirects + */ + +#pragma once + +#include "httpd-types.h" + +/** + * Use this as a cgi function to redirect one url to another. + * + * - arg1 is the URL. + */ +httpd_cgi_state cgiRedirect(HttpdConnData *connData); + +/** + * This CGI function redirects to a fixed url of http://[hostname]/ if hostname field of request isn't + * already that hostname. Use this in combination with a DNS server that redirects everything to the + * ESP in order to load a HTML page as soon as a phone, tablet etc connects to the ESP. Watch out: + * this will also redirect connections when the ESP is in STA mode, potentially to a hostname that is not + * in the 'official' DNS and so will fail. + * + * - arg1 is the new hostname + * + * @param conn - connection + */ +httpd_cgi_state cgiRedirectToHostname(HttpdConnData *connData); + + +/** + * Used to spit out a 404 error + */ +httpd_cgi_state cgiNotFound(HttpdConnData *connData); diff --git a/spritehttpd/include/httpd-logging.h b/spritehttpd/include/httpd-logging.h index dc76b59..b3d97e9 100755 --- a/spritehttpd/include/httpd-logging.h +++ b/spritehttpd/include/httpd-logging.h @@ -84,7 +84,7 @@ #endif #ifndef DEBUG_ESPFS -#define DEBUG_ESPFS 1 +#define DEBUG_ESPFS 0 #endif #ifndef DEBUG_WS @@ -100,7 +100,7 @@ #endif #ifndef DEBUG_HEATSHRINK -#define DEBUG_HEATSHRINK 1 +#define DEBUG_HEATSHRINK 0 #endif #ifndef DEBUG_MALLOC diff --git a/spritehttpd/include/httpd-routes.h b/spritehttpd/include/httpd-routes.h index 8c7aa3f..0febbb4 100644 --- a/spritehttpd/include/httpd-routes.h +++ b/spritehttpd/include/httpd-routes.h @@ -5,6 +5,7 @@ #pragma once #include "cgi-websocket.h" +#include "cgi-redirects.h" //A struct describing an url. This is the main struct that's used to send different URL requests to //different routines. @@ -48,7 +49,7 @@ typedef struct { #define ROUTE_WS(path, callback) ROUTE_CGI_ARG((path), cgiWebsocket, (WsConnectedCb)(callback)) /** Catch-all filesystem route */ -#define ROUTE_FILESYSTEM() ROUTE_CGI("*", cgiEspFsHook) +#define ROUTE_FILESYSTEM() ROUTE_CGI("*", cgiEspFsStaticFile) /** Marker for the end of the route list */ #define ROUTE_END() {NULL, NULL, NULL, NULL} diff --git a/spritehttpd/include/httpd-utils.h b/spritehttpd/include/httpd-utils.h index 7c0bc19..580b412 100644 --- a/spritehttpd/include/httpd-utils.h +++ b/spritehttpd/include/httpd-utils.h @@ -71,11 +71,26 @@ int httpdFindArg(const char *line, const char *arg, char *buff, size_t buffLen); /** * Returns a static char* to a mime type for a given url to a file. * - * @param url - url to parse - * @return mime type string + * @param url - URL or filename to parse + * @return mime type string; NULL if unknown. */ const char *httpdGetMimetype(const char *url); +/** + * Get mimetype or default + * + * @param url - URL or filename to parse + * @param fallback - fallback mime if none was resolved + * @return mime string + */ +static inline const char *httpdGetMimetypeOr(const char *url, const char *fallback) { + const char *mime = httpdGetMimetype(url); + if (!mime) { + mime = fallback; + } + return mime; +} + /** * Turn HTTP method to text * diff --git a/spritehttpd/include/httpd.h b/spritehttpd/include/httpd.h index 7fa89ce..d929a07 100644 --- a/spritehttpd/include/httpd.h +++ b/spritehttpd/include/httpd.h @@ -65,20 +65,11 @@ const char *httpdGetVersion(void); /** - * Use this as a cgi function to redirect one url to another. - */ -httpd_cgi_state cgiRedirect(HttpdConnData *connData); - -/** - * This CGI function redirects to a fixed url of http://[hostname]/ if hostname field of request isn't - * already that hostname. Use this in combination with a DNS server that redirects everything to the - * ESP in order to load a HTML page as soon as a phone, tablet etc connects to the ESP. Watch out: - * this will also redirect connections when the ESP is in STA mode, potentially to a hostname that is not - * in the 'official' DNS and so will fail. + * Set server name (Should not be on stack - the pointer must live as long as the server! Const is preferable.) * - * @param conn - connection + * @param name - new server name */ -httpd_cgi_state cgiRedirectToHostname(HttpdConnData *connData); +void httpdSetName(const char *name); /** * Redirect to the given URL. @@ -271,6 +262,7 @@ size_t httpGetBacklogSize(const HttpdConnData *connData); */ void httdResponseOptions(HttpdConnData *conn, int cors); + //Platform dependent code should call these. /** @@ -313,13 +305,6 @@ void httpdDisconCb(ConnTypePtr conn, const char *remIp, int remPort); */ int httpdConnectCb(ConnTypePtr conn, const char *remIp, int remPort); -/** - * Set server name (Should not be on stack - the pointer must live as long as the server! Const is preferable.) - * - * @param name - new server name - */ -void httpdSetName(const char *name); - /** * Low level function to close & release a connection * diff --git a/spritehttpd/src/cgi-espfs.c b/spritehttpd/src/cgi-espfs.c index cdf9693..150cac9 100644 --- a/spritehttpd/src/cgi-espfs.c +++ b/spritehttpd/src/cgi-espfs.c @@ -135,7 +135,7 @@ static httpd_cgi_state serveStaticFile(HttpdConnData *connData, const char *file connData->cgiData = file; httpdStartResponse(connData, 200); - const char *mime = httpdGetMimetype(filepath); + const char *mime = httpdGetMimetypeOr(filepath, "application/octet-stream"); httpdHeader(connData, "Content-Type", mime); if (isGzip) { httpdHeader(connData, "Content-Encoding", "gzip"); @@ -282,6 +282,13 @@ httpd_cgi_state cgiEspFsTemplate(HttpdConnData *conn) conn->cgiData = tdi; httpdStartResponse(conn, 200); const char *mime = httpdGetMimetype(conn->url); + if (!mime) { + mime = httpdGetMimetype(filepath); + } + if (!mime) { + mime = "text/html"; // this is generally a good fallback for templates + } + httpdHeader(conn, "Content-Type", mime); httpdAddCacheHeaders(conn, mime); httpdEndHeaders(conn); @@ -366,6 +373,10 @@ httpd_cgi_state cgiEspFsTemplate(HttpdConnData *conn) tdi->buff_x = x; break; } + else if (status == HTTPD_CGI_NOTFOUND) { + tdi->tokenPos--; // undo the ++ above + goto put_token_back; + } } //Go collect normal chars again. e = &buff[x + 1]; @@ -374,6 +385,7 @@ httpd_cgi_state cgiEspFsTemplate(HttpdConnData *conn) // Add char to the token buf bool outOfSpace = tdi->tokenPos >= ((int) sizeof(tdi->token) - 1); if (outOfSpace) { + put_token_back: // looks like we collected some garbage, put it back httpdSendStrN(conn, "%", 1); if (tdi->tokenPos > 0) { diff --git a/spritehttpd/src/cgi-redirects.c b/spritehttpd/src/cgi-redirects.c new file mode 100644 index 0000000..137b946 --- /dev/null +++ b/spritehttpd/src/cgi-redirects.c @@ -0,0 +1,66 @@ +#include "httpd.h" +#include "httpd-logging.h" + + +//Use this as a cgi function to redirect one url to another. +httpd_cgi_state cgiRedirect(HttpdConnData *connData) +{ + if (connData->conn == NULL) { + //Connection aborted. Clean up. + return HTTPD_CGI_DONE; + } + httpdRedirect(connData, (char *) connData->cgiArg); + return HTTPD_CGI_DONE; +} + +//Used to spit out a 404 error +httpd_cgi_state cgiNotFound(HttpdConnData *connData) +{ + if (connData->conn == NULL) { return HTTPD_CGI_DONE; } + httpdStartResponse(connData, 404); + httpdEndHeaders(connData); + httpdSendStr(connData, "404 File not found."); + return HTTPD_CGI_DONE; +} + +//This CGI function redirects to a fixed url of http://[hostname]/ if hostname field of request isn't +//already that hostname. Use this in combination with a DNS server that redirects everything to the +//ESP in order to load a HTML page as soon as a phone, tablet etc connects to the ESP. Watch out: +//this will also redirect connections when the ESP is in STA mode, potentially to a hostname that is not +//in the 'official' DNS and so will fail. +httpd_cgi_state cgiRedirectToHostname(HttpdConnData *connData) +{ + static const char hostFmt[] = "http://%s/"; + char *buff; + int isIP = 0; + if (connData->conn == NULL) { + //Connection aborted. Clean up. + return HTTPD_CGI_DONE; + } + if (connData->hostName == NULL) { + http_warn("Huh? No hostname."); + return HTTPD_CGI_NOTFOUND; + } + + //Quick and dirty code to see if host is an IP + if (strlen(connData->hostName) > 8) { + isIP = 1; + for (size_t x = 0; x < strlen(connData->hostName); x++) { + if (connData->hostName[x] != '.' && (connData->hostName[x] < '0' || connData->hostName[x] > '9')) { isIP = 0; } + } + } + if (isIP) { return HTTPD_CGI_NOTFOUND; } + //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)); + if (buff == NULL) { + //Bail out + return HTTPD_CGI_DONE; + } + sprintf(buff, hostFmt, (char *) connData->cgiArg); + http_info("Redirecting to hostname url %s", buff); + httpdRedirect(connData, buff); + httpdPlatFree(buff); + return HTTPD_CGI_DONE; +} diff --git a/spritehttpd/src/httpd-utils.c b/spritehttpd/src/httpd-utils.c index 8921a6a..1cd3647 100644 --- a/spritehttpd/src/httpd-utils.c +++ b/spritehttpd/src/httpd-utils.c @@ -95,7 +95,7 @@ static const MimeMap MIME_TYPES[] = { {"svg", "image/svg+xml"}, {"xml", "text/xml"}, {"json", "application/json"}, - {NULL, "text/html"}, //default value + {NULL, NULL}, //default value }; diff --git a/spritehttpd/src/httpd.c b/spritehttpd/src/httpd.c index 43a1bb3..798ab6a 100644 --- a/spritehttpd/src/httpd.c +++ b/spritehttpd/src/httpd.c @@ -258,69 +258,6 @@ void httpdRedirect(HttpdConnData *conn, const char *newUrl) httpdSendStr(conn, newUrl); } -//Use this as a cgi function to redirect one url to another. -httpd_cgi_state cgiRedirect(HttpdConnData *connData) -{ - if (connData->conn == NULL) { - //Connection aborted. Clean up. - return HTTPD_CGI_DONE; - } - httpdRedirect(connData, (char *) connData->cgiArg); - return HTTPD_CGI_DONE; -} - -//Used to spit out a 404 error -static httpd_cgi_state cgiNotFound(HttpdConnData *connData) -{ - if (connData->conn == NULL) { return HTTPD_CGI_DONE; } - httpdStartResponse(connData, 404); - httpdEndHeaders(connData); - httpdSendStr(connData, "404 File not found."); - return HTTPD_CGI_DONE; -} - -//This CGI function redirects to a fixed url of http://[hostname]/ if hostname field of request isn't -//already that hostname. Use this in combination with a DNS server that redirects everything to the -//ESP in order to load a HTML page as soon as a phone, tablet etc connects to the ESP. Watch out: -//this will also redirect connections when the ESP is in STA mode, potentially to a hostname that is not -//in the 'official' DNS and so will fail. -httpd_cgi_state cgiRedirectToHostname(HttpdConnData *connData) -{ - static const char hostFmt[] = "http://%s/"; - char *buff; - int isIP = 0; - if (connData->conn == NULL) { - //Connection aborted. Clean up. - return HTTPD_CGI_DONE; - } - if (connData->hostName == NULL) { - http_warn("Huh? No hostname."); - return HTTPD_CGI_NOTFOUND; - } - - //Quick and dirty code to see if host is an IP - if (strlen(connData->hostName) > 8) { - isIP = 1; - for (size_t x = 0; x < strlen(connData->hostName); x++) { - if (connData->hostName[x] != '.' && (connData->hostName[x] < '0' || connData->hostName[x] > '9')) { isIP = 0; } - } - } - if (isIP) { return HTTPD_CGI_NOTFOUND; } - //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)); - if (buff == NULL) { - //Bail out - return HTTPD_CGI_DONE; - } - sprintf(buff, hostFmt, (char *) connData->cgiArg); - http_info("Redirecting to hostname url %s", buff); - httpdRedirect(connData, buff); - httpdPlatFree(buff); - return HTTPD_CGI_DONE; -} - //Add data to the send buffer. len is the length of the data. If len is -1 //the data is seen as a C-string.