fixes in templates, better template api

master
Ondřej Hruška 2 years ago
parent b02f28f6f5
commit 4e4a340b4c
  1. 3
      demo/Makefile
  2. 67
      demo/server_demo.c
  3. 19
      demo/staticfiles/template.tpl.txt
  4. 3
      spritehttpd/Makefile
  5. 33
      spritehttpd/include/cgi-redirects.h
  6. 4
      spritehttpd/include/httpd-logging.h
  7. 3
      spritehttpd/include/httpd-routes.h
  8. 19
      spritehttpd/include/httpd-utils.h
  9. 23
      spritehttpd/include/httpd.h
  10. 14
      spritehttpd/src/cgi-espfs.c
  11. 66
      spritehttpd/src/cgi-redirects.c
  12. 2
      spritehttpd/src/httpd-utils.c
  13. 63
      spritehttpd/src/httpd.c

@ -6,7 +6,8 @@ LIBFILE = ../spritehttpd/libspritehttpd.a
STATIC_FILES = \ STATIC_FILES = \
staticfiles/index.html \ staticfiles/index.html \
staticfiles/kocour.jpg staticfiles/kocour.jpg \
staticfiles/template.tpl.txt
all: demo all: demo

@ -4,24 +4,72 @@
#include "httpd.h" #include "httpd.h"
#include "httpd-utils.h" #include "httpd-utils.h"
#include "httpd-espfs.h" #include "cgi-espfs.h"
extern unsigned char espfs_image[]; extern unsigned char espfs_image[];
extern unsigned int espfs_image_len; extern unsigned int espfs_image_len;
httpd_thread_handle_t *s_serverHandle = NULL; httpd_thread_handle_t *s_serverHandle = NULL;
struct SharedData {
int visits;
};
static struct SharedData shared = {};
struct RequestData {
int counter;
};
/** "About" page */ /** "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; } 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;
else if (streq(token, "date")) { shared.visits++;
tplSend(connData, __DATE__, -1); } else {
} else if (streq(token, "time")) { rd = td->userData;
tplSend(connData, __TIME__, -1); }
} else if (streq(token, "vers_httpd")) {
tplSend(connData, httpdGetVersion(), -1); char buf[100];
if (streq(td->token, "visits")) {
sprintf(buf, "%d", shared.visits);
tplSend(td, buf);
}
else if (streq(td->token, "html1")) {
tplSend(td, "<a href=\"foo\">foo</a>");
}
else if (streq(td->token, "html2")) {
tplSend(td, "<a href=\"bar\">bar</a>");
}
else if (streq(td->token, "json1")) {
tplSend(td, "\"hello\":\"foo<angle>'' 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; return HTTPD_CGI_DONE;
@ -37,6 +85,7 @@ const HttpdBuiltInUrl routes[] = {
// --- Web pages --- // --- Web pages ---
// ROUTE_TPL_FILE("/", tplIndex, "/index.tpl"), // ROUTE_TPL_FILE("/", tplIndex, "/index.tpl"),
ROUTE_FILE("/", "index.html"), ROUTE_FILE("/", "index.html"),
ROUTE_TPL_FILE("/tpl", templateReplacer, "template.tpl.txt"),
ROUTE_FILESYSTEM(), ROUTE_FILESYSTEM(),
ROUTE_END(), ROUTE_END(),

@ -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
.

@ -21,10 +21,11 @@ LIB_SOURCES = ${PORT_SOURCES} \
src/utils/base64.c \ src/utils/base64.c \
src/utils/sha1.c \ src/utils/sha1.c \
src/httpd.c \ src/httpd.c \
src/cgi-espfs.c \
src/httpd-auth.c \ src/httpd-auth.c \
src/httpd-utils.c \ src/httpd-utils.c \
src/httpd-loop.c \ src/httpd-loop.c \
src/cgi-espfs.c \
src/cgi-redirects.c \
src/cgi-websocket.c src/cgi-websocket.c
LIB_OBJS = $(LIB_SOURCES:.c=.o) LIB_OBJS = $(LIB_SOURCES:.c=.o)

@ -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);

@ -84,7 +84,7 @@
#endif #endif
#ifndef DEBUG_ESPFS #ifndef DEBUG_ESPFS
#define DEBUG_ESPFS 1 #define DEBUG_ESPFS 0
#endif #endif
#ifndef DEBUG_WS #ifndef DEBUG_WS
@ -100,7 +100,7 @@
#endif #endif
#ifndef DEBUG_HEATSHRINK #ifndef DEBUG_HEATSHRINK
#define DEBUG_HEATSHRINK 1 #define DEBUG_HEATSHRINK 0
#endif #endif
#ifndef DEBUG_MALLOC #ifndef DEBUG_MALLOC

@ -5,6 +5,7 @@
#pragma once #pragma once
#include "cgi-websocket.h" #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 //A struct describing an url. This is the main struct that's used to send different URL requests to
//different routines. //different routines.
@ -48,7 +49,7 @@ typedef struct {
#define ROUTE_WS(path, callback) ROUTE_CGI_ARG((path), cgiWebsocket, (WsConnectedCb)(callback)) #define ROUTE_WS(path, callback) ROUTE_CGI_ARG((path), cgiWebsocket, (WsConnectedCb)(callback))
/** Catch-all filesystem route */ /** Catch-all filesystem route */
#define ROUTE_FILESYSTEM() ROUTE_CGI("*", cgiEspFsHook) #define ROUTE_FILESYSTEM() ROUTE_CGI("*", cgiEspFsStaticFile)
/** Marker for the end of the route list */ /** Marker for the end of the route list */
#define ROUTE_END() {NULL, NULL, NULL, NULL} #define ROUTE_END() {NULL, NULL, NULL, NULL}

