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.