improvements in template engine and routes

master
Ondřej Hruška 8 years ago
parent 24a53b306b
commit 39a4e0ef91
  1. 4
      Makefile
  2. 14
      esp_iot_sdk_v1.5.2/include/spi_flash.h
  3. 3
      esp_meas.pro
  4. 2
      esp_meas.pro.user
  5. 4
      esphttpdconfig.mk
  6. 2
      html/chibi.js
  7. 12
      htmlpreview.sh
  8. 4
      htmlserver.sh
  9. 12
      libesphttpd/Makefile
  10. 11
      libesphttpd/core/httpd.c
  11. 87
      libesphttpd/core/httpdespfs.c
  12. 158
      libesphttpd/espfs/espfs.c
  13. 6
      libesphttpd/espfs/espfsformat.h
  14. 25
      libesphttpd/html-minifier-conf.json
  15. 6
      libesphttpd/include/espfs.h
  16. 40
      libesphttpd/include/httpd.h
  17. 8
      libesphttpd/include/httpdespfs.h
  18. 35
      user/user_main.c

@ -15,6 +15,10 @@ ESP_FLASH_MODE=0
ESP_FLASH_FREQ_DIV=0 ESP_FLASH_FREQ_DIV=0
GZIP_COMPRESSION=yes
USE_HEATSHRINK=yes
ifeq ("$(OUTPUT_TYPE)","separate") ifeq ("$(OUTPUT_TYPE)","separate")
#In case of separate ESPFS and binaries, set the pos and length of the ESPFS here. #In case of separate ESPFS and binaries, set the pos and length of the ESPFS here.
ESPFS_POS = 0x18000 ESPFS_POS = 0x18000