@ -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. * Returns a static char* to a mime type for a given url to a file.
* *
* @param url - url to parse * @param url - URL or filename to parse
* @return mime type string * @return mime type string; NULL if unknown.
*/ */
const char *httpdGetMimetype(const char *url); 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 * Turn HTTP method to text
* *

@ -65,20 +65,11 @@
const char *httpdGetVersion(void); const char *httpdGetVersion(void);
/** /**
* Use this as a cgi function to redirect one url to another. * Set server name (Should not be on stack - the pointer must live as long as the server! Const is preferable.)
*/
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.
* *
* @param conn - connection * @param name - new server name
*/ */
httpd_cgi_state cgiRedirectToHostname(HttpdConnData *connData); void httpdSetName(const char *name);
/** /**
* Redirect to the given URL. * Redirect to the given URL.
@ -271,6 +262,7 @@ size_t httpGetBacklogSize(const HttpdConnData *connData);
*/ */
void httdResponseOptions(HttpdConnData *conn, int cors); void httdResponseOptions(HttpdConnData *conn, int cors);
//Platform dependent code should call these. //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); 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 * Low level function to close & release a connection
* *

@ -135,7 +135,7 @@ static httpd_cgi_state serveStaticFile(HttpdConnData *connData, const char *file
connData->cgiData = file; connData->cgiData = file;
httpdStartResponse(connData, 200); httpdStartResponse(connData, 200);
const char *mime = httpdGetMimetype(filepath); const char *mime = httpdGetMimetypeOr(filepath, "application/octet-stream");
httpdHeader(connData, "Content-Type", mime); httpdHeader(connData, "Content-Type", mime);
if (isGzip) { if (isGzip) {
httpdHeader(connData, "Content-Encoding", "gzip"); httpdHeader(connData, "Content-Encoding", "gzip");
@ -282,6 +282,13 @@ httpd_cgi_state cgiEspFsTemplate(HttpdConnData *conn)
conn->cgiData = tdi; conn->cgiData = tdi;
httpdStartResponse(conn, 200); httpdStartResponse(conn, 200);
const char *mime = httpdGetMimetype(conn->url); 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); httpdHeader(conn, "Content-Type", mime);
httpdAddCacheHeaders(conn, mime); httpdAddCacheHeaders(conn, mime);
httpdEndHeaders(conn); httpdEndHeaders(conn);
@ -366,6 +373,10 @@ httpd_cgi_state cgiEspFsTemplate(HttpdConnData *conn)
tdi->buff_x = x; tdi->buff_x = x;
break; break;
} }
else if (status == HTTPD_CGI_NOTFOUND) {
tdi->tokenPos--; // undo the ++ above
goto put_token_back;
}
} }
//Go collect normal chars again. //Go collect normal chars again.
e = &buff[x + 1]; e = &buff[x + 1];
@ -374,6 +385,7 @@ httpd_cgi_state cgiEspFsTemplate(HttpdConnData *conn)
// Add char to the token buf // Add char to the token buf
bool outOfSpace = tdi->tokenPos >= ((int) sizeof(tdi->token) - 1); bool outOfSpace = tdi->tokenPos >= ((int) sizeof(tdi->token) - 1);
if (outOfSpace) { if (outOfSpace) {
put_token_back:
// looks like we collected some garbage, put it back // looks like we collected some garbage, put it back
httpdSendStrN(conn, "%", 1); httpdSendStrN(conn, "%", 1);
if (tdi->tokenPos > 0) { if (tdi->tokenPos > 0) {

@ -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;
}

@ -95,7 +95,7 @@ static const MimeMap MIME_TYPES[] = {
{"svg", "image/svg+xml"}, {"svg", "image/svg+xml"},
{"xml", "text/xml"}, {"xml", "text/xml"},
{"json", "application/json"}, {"json", "application/json"},
{NULL, "text/html"}, //default value {NULL, NULL}, //default value
}; };

@ -258,69 +258,6 @@ void httpdRedirect(HttpdConnData *conn, const char *newUrl)
httpdSendStr(conn, 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 //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. //the data is seen as a C-string.

Loading…
Cancel
Save