@ -1,15 +1,17 @@
/* /*
* copyright (c) Espressif System 2010 * copyright (c) Espressif System 2010
* *
*/ */
#ifndef SPI_FLASH_H #ifndef SPI_FLASH_H
#define SPI_FLASH_H #define SPI_FLASH_H
#include <c_types.h>
typedef enum { typedef enum {
SPI_FLASH_RESULT_OK, SPI_FLASH_RESULT_OK,
SPI_FLASH_RESULT_ERR, SPI_FLASH_RESULT_ERR,
SPI_FLASH_RESULT_TIMEOUT SPI_FLASH_RESULT_TIMEOUT
} SpiFlashOpResult; } SpiFlashOpResult;
typedef struct{ typedef struct{
@ -32,7 +34,7 @@ typedef SpiFlashOpResult (* user_spi_flash_read)(
SpiFlashChip *spi, SpiFlashChip *spi,
uint32 src_addr, uint32 src_addr,
uint32 *des_addr, uint32 *des_addr,
uint32 size); uint32 size);
void spi_flash_set_read_func(user_spi_flash_read read); void spi_flash_set_read_func(user_spi_flash_read read);

@ -3,7 +3,7 @@ CONFIG += console
CONFIG -= app_bundle CONFIG -= app_bundle
CONFIG -= qt CONFIG -= qt
DEFINES = ESPFS_HEATSHRINK DEFINES = ESPFS_HEATSHRINK HTTPD_MAX_CONNECTIONS=4 __ets__
INCLUDEPATH = . \ INCLUDEPATH = . \
esp_iot_sdk_v1.5.2/include \ esp_iot_sdk_v1.5.2/include \
@ -11,6 +11,7 @@ INCLUDEPATH = . \
libesphttpd/include \ libesphttpd/include \
libesphttpd/espfs \ libesphttpd/espfs \
libesphttpd/core \ libesphttpd/core \
libesphttpd/lib/heatshrink \
sbmp sbmp
SOURCES += \ SOURCES += \

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject> <!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 3.6.0, 2016-03-17T20:40:21. --> <!-- Written by QtCreator 3.6.0, 2016-03-18T00:49:39. -->
<qtcreator> <qtcreator>
<data> <data>
<variable>EnvironmentId</variable> <variable>EnvironmentId</variable>

@ -12,13 +12,13 @@
# Adding JPG or PNG files (and any other compressed formats) is not recommended, because GZIP compression does not works effectively on compressed files. # Adding JPG or PNG files (and any other compressed formats) is not recommended, because GZIP compression does not works effectively on compressed files.
#Static gzipping is disabled by default. #Static gzipping is disabled by default.
GZIP_COMPRESSION ?= no GZIP_COMPRESSION ?= yes
# If COMPRESS_W_YUI is set to "yes" then the static css and js files will be compressed with yui-compressor # If COMPRESS_W_YUI is set to "yes" then the static css and js files will be compressed with yui-compressor
# This option works only when GZIP_COMPRESSION is set to "yes" # This option works only when GZIP_COMPRESSION is set to "yes"
# http://yui.github.io/yuicompressor/ # http://yui.github.io/yuicompressor/
#Disabled by default. #Disabled by default.
COMPRESS_W_YUI ?= no COMPRESS_W_YUI ?= yes
YUI-COMPRESSOR ?= /usr/bin/yui-compressor YUI-COMPRESSOR ?= /usr/bin/yui-compressor
#If USE_HEATSHRINK is set to "yes" then the espfs files will be compressed with Heatshrink and decompressed #If USE_HEATSHRINK is set to "yes" then the espfs files will be compressed with Heatshrink and decompressed

File diff suppressed because one or more lines are too long

@ -1,12 +0,0 @@
#!/bin/bash
rm -rf html_preview
cp -rs "$PWD/html" "$PWD/html_preview/"
for file in $(find html_preview/ -name "*.tpl")
do
mv $file `echo $file | sed s/.tpl$/.html/`
done
echo "Html preview updated."

@ -1,4 +0,0 @@
#!/bin/bash
php -S localhost:8266 -t html_preview

@ -7,8 +7,8 @@ THISDIR:=$(dir $(abspath $(lastword $(MAKEFILE_LIST))))
#Default options. If you want to change them, please create ../esphttpdconfig.mk with the options you want in it. #Default options. If you want to change them, please create ../esphttpdconfig.mk with the options you want in it.
GZIP_COMPRESSION ?= no GZIP_COMPRESSION ?= yes
COMPRESS_W_YUI ?= no COMPRESS_W_YUI ?= yes
YUI-COMPRESSOR ?= /usr/bin/yui-compressor YUI-COMPRESSOR ?= /usr/bin/yui-compressor
USE_HEATSHRINK ?= yes USE_HEATSHRINK ?= yes
HTTPD_WEBSOCKETS ?= yes HTTPD_WEBSOCKETS ?= yes
@ -18,6 +18,11 @@ HTTPD_MAX_CONNECTIONS ?= 4
#For FreeRTOS #For FreeRTOS
HTTPD_STACKSIZE ?= 2048 HTTPD_STACKSIZE ?= 2048
# this works only if you also enable YUI-COMPRESSOR
HTML-MINIFIER ?= html-minifier -c html-minifier-conf.json
COMPRESS_W_HTMLMINIFIER ?= yes
# Output directors to store intermediate compiled files # Output directors to store intermediate compiled files
# relative to the project directory # relative to the project directory
BUILD_BASE = build BUILD_BASE = build
@ -162,6 +167,9 @@ ifeq ("$(COMPRESS_W_YUI)","yes")
$(Q) echo "Compression assets with yui-compressor. This may take a while..." $(Q) echo "Compression assets with yui-compressor. This may take a while..."
$(Q) for file in `find html_compressed -type f -name "*.js"`; do $(YUI-COMPRESSOR) --type js $$file -o $$file; done $(Q) for file in `find html_compressed -type f -name "*.js"`; do $(YUI-COMPRESSOR) --type js $$file -o $$file; done
$(Q) for file in `find html_compressed -type f -name "*.css"`; do $(YUI-COMPRESSOR) --type css $$file -o $$file; done $(Q) for file in `find html_compressed -type f -name "*.css"`; do $(YUI-COMPRESSOR) --type css $$file -o $$file; done
ifeq ("$(COMPRESS_W_HTMLMINIFIER)","yes")
$(Q) for file in `find html_compressed -type f -name "*.html" -o -name "*.htm" -o -name "*.tpl"`; do $(HTML-MINIFIER) $$file -o $$file; done
endif
$(Q) awk "BEGIN {printf \"YUI compression ratio was: %.2f%%\\n\", (`du -b -s html_compressed/ | sed 's/\([0-9]*\).*/\1/'`/`du -b -s ../html/ | sed 's/\([0-9]*\).*/\1/'`)*100}" $(Q) awk "BEGIN {printf \"YUI compression ratio was: %.2f%%\\n\", (`du -b -s html_compressed/ | sed 's/\([0-9]*\).*/\1/'`/`du -b -s ../html/ | sed 's/\([0-9]*\).*/\1/'`)*100}"
# mkespfsimage will compress html, css, svg and js files with gzip by default if enabled # mkespfsimage will compress html, css, svg and js files with gzip by default if enabled
# override with -g cmdline parameter # override with -g cmdline parameter

@ -86,10 +86,10 @@ static const ICACHE_RODATA_ATTR MimeMap mimeTypes[]={
}; };
//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.
const char ICACHE_FLASH_ATTR *httpdGetMimetype(char *url) { const char ICACHE_FLASH_ATTR *httpdGetMimetype(const char *url) {
int i=0; int i=0;
//Go find the extension //Go find the extension
char *ext=url+(strlen(url)-1); const char *ext=url+(strlen(url)-1);
while (ext!=url && *ext!='.') ext--; while (ext!=url && *ext!='.') ext--;
if (*ext=='.') ext++; if (*ext=='.') ext++;
@ -99,7 +99,7 @@ const char ICACHE_FLASH_ATTR *httpdGetMimetype(char *url) {
} }
//Looks up the connData info for a specific connection //Looks up the connData info for a specific connection
static HttpdConnData ICACHE_FLASH_ATTR *httpdFindConnData(ConnTypePtr conn, char *remIp, int remPort) { static HttpdConnData ICACHE_FLASH_ATTR *httpdFindConnData(ConnTypePtr conn, const char *remIp, int remPort) {
for (int i=0; i<HTTPD_MAX_CONNECTIONS; i++) { for (int i=0; i<HTTPD_MAX_CONNECTIONS; i++) {
if (connData[i] && connData[i]->remote_port == remPort && if (connData[i] && connData[i]->remote_port == remPort &&
memcmp(connData[i]->remote_ip, remIp, 4) == 0) { memcmp(connData[i]->remote_ip, remIp, 4) == 0) {
@ -154,7 +154,7 @@ int ICACHE_FLASH_ATTR httpdUrlDecode(char *val, int valLen, char *ret, int retLe
esced=2; esced=2;
} else if (esced==2) { } else if (esced==2) {
escVal+=httpdHexVal(val[s]); escVal+=httpdHexVal(val[s]);
ret[d++]=escVal; ret[d++]=(char)escVal;
esced=0; esced=0;
} else if (val[s]=='%') { } else if (val[s]=='%') {
esced=1; esced=1;
@ -481,7 +481,7 @@ void ICACHE_FLASH_ATTR httpdSentCb(ConnTypePtr rconn, char *remIp, int remPort)
httpdCgiIsDone(conn); httpdCgiIsDone(conn);
} }
if (r==HTTPD_CGI_NOTFOUND || r==HTTPD_CGI_AUTHENTICATED) { if (r==HTTPD_CGI_NOTFOUND || r==HTTPD_CGI_AUTHENTICATED) {
httpd_printf("ERROR! CGI fn returns code %d after sending data! Bad CGI!\n", r); error("ERROR! CGI fn returns code %d after sending data! Bad CGI!", r);
httpdCgiIsDone(conn); httpdCgiIsDone(conn);
} }
httpdFlushSendBuffer(conn); httpdFlushSendBuffer(conn);
@ -514,6 +514,7 @@ static void ICACHE_FLASH_ATTR httpdProcessRequest(HttpdConnData *conn) {
conn->cgiData=NULL; conn->cgiData=NULL;
conn->cgi=builtInUrls[i].cgiCb; conn->cgi=builtInUrls[i].cgiCb;
conn->cgiArg=builtInUrls[i].cgiArg; conn->cgiArg=builtInUrls[i].cgiArg;
conn->cgiArg2=builtInUrls[i].cgiArg2;
break; break;
} }
i++; i++;

@ -27,7 +27,8 @@ static const char *gzipNonSupportedMessage = "HTTP/1.0 501 Not implemented\r\n"
"Your browser does not accept gzip-compressed data.\r\n"; "Your browser does not accept gzip-compressed data.\r\n";
EspFsFile *tryOpenIndex(const char *path)
static EspFsFile *tryOpenIndex_do(const char *path, const char *indexname)
{ {
// Try appending index.tpl // Try appending index.tpl
char fname[100]; char fname[100];
@ -41,17 +42,38 @@ EspFsFile *tryOpenIndex(const char *path)
} }
// add index // add index
strcpy(fname + url_len, "index.tpl"); strcpy(fname + url_len, indexname);
return espFsOpen(fname); return espFsOpen(fname);
} }
EspFsFile *tryOpenIndex(const char *path)
{
EspFsFile * file;
//This is a catch-all cgi function. It takes the url passed to it, looks up the corresponding // if there is a dot in the file, assume it's an extension
//path in the filesystem and if it exists, passes the file through. This simulates what a normal // no point in trying to find index in this case, abort.
//webserver would do with static files. if (strchr(path, '.') != NULL) return NULL;
int ICACHE_FLASH_ATTR cgiEspFsHook(HttpdConnData *connData)
// try index.html
file = tryOpenIndex_do(path, "index.html");
if (file != NULL) return file;
// try index.htm
file = tryOpenIndex_do(path, "index.htm");
if (file != NULL) return file;
// try index.tpl
file = tryOpenIndex_do(path, "index.tpl");
if (file != NULL) return file;
return NULL; // failed to guess the right name
}
int ICACHE_FLASH_ATTR serveStaticFile(HttpdConnData *connData, const char* filepath)
{ {
EspFsFile *file = connData->cgiData; EspFsFile *file = connData->cgiData;
int len; int len;
@ -65,18 +87,21 @@ int ICACHE_FLASH_ATTR cgiEspFsHook(HttpdConnData *connData)
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
// invalid call.
if (filepath == NULL) {
printf("serveStaticFile called with NULL path!\n");
return HTTPD_CGI_NOTFOUND;
}
if (file == NULL) { if (file == NULL) {
//First call to this cgi. Open the file so we can read it. //First call to this cgi. Open the file so we can read it.
file = espFsOpen(connData->url); file = espFsOpen(filepath);
if (file == NULL) { if (file == NULL) {
// file not found // file not found
file = tryOpenIndex(connData->url);
if (file == NULL) {
return HTTPD_CGI_NOTFOUND;
}
// If this is a folder, look for index file
file = tryOpenIndex(filepath);
if (file == NULL) return HTTPD_CGI_NOTFOUND;
} }
// The gzip checking code is intentionally without #ifdefs because checking // The gzip checking code is intentionally without #ifdefs because checking
@ -100,7 +125,7 @@ int ICACHE_FLASH_ATTR cgiEspFsHook(HttpdConnData *connData)
connData->cgiData = file; connData->cgiData = file;
httpdStartResponse(connData, 200); httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", httpdGetMimetype(connData->url)); httpdHeader(connData, "Content-Type", httpdGetMimetype(filepath));
if (isGzip) { if (isGzip) {
httpdHeader(connData, "Content-Encoding", "gzip"); httpdHeader(connData, "Content-Encoding", "gzip");
} }
@ -122,6 +147,21 @@ int ICACHE_FLASH_ATTR cgiEspFsHook(HttpdConnData *connData)
} }
//This is a catch-all cgi function. It takes the url passed to it, looks up the corresponding
//path in the filesystem and if it exists, passes the file through. This simulates what a normal
//webserver would do with static files.
int ICACHE_FLASH_ATTR cgiEspFsHook(HttpdConnData *connData)
{
return serveStaticFile(connData, connData->url);
}
int ICACHE_FLASH_ATTR cgiEspFsFile(HttpdConnData *connData)
{
return serveStaticFile(connData, connData->cgiArg);
}
//cgiEspFsTemplate can be used as a template. //cgiEspFsTemplate can be used as a template.
#define TEMPLATE_CHUNK 1024 #define TEMPLATE_CHUNK 1024
@ -164,18 +204,25 @@ int ICACHE_FLASH_ATTR cgiEspFsTemplate(HttpdConnData *connData)
tpd->chunk_resume = false; tpd->chunk_resume = false;
tpd->file = espFsOpen(connData->url);
if (tpd->file == NULL) {
// file not found const char *filepath = connData->url;
tpd->file = tryOpenIndex(connData->url);
// check for custom template URL
if (connData->cgiArg2 != NULL) {
filepath = connData->cgiArg2;
printf("Using filepath %s\n", filepath);
}
tpd->file = espFsOpen(filepath);
if (tpd->file == NULL) {
// If this is a folder, look for index file
tpd->file = tryOpenIndex(filepath);
if (tpd->file == NULL) { if (tpd->file == NULL) {
espFsClose(tpd->file);
free(tpd); free(tpd);
return HTTPD_CGI_NOTFOUND; return HTTPD_CGI_NOTFOUND;
} }
} }
tpd->tplArg = NULL; tpd->tplArg = NULL;
tpd->tokenPos = -1; tpd->tokenPos = -1;
if (espFsFlags(tpd->file) & FLAG_GZIP) { if (espFsFlags(tpd->file) & FLAG_GZIP) {
@ -186,7 +233,7 @@ int ICACHE_FLASH_ATTR cgiEspFsTemplate(HttpdConnData *connData)
} }
connData->cgiData = tpd; connData->cgiData = tpd;
httpdStartResponse(connData, 200); httpdStartResponse(connData, 200);
httpdHeader(connData, "Content-Type", httpdGetMimetype(connData->url)); httpdHeader(connData, "Content-Type", httpdGetMimetype(filepath));
httpdEndHeaders(connData); httpdEndHeaders(connData);
return HTTPD_CGI_MORE; return HTTPD_CGI_MORE;
} }

@ -39,15 +39,15 @@ It's written for use with httpd, but doesn't need to be used as such.
#include "heatshrink_decoder.h" #include "heatshrink_decoder.h"
#endif #endif
static char* espFsData = NULL; static const char* espFsData = NULL;
struct EspFsFile { struct EspFsFile {
EspFsHeader *header; const EspFsHeader *header;
char decompressor; char decompressor;
int32_t posDecomp; int32_t posDecomp;
char *posStart; const char *posStart;
char *posComp; const char *posComp;
void *decompData; void *decompData;
}; };
@ -67,9 +67,10 @@ Accessing the flash through the mem emulation at 0x40200000 is a bit hairy: All
a memory exception, crashing the program. a memory exception, crashing the program.
*/ */
EspFsInitResult ICACHE_FLASH_ATTR espFsInit(void *flashAddress) { EspFsInitResult ICACHE_FLASH_ATTR espFsInit(const void *flashAddress)
if((uint32_t)flashAddress > 0x40200000) { {
flashAddress = (void*)((uint32_t)flashAddress-0x40200000); if ((uint32_t)flashAddress > 0x40200000) {
flashAddress = (void*)((uint32_t)flashAddress - 0x40200000);
} }
// base address must be aligned to 4 bytes // base address must be aligned to 4 bytes
@ -84,7 +85,7 @@ EspFsInitResult ICACHE_FLASH_ATTR espFsInit(void *flashAddress) {
return ESPFS_INIT_RESULT_NO_IMAGE; return ESPFS_INIT_RESULT_NO_IMAGE;
} }
espFsData = (char *)flashAddress; espFsData = (const char *)flashAddress;
return ESPFS_INIT_RESULT_OK; return ESPFS_INIT_RESULT_OK;
} }
@ -93,20 +94,22 @@ EspFsInitResult ICACHE_FLASH_ATTR espFsInit(void *flashAddress) {
//ToDo: perhaps memcpy also does unaligned accesses? //ToDo: perhaps memcpy also does unaligned accesses?
#ifdef __ets__ #ifdef __ets__
void ICACHE_FLASH_ATTR readFlashUnaligned(char *dst, char *src, int len) { void ICACHE_FLASH_ATTR readFlashUnaligned(char *dst, char *src, int len)
{
uint8_t src_offset = ((uint32_t)src) & 3; uint8_t src_offset = ((uint32_t)src) & 3;
uint32_t src_address = ((uint32_t)src) - src_offset; uint32_t src_address = ((uint32_t)src) - src_offset;
uint32_t tmp_buf[len/4 + 2]; uint32_t tmp_buf[len / 4 + 2];
spi_flash_read((uint32)src_address, (uint32*)tmp_buf, len+src_offset); spi_flash_read((uint32)src_address, (uint32*)tmp_buf, len + src_offset);
memcpy(dst, ((uint8_t*)tmp_buf)+src_offset, len); memcpy(dst, ((uint8_t*)tmp_buf) + src_offset, len);
} }
#else #else
#define readFlashUnaligned memcpy #define readFlashUnaligned memcpy
#endif #endif
// Returns flags of opened file. // Returns flags of opened file.
int ICACHE_FLASH_ATTR espFsFlags(EspFsFile *fh) { int ICACHE_FLASH_ATTR espFsFlags(EspFsFile *fh)
{
if (fh == NULL) { if (fh == NULL) {
httpd_printf("File handle not ready\n"); httpd_printf("File handle not ready\n");
return -1; return -1;
@ -118,61 +121,64 @@ int ICACHE_FLASH_ATTR espFsFlags(EspFsFile *fh) {
} }
//Open a file and return a pointer to the file desc struct. //Open a file and return a pointer to the file desc struct.
EspFsFile ICACHE_FLASH_ATTR *espFsOpen(char *fileName) { EspFsFile ICACHE_FLASH_ATTR *espFsOpen(const char *fileName)
{
printf("Open file %s\n", fileName);
if (espFsData == NULL) { if (espFsData == NULL) {
httpd_printf("Call espFsInit first!\n"); httpd_printf("Call espFsInit first!\n");
return NULL; return NULL;
} }
char *p=espFsData; const char *p = espFsData;
char *hpos; const char *hpos;
char namebuf[256]; char namebuf[256];
EspFsHeader h; EspFsHeader h;
EspFsFile *r; EspFsFile *r;
//Strip initial slashes //Strip initial slashes
while(fileName[0]=='/') fileName++; while (fileName[0] == '/') fileName++;
//Go find that file! //Go find that file!
while(1) { while (1) {
hpos=p; hpos = p;
//Grab the next file header. //Grab the next file header.
spi_flash_read((uint32)p, (uint32*)&h, sizeof(EspFsHeader)); spi_flash_read((uint32)p, (uint32*)&h, sizeof(EspFsHeader));
if (h.magic!=ESPFS_MAGIC) { if (h.magic != ESPFS_MAGIC) {
httpd_printf("Magic mismatch. EspFS image broken.\n"); httpd_printf("Magic mismatch. EspFS image broken.\n");
return NULL; return NULL;
} }
if (h.flags&FLAG_LASTFILE) { if (h.flags & FLAG_LASTFILE) {
httpd_printf("End of image.\n"); httpd_printf("File %s not found in EspFS.\n", fileName);
return NULL; return NULL;
} }
//Grab the name of the file. //Grab the name of the file.
p+=sizeof(EspFsHeader); p += sizeof(EspFsHeader);
spi_flash_read((uint32)p, (uint32*)&namebuf, sizeof(namebuf)); spi_flash_read((uint32)p, (uint32*)&namebuf, sizeof(namebuf));
// httpd_printf("Found file '%s'. Namelen=%x fileLenComp=%x, compr=%d flags=%d\n", // httpd_printf("Found file '%s'. Namelen=%x fileLenComp=%x, compr=%d flags=%d\n",
// namebuf, (unsigned int)h.nameLen, (unsigned int)h.fileLenComp, h.compression, h.flags); // namebuf, (unsigned int)h.nameLen, (unsigned int)h.fileLenComp, h.compression, h.flags);
if (strcmp(namebuf, fileName)==0) { if (strcmp(namebuf, fileName) == 0) {
//Yay, this is the file we need! //Yay, this is the file we need!
p+=h.nameLen; //Skip to content. p += h.nameLen; //Skip to content.
r=(EspFsFile *)malloc(sizeof(EspFsFile)); //Alloc file desc mem r = (EspFsFile *)malloc(sizeof(EspFsFile)); //Alloc file desc mem
// httpd_printf("Alloc %p\n", r); // httpd_printf("Alloc %p\n", r);
if (r==NULL) return NULL; if (r == NULL) return NULL;
r->header=(EspFsHeader *)hpos; r->header = (const EspFsHeader *)hpos;
r->decompressor=h.compression; r->decompressor = h.compression;
r->posComp=p; r->posComp = p;
r->posStart=p; r->posStart = p;
r->posDecomp=0; r->posDecomp = 0;
if (h.compression==COMPRESS_NONE) { if (h.compression == COMPRESS_NONE) {
r->decompData=NULL; r->decompData = NULL;
#ifdef ESPFS_HEATSHRINK #ifdef ESPFS_HEATSHRINK
} else if (h.compression==COMPRESS_HEATSHRINK) { } else if (h.compression == COMPRESS_HEATSHRINK) {
//File is compressed with Heatshrink. //File is compressed with Heatshrink.
char parm; char parm;
heatshrink_decoder *dec; heatshrink_decoder *dec;
//Decoder params are stored in 1st byte. //Decoder params are stored in 1st byte.
readFlashUnaligned(&parm, r->posComp, 1); readFlashUnaligned(&parm, (char*)r->posComp, 1);
r->posComp++; r->posComp++;
httpd_printf("Heatshrink compressed file; decode parms = %x\n", parm); httpd_printf("Heatshrink compressed file; decode parms = %x\n", parm);
dec=heatshrink_decoder_alloc(16, (parm>>4)&0xf, parm&0xf); dec = heatshrink_decoder_alloc(16, (parm >> 4) & 0xf, parm & 0xf);
r->decompData=dec; r->decompData = dec;
#endif #endif
} else { } else {
httpd_printf("Invalid compression: %d\n", h.compression); httpd_printf("Invalid compression: %d\n", h.compression);
@ -181,37 +187,38 @@ EspFsFile ICACHE_FLASH_ATTR *espFsOpen(char *fileName) {
return r; return r;
} }
//We don't need this file. Skip name and file //We don't need this file. Skip name and file
p+=h.nameLen+h.fileLenComp; p += h.nameLen + h.fileLenComp;
if ((int)p&3) p+=4-((int)p&3); //align to next 32bit val if ((int)p & 3) p += 4 - ((int)p & 3); //align to next 32bit val
} }
} }
//Read len bytes from the given file into buff. Returns the actual amount of bytes read. //Read len bytes from the given file into buff. Returns the actual amount of bytes read.
int ICACHE_FLASH_ATTR espFsRead(EspFsFile *fh, char *buff, int len) { int ICACHE_FLASH_ATTR espFsRead(EspFsFile *fh, char *buff, int len)
{
int flen, fdlen; int flen, fdlen;
if (fh==NULL) return 0; if (fh == NULL) return 0;
readFlashUnaligned((char*)&flen, (char*)&fh->header->fileLenComp, 4); readFlashUnaligned((char*)&flen, (char*)&fh->header->fileLenComp, 4);
//Cache file length. //Cache file length.
//Do stuff depending on the way the file is compressed. //Do stuff depending on the way the file is compressed.
if (fh->decompressor==COMPRESS_NONE) { if (fh->decompressor == COMPRESS_NONE) {
int toRead; int toRead;
toRead=flen-(fh->posComp-fh->posStart); toRead = flen - (fh->posComp - fh->posStart);
if (len>toRead) len=toRead; if (len > toRead) len = toRead;
// httpd_printf("Reading %d bytes from %x\n", len, (unsigned int)fh->posComp); // httpd_printf("Reading %d bytes from %x\n", len, (unsigned int)fh->posComp);
readFlashUnaligned(buff, fh->posComp, len); readFlashUnaligned(buff, (char*)fh->posComp, len);
fh->posDecomp+=len; fh->posDecomp += len;
fh->posComp+=len; fh->posComp += len;
// httpd_printf("Done reading %d bytes, pos=%x\n", len, fh->posComp); // httpd_printf("Done reading %d bytes, pos=%x\n", len, fh->posComp);
return len; return len;
#ifdef ESPFS_HEATSHRINK #ifdef ESPFS_HEATSHRINK
} else if (fh->decompressor==COMPRESS_HEATSHRINK) { } else if (fh->decompressor == COMPRESS_HEATSHRINK) {
readFlashUnaligned((char*)&fdlen, (char*)&fh->header->fileLenDecomp, 4); readFlashUnaligned((char*)&fdlen, (char*)&fh->header->fileLenDecomp, 4);
int decoded=0; int decoded = 0;
size_t elen, rlen; size_t elen, rlen;
char ebuff[16]; char ebuff[16];
heatshrink_decoder *dec=(heatshrink_decoder *)fh->decompData; heatshrink_decoder *dec = (heatshrink_decoder *)fh->decompData;
// httpd_printf("Alloc %p\n", dec); // httpd_printf("Alloc %p\n", dec);
if (fh->posDecomp == fdlen) { if (fh->posDecomp == fdlen) {
return 0; return 0;
} }
@ -220,26 +227,26 @@ int ICACHE_FLASH_ATTR espFsRead(EspFsFile *fh, char *buff, int len) {
// This means even when there is no input data (elen==0) try to poll decoder until // This means even when there is no input data (elen==0) try to poll decoder until
// posDecomp equals decompressed file length // posDecomp equals decompressed file length
while(decoded<len) { while (decoded < len) {
//Feed data into the decompressor //Feed data into the decompressor
//ToDo: Check ret val of heatshrink fns for errors //ToDo: Check ret val of heatshrink fns for errors
elen=flen-(fh->posComp - fh->posStart); elen = flen - (fh->posComp - fh->posStart);
if (elen>0) { if (elen > 0) {
readFlashUnaligned(ebuff, fh->posComp, 16); readFlashUnaligned(ebuff, (char*)fh->posComp, 16);
heatshrink_decoder_sink(dec, (uint8_t *)ebuff, (elen>16)?16:elen, &rlen); heatshrink_decoder_sink(dec, (uint8_t *)ebuff, (elen > 16) ? 16 : elen, &rlen);
fh->posComp+=rlen; fh->posComp += rlen;
} }
//Grab decompressed data and put into buff //Grab decompressed data and put into buff
heatshrink_decoder_poll(dec, (uint8_t *)buff, len-decoded, &rlen); heatshrink_decoder_poll(dec, (uint8_t *)buff, len - decoded, &rlen);
fh->posDecomp+=rlen; fh->posDecomp += rlen;
buff+=rlen; buff += rlen;
decoded+=rlen; decoded += rlen;
// httpd_printf("Elen %d rlen %d d %d pd %ld fdl %d\n",elen,rlen,decoded, fh->posDecomp, fdlen); // httpd_printf("Elen %d rlen %d d %d pd %ld fdl %d\n",elen,rlen,decoded, fh->posDecomp, fdlen);
if (elen == 0) { if (elen == 0) {
if (fh->posDecomp == fdlen) { if (fh->posDecomp == fdlen) {
// httpd_printf("Decoder finish\n"); // httpd_printf("Decoder finish\n");
heatshrink_decoder_finish(dec); heatshrink_decoder_finish(dec);
} }
return decoded; return decoded;
@ -252,16 +259,17 @@ int ICACHE_FLASH_ATTR espFsRead(EspFsFile *fh, char *buff, int len) {
} }
//Close the file. //Close the file.
void ICACHE_FLASH_ATTR espFsClose(EspFsFile *fh) { void ICACHE_FLASH_ATTR espFsClose(EspFsFile *fh)
if (fh==NULL) return; {
if (fh == NULL) return;
#ifdef ESPFS_HEATSHRINK #ifdef ESPFS_HEATSHRINK
if (fh->decompressor==COMPRESS_HEATSHRINK) { if (fh->decompressor == COMPRESS_HEATSHRINK) {
heatshrink_decoder *dec=(heatshrink_decoder *)fh->decompData; heatshrink_decoder *dec = (heatshrink_decoder *)fh->decompData;
heatshrink_decoder_free(dec); heatshrink_decoder_free(dec);
// httpd_printf("Freed %p\n", dec); // httpd_printf("Freed %p\n", dec);
} }
#endif #endif
// httpd_printf("Freed %p\n", fh); // httpd_printf("Freed %p\n", fh);
free(fh); free(fh);
} }

@ -1,10 +1,12 @@
#ifndef ESPROFSFORMAT_H #ifndef ESPROFSFORMAT_H
#define ESPROFSFORMAT_H #define ESPROFSFORMAT_H
#include <stdint.h>
/* /*
Stupid cpio-like tool to make read-only 'filesystems' that live on the flash SPI chip of the module. Stupid cpio-like tool to make read-only 'filesystems' that live on the flash SPI chip of the module.
Can (will) use lzf compression (when I come around to it) to make shit quicker. Aligns names, files, Can (will) use lzf compression (when I come around to it) to make shit quicker. Aligns names, files,
headers on 4-byte boundaries so the SPI abstraction hardware in the ESP8266 doesn't crap on itself headers on 4-byte boundaries so the SPI abstraction hardware in the ESP8266 doesn't crap on itself
when trying to do a <4byte or unaligned read. when trying to do a <4byte or unaligned read.
*/ */
@ -30,4 +32,4 @@ typedef struct {
int32_t fileLenDecomp; int32_t fileLenDecomp;
} __attribute__((packed)) EspFsHeader; } __attribute__((packed)) EspFsHeader;
#endif #endif

@ -0,0 +1,25 @@
{
"removeComments": true,
"removeCommentsFromCDATA": true,
"removeCDATASectionsFromCDATA": true,
"collapseWhitespace": true,
"conservativeCollapse": false,
"collapseBooleanAttributes": true,
"removeTagWhitespace": true,
"removeAttributeQuotes": true,
"removeRedundantAttributes": true,
"useShortDoctype": true,
"removeEmptyAttributes": true,
"removeScriptTypeAttributes": true,
"removeStyleLinkTypeAttributes": true,
"removeOptionalTags": false,
"removeEmptyElements": false,
"lint": false,
"keepClosingSlash": false,
"caseSensitive": false,
"minifyJS": true,
"minifyCSS": true,
"includeAutoGeneratedTags": false,
"ignoreCustomComments": [],
"processScripts": []
}

@ -13,11 +13,11 @@ typedef enum {
typedef struct EspFsFile EspFsFile; typedef struct EspFsFile EspFsFile;
EspFsInitResult espFsInit(void *flashAddress); EspFsInitResult espFsInit(const void *flashAddress);
EspFsFile *espFsOpen(char *fileName); EspFsFile *espFsOpen(const char *fileName);
int espFsFlags(EspFsFile *fh); int espFsFlags(EspFsFile *fh);
int espFsRead(EspFsFile *fh, char *buff, int len); int espFsRead(EspFsFile *fh, char *buff, int len);
void espFsClose(EspFsFile *fh); void espFsClose(EspFsFile *fh);
#endif #endif

@ -26,9 +26,15 @@ struct HttpdConnData {
char requestType; // One of the HTTPD_METHOD_* values char requestType; // One of the HTTPD_METHOD_* values
char *url; // The URL requested, without hostname or GET arguments char *url; // The URL requested, without hostname or GET arguments
char *getArgs; // The GET arguments for this request, if any. char *getArgs; // The GET arguments for this request, if any.
const void *cgiArg; // Argument to the CGI function, as stated as the 3rd argument of const void *cgiArg; // Argument to the CGI function, as stated as the 3rd argument of
// the builtInUrls entry that referred to the CGI function. // the builtInUrls entry that referred to the CGI function.
const void *cgiArg2; // Argument to the CGI function, as stated as the 4th argument of
// the builtInUrls entry that referred to the CGI function.
void *cgiData; // Opaque data pointer for the CGI function void *cgiData; // Opaque data pointer for the CGI function
char *hostName; // Host name field of request char *hostName; // Host name field of request
HttpdPriv *priv; // Opaque pointer to data for internal httpd housekeeping HttpdPriv *priv; // Opaque pointer to data for internal httpd housekeeping
cgiSendCallback cgi; // CGI function pointer cgiSendCallback cgi; // CGI function pointer
@ -55,8 +61,33 @@ typedef struct {
const char *url; const char *url;
cgiSendCallback cgiCb; cgiSendCallback cgiCb;
const void *cgiArg; const void *cgiArg;
const void *cgiArg2;
} HttpdBuiltInUrl; } HttpdBuiltInUrl;
// macros for defining HttpdBuiltInUrl's
#define ROUTE_CGI_ARG2(path, handler, arg1, arg2) {path, handler, (void *)arg1, (void *)arg2}
#define ROUTE_CGI_ARG(path, handler, arg1) ROUTE_CGI_ARG2(path, handler, arg1, NULL)
#define ROUTE_CGI(path, handler) ROUTE_CGI_ARG2(path, handler, NULL, NULL)
#define ROUTE_FILE(path, filepath) ROUTE_CGI_ARG(path, cgiEspFsStaticFile, filepath)
// the argument of a template route is accessible as cgiArg2 on the connData struct.
#define ROUTE_TPL(path, replacer) ROUTE_CGI_ARG(path, cgiEspFsTemplate, replacer)
#define ROUTE_TPL_FILE(path, replacer, filepath) ROUTE_CGI_ARG2(path, cgiEspFsTemplate, replacer, filepath)
#define ROUTE_REDIRECT(path, target) ROUTE_CGI_ARG(path, cgiRedirect, target)
#define ROUTE_AUTH(path, passwdFunc) ROUTE_CGI_ARG(path, authBasic, passwdFunc)
// catch-all route
#define ROUTE_FS(path) ROUTE_CGI(path, cgiEspFsHook)
#define ROUTE_END() {NULL, NULL, NULL, NULL}
int cgiRedirect(HttpdConnData *connData); int cgiRedirect(HttpdConnData *connData);
int cgiRedirectToHostname(HttpdConnData *connData); int cgiRedirectToHostname(HttpdConnData *connData);
int cgiRedirectApClientToHostname(HttpdConnData *connData); int cgiRedirectApClientToHostname(HttpdConnData *connData);
@ -64,7 +95,7 @@ void httpdRedirect(HttpdConnData *conn, char *newUrl);
int httpdUrlDecode(char *val, int valLen, char *ret, int retLen); int httpdUrlDecode(char *val, int valLen, char *ret, int retLen);
int httpdFindArg(char *line, char *arg, char *buff, int buffLen); int httpdFindArg(char *line, char *arg, char *buff, int buffLen);
void httpdInit(HttpdBuiltInUrl *fixedUrls, int port); void httpdInit(HttpdBuiltInUrl *fixedUrls, int port);
const char *httpdGetMimetype(char *url); const char *httpdGetMimetype(const char *url);
void httpdDisableTransferEncoding(HttpdConnData *conn); void httpdDisableTransferEncoding(HttpdConnData *conn);
void httpdStartResponse(HttpdConnData *conn, int code); void httpdStartResponse(HttpdConnData *conn, int code);
void httpdHeader(HttpdConnData *conn, const char *field, const char *val); void httpdHeader(HttpdConnData *conn, const char *field, const char *val);
@ -80,4 +111,11 @@ void httpdDisconCb(ConnTypePtr conn, char *remIp, int remPort);
int httpdConnectCb(ConnTypePtr conn, char *remIp, int remPort); int httpdConnectCb(ConnTypePtr conn, char *remIp, int remPort);
// debugging
#define LOG_EOL "\n"
#define dbg(fmt, ...) httpd_printf(fmt LOG_EOL, ##__VA_ARGS__);
#define error(fmt, ...) httpd_printf("\x1b[31;1m"fmt"\x1b[0m"LOG_EOL, ##__VA_ARGS__);
#define info(fmt, ...) httpd_printf("\x1b[32;1m"fmt"\x1b[0m"LOG_EOL, ##__VA_ARGS__);
#endif #endif

@ -3,7 +3,13 @@
#include "httpd.h" #include "httpd.h"
/** Catch-all, use in '*' routes */
int cgiEspFsHook(HttpdConnData *connData); int cgiEspFsHook(HttpdConnData *connData);
/** Template route */
int ICACHE_FLASH_ATTR cgiEspFsTemplate(HttpdConnData *connData); int ICACHE_FLASH_ATTR cgiEspFsTemplate(HttpdConnData *connData);
#endif /** Static file route with the file as the first arg. */
int ICACHE_FLASH_ATTR cgiEspFsFile(HttpdConnData *connData);
#endif

@ -107,27 +107,28 @@ CgiUploadFlashDef uploadParams = {
* general ones. Authorization things (like authBasic) act as a 'barrier' and * general ones. Authorization things (like authBasic) act as a 'barrier' and
* should be placed above the URLs they protect. * should be placed above the URLs they protect.
*/ */
static HttpdBuiltInUrl builtInUrls[] = {
{"*", cgiRedirectApClientToHostname, "esp8266.nonet"}, // redirect func for the captive portal
{"/", cgiEspFsTemplate, (void *)tplCounter},
{"/multipart.tpl", cgiEspFsTemplate, (void *)tplMultipart},
// {"/random.tpl", cgiRandomNumbers, NULL}, static HttpdBuiltInUrl builtInUrls[] = {
ROUTE_CGI_ARG("*", cgiRedirectApClientToHostname, "esp8266.nonet"), // redirect func for the captive portal
ROUTE_TPL("/", tplCounter),
ROUTE_TPL_FILE("/multipart", tplMultipart, "/multipart.tpl"),
//Enable the line below to protect the WiFi configuration with an username/password combo. //Enable the line below to protect the WiFi configuration with an username/password combo.
// {"/wifi/*", authBasic, (void *)myPassFn}, // ROUTE_AUTH("/wifi/*", myPassFn),
{"/wifi", cgiRedirect, "/wifi/"}, ROUTE_REDIRECT("/wifi", "/wifi/"),
{"/wifi/", cgiEspFsTemplate, (void *)tplWlan}, ROUTE_TPL_FILE("/wifi/", tplWlan, "/wifi/index.tpl"),
//{"/wifi/", cgiRedirect, "/wifi/wifi.tpl"},
{"/wifi/wifiscan.cgi", cgiWiFiScan, NULL}, ROUTE_CGI("/wifi/wifiscan.cgi", cgiWiFiScan),
{"/wifi/connect.cgi", cgiWiFiConnect, NULL}, ROUTE_CGI("/wifi/connect.cgi", cgiWiFiConnect),
{"/wifi/connstatus.cgi", cgiWiFiConnStatus, NULL}, ROUTE_CGI("/wifi/connstatus.cgi", cgiWiFiConnStatus),
{"/wifi/setmode.cgi", cgiWiFiSetMode, NULL}, ROUTE_CGI("/wifi/setmode.cgi", cgiWiFiSetMode),
{"*", cgiEspFsHook, NULL}, //Catch-all cgi function for the filesystem ROUTE_FS("*"), //Catch-all cgi function for the filesystem
{NULL, NULL, NULL}
ROUTE_END()
}; };

Loading…
Cancel
Save