add buildable demo, heatshrink kinda broken

master
Ondřej Hruška 2 years ago
parent 26867f4767
commit aa0754d302
  1. 27
      Makefile
  2. 7
      demo/.gitignore
  3. 35
      demo/Makefile
  4. 27
      demo/server_demo.c
  5. 10
      demo/staticfiles/index.html
  6. BIN
      demo/staticfiles/kocour.jpg
  7. 11
      espfsbuilder/Makefile
  8. 7
      espfsbuilder/README.md
  9. 184
      espfsbuilder/main.c
  10. 67
      espfsbuilder/parsing.c
  11. 7
      espfsbuilder/parsing.h
  12. 5
      spritehttpd/.gitignore
  13. 51
      spritehttpd/Makefile
  14. 0
      spritehttpd/include/auth.h
  15. 0
      spritehttpd/include/cgiwebsocket.h
  16. 0
      spritehttpd/include/httpd-platform.h
  17. 0
      spritehttpd/include/httpd-utils.h
  18. 0
      spritehttpd/include/httpd.h
  19. 0
      spritehttpd/include/httpdespfs.h
  20. 0
      spritehttpd/include/logging.h
  21. 9
      spritehttpd/lib/espfs/espfs.c
  22. 1
      spritehttpd/lib/espfs/espfs.h
  23. 2
      spritehttpd/lib/espfs/espfsformat.h
  24. 0
      spritehttpd/lib/heatshrink/heatshrink_common.h
  25. 0
      spritehttpd/lib/heatshrink/heatshrink_config.h
  26. 0
      spritehttpd/lib/heatshrink/heatshrink_decoder.c
  27. 0
      spritehttpd/lib/heatshrink/heatshrink_decoder.h
  28. 22
      spritehttpd/lib/heatshrink/heatshrink_encoder.c
  29. 2
      spritehttpd/lib/heatshrink/heatshrink_encoder.h
  30. 0
      spritehttpd/src/cgiwebsocket.c
  31. 16
      spritehttpd/src/httpd-loop.c
  32. 0
      spritehttpd/src/httpd.c
  33. 5
      spritehttpd/src/httpdespfs.c
  34. 0
      spritehttpd/src/port/httpd-freertos.c
  35. 0
      spritehttpd/src/port/httpd-posix.c
  36. 0
      spritehttpd/src/utils/base64.c
  37. 0
      spritehttpd/src/utils/base64.h
  38. 0
      spritehttpd/src/utils/sha1.c
  39. 0
      spritehttpd/src/utils/sha1.h
  40. 0
      spritehttpd/todo/esphttpclient/LICENSE
  41. 0
      spritehttpd/todo/esphttpclient/README.md
  42. 0
      spritehttpd/todo/esphttpclient/httpclient.c
  43. 0
      spritehttpd/todo/httpclient.h
  44. 0
      spritehttpd/todo/uptime.c
  45. 0
      spritehttpd/todo/uptime.h
  46. 0
      spritehttpd/todo/webpages-espfs.h
  47. 0
      spritehttpd/todo/webpages.espfs.ld

@ -1,21 +1,14 @@
PORT_SOURCES = lib/src/port/httpd-posix.c .PHONY: all clean espfsbuilder demo lib
LIB_SOURCES = ${PORT_SOURCES} \ all: demo espfsbuilder
lib/src/utils/base64.c \
lib/src/utils/sha1.c \
lib/espfs/espfs.c \
lib/src/httpdespfs.c \
lib/src/httpd.c \
lib/src/httpd-loop.c \
lib/src/cgiwebsocket.c \
lib/heatshrink/heatshrink_decoder.c
LIB_INCLUDES = -Ilib/include -Ilib/heatshrink -Ilib/espfs clean:
make -C ./spritehttpd clean
make -C ./demo clean
make -C ./espfsbuilder clean
DEMO_SOURCES = main.c espfsbuilder:
make -C ./espfsbuilder
demo:
all: demo make -C ./demo
demo: ${LIB_SOURCES} ${DEMO_SOURCES}
cc -g $^ -o $@ ${LIB_INCLUDES}

7
demo/.gitignore vendored

@ -0,0 +1,7 @@
demo
*.o
.idea/
*.bin
*.a
staticfiles-embed.c
staticfiles.bin

@ -0,0 +1,35 @@
DEMO_SOURCES = server_demo.c staticfiles-embed.c
FSBUILDER = ../espfsbuilder/mkespfsimage
LIBFILE = ../spritehttpd/libspritehttpd.a
STATIC_FILES = \
staticfiles/index.html \
staticfiles/kocour.jpg
all: demo
$(FSBUILDER):
make -C ../espfsbuilder
staticfiles.bin: $(FSBUILDER) ${STATIC_FILES}
$(FSBUILDER) -c1 -g jpg --strip-path staticfiles -o $@ ${STATIC_FILES}
$(FSBUILDER) -p $@
staticfiles-embed.c: staticfiles.bin
xxd -i -n espfs_image $< $@
clean:
rm -f demo staticfiles.bin staticfiles-embed.c
make -C ../spritehttpd
$(LIBFILE):
make -C ../spritehttpd PLATFORM=posix CFLAGS="-Og -g"
demo: ${DEMO_SOURCES} $(LIBFILE)
cc -Og -g -Wall ${DEMO_SOURCES} \
-o $@ \
-I../spritehttpd/include \
-lspritehttpd \
-L../spritehttpd/

@ -3,14 +3,17 @@
#include "httpd-utils.h" #include "httpd-utils.h"
#include "httpdespfs.h" #include "httpdespfs.h"
#include <httpd.h> #include <httpd.h>
#include <cgiwebsocket.h> #include <cgiwebsocket.h>
#include <httpdespfs.h> #include <httpdespfs.h>
#include <auth.h> #include <auth.h>
#include <signal.h>
#include "logging.h" #include "logging.h"
extern unsigned char espfs_image[];
extern unsigned int espfs_image_len;
/** "About" page */ /** "About" page */
httpd_cgi_state tplIndex(HttpdConnData *connData, char *token, void **arg) httpd_cgi_state tplIndex(HttpdConnData *connData, char *token, void **arg)
{ {
@ -35,18 +38,26 @@ const HttpdBuiltInUrl routes[] = {
// TODO password lock ... // TODO password lock ...
// --- Web pages --- // --- Web pages ---
ROUTE_TPL_FILE("/", tplIndex, "/index.tpl"), // ROUTE_TPL_FILE("/", tplIndex, "/index.tpl"),
ROUTE_FILE("/", "index.html"),
ROUTE_FILESYSTEM(), ROUTE_FILESYSTEM(),
ROUTE_END(), ROUTE_END(),
}; };
void sigpipe_handler(int unused)
{
}
int main() int main()
{ {
printf("Hello, World!\n"); printf("Hello, World!\n");
// prevent abort on sigpipe
sigaction(SIGPIPE, &(struct sigaction) {sigpipe_handler}, NULL);
struct httpd_options opts = { struct httpd_options opts = {
.port = 80, .port = 8080,
}; };
httpd_thread_handle_t *handle = httpdInit(routes, &opts); httpd_thread_handle_t *handle = httpdInit(routes, &opts);
@ -56,3 +67,13 @@ int main()
return 0; return 0;
} }
int httpdPlatEspfsRead(void *dest, uint32_t offset, size_t len)
{
if (offset + len > espfs_image_len) {
return -1;
}
memcpy(dest, &espfs_image[offset], len);
return 0;
}

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SpriteHTTPD demo</title>
</head>
<body>
<img src="kocour.jpg" width="500">
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

@ -1,7 +1,14 @@
TARGET=mkespfsimage TARGET=mkespfsimage
SOURCES = main.c ../lib/heatshrink/heatshrink_encoder.c ../lib/heatshrink/heatshrink_decoder.c ../lib/espfs/espfs.c SOURCES = main.c parsing.c \
CFLAGS = -I. -I../lib/heatshrink/ -I../lib/espfs/ ../spritehttpd/lib/heatshrink/heatshrink_encoder.c \
../spritehttpd/lib/heatshrink/heatshrink_decoder.c \
../spritehttpd/lib/espfs/espfs.c
CFLAGS = -I. \
-I../spritehttpd/lib/heatshrink/ \
-I../spritehttpd/lib/espfs/ \
-DZLIB_CONST
all: $(TARGET) all: $(TARGET)

@ -0,0 +1,7 @@
This is a command-line utility that builds the binary espfs image.
The tool can also parse the espfs image file for verification.
It uses heatshrink files & the config header from the main project.
The binary can be embedded in the fileserver project e.g. using `xxd -i` to convert it to a C source file
that can be `#include`'d.

@ -11,12 +11,10 @@
#include "espfsformat.h" #include "espfsformat.h"
#include "heatshrink_encoder.h" #include "heatshrink_encoder.h"
#include "espfs.h" #include "parsing.h"
#define DEFAULT_GZIP_EXTS "html,css,js,svg,png,jpg,gif" #define DEFAULT_GZIP_EXTS "html,css,js,svg,png,jpg,gif"
// static variables
static int s_outFd = 1;
struct InputFileLinkedListEntry; struct InputFileLinkedListEntry;
struct InputFileLinkedListEntry { struct InputFileLinkedListEntry {
@ -24,10 +22,16 @@ struct InputFileLinkedListEntry {
struct InputFileLinkedListEntry *next; struct InputFileLinkedListEntry *next;
}; };
/// two ends of a linked list with input files
static struct InputFileLinkedListEntry *s_inputFiles = NULL; static struct InputFileLinkedListEntry *s_inputFiles = NULL;
static struct InputFileLinkedListEntry *s_lastInputFile = NULL; static struct InputFileLinkedListEntry *s_lastInputFile = NULL;
/// Output file FD
static int s_outFd = 1;
/// Array of gzipped extensions, ends with a NULL pointer
static char **s_gzipExtensions = NULL; static char **s_gzipExtensions = NULL;
/// Gzip all files
static bool s_gzipAll = false; static bool s_gzipAll = false;
// impls to satisfy defs in the config header // impls to satisfy defs in the config header
@ -41,9 +45,19 @@ void httpdPlatFree(void *ptr)
free(ptr); free(ptr);
} }
size_t compressHeatshrink(uint8_t *in, size_t insize, uint8_t *out, size_t outcap, int level) /**
* Compress a file using Heatshrink
*
* @param[in] in - pointer to the uncompressed input
* @param insize - len of the uncompressed input
* @param[out] out - destination buffer for the compressed data
* @param outcap - capacity of the output buffer
* @param level - compression level, 1-9; -1 for default.
* @return actual length of the compressed data
*/
size_t compressHeatshrink(const uint8_t *in, size_t insize, uint8_t *out, size_t outcap, int level)
{ {
uint8_t *inp = in; const uint8_t *inp = in;
uint8_t *outp = out; uint8_t *outp = out;
size_t len; size_t len;
int ws[] = {5, 6, 8, 11, 13}; int ws[] = {5, 6, 8, 11, 13};
@ -82,7 +96,7 @@ size_t compressHeatshrink(uint8_t *in, size_t insize, uint8_t *out, size_t outca
} while (insize != 0); } while (insize != 0);
if (insize != 0) { if (insize != 0) {
fprintf(stderr, "Heatshrink: Bug? insize is still %d. sres=%d pres=%d\n", (int)insize, sres, pres); fprintf(stderr, "Heatshrink: Bug? insize is still %d. sres=%d pres=%d\n", (int) insize, sres, pres);
exit(1); exit(1);
} }
@ -90,7 +104,17 @@ size_t compressHeatshrink(uint8_t *in, size_t insize, uint8_t *out, size_t outca
return r; return r;
} }
size_t compressGzip(uint8_t *in, size_t insize, uint8_t *out, size_t outsize, int level) /**
* Compress a file using Gzip
*
* @param[in] in - pointer to the uncompressed input
* @param insize - len of the uncompressed input
* @param[out] out - destination buffer for the compressed data
* @param outcap - capacity of the output buffer
* @param level - compression level, 1-9; -1 for default.
* @return actual length of the compressed data
*/
size_t compressGzip(const uint8_t *in, size_t insize, uint8_t *out, size_t outcap, int level)
{ {
z_stream stream; z_stream stream;
int zresult; int zresult;
@ -101,7 +125,7 @@ size_t compressGzip(uint8_t *in, size_t insize, uint8_t *out, size_t outsize, in
stream.next_in = in; stream.next_in = in;
stream.avail_in = insize; stream.avail_in = insize;
stream.next_out = out; stream.next_out = out;
stream.avail_out = outsize; stream.avail_out = outcap;
// 31 -> 15 window bits + 16 for gzip // 31 -> 15 window bits + 16 for gzip
zresult = deflateInit2(&stream, level, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY); zresult = deflateInit2(&stream, level, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);
if (zresult != Z_OK) { if (zresult != Z_OK) {
@ -124,6 +148,12 @@ size_t compressGzip(uint8_t *in, size_t insize, uint8_t *out, size_t outsize, in
return stream.total_out; return stream.total_out;
} }
/**
* Check if a file name should be compressed by gzip
*
* @param name - file name
* @return true if should compress
*/
bool shouldCompressGzip(const char *name) bool shouldCompressGzip(const char *name)
{ {
if (!s_gzipExtensions) { return false; } if (!s_gzipExtensions) { return false; }
@ -150,7 +180,13 @@ bool shouldCompressGzip(const char *name)
return false; return false;
} }
int parseGzipExtensions(char *input) /**
* Parse a list of gzipped extensions
*
* @param input - list of comma-separated extensions, e.g. "jpg,png"
* @return
*/
void parseGzipExtensions(char *input)
{ {
char *token; char *token;
char *extList = input; char *extList = input;
@ -173,19 +209,17 @@ int parseGzipExtensions(char *input)
} }
// terminate list // terminate list
s_gzipExtensions[count] = NULL; s_gzipExtensions[count] = NULL;
return 1;
} }
/** /**
* Process a file. * Process a file.
* *
* @param fd - filedes * @param fd - filedes
* @param name - filename to embed in the archive * @param[in] name - filename to embed in the archive
* @param compression_mode - compression mode * @param compression_mode - compression mode
* @param level - compression level for heatshrink, 1-9 * @param level - compression level for heatshrink, 1-9
* @param[out] compName - the used compression is output here (for debug print) * @param[out] compName - the used compression is output here (for debug print)
* @return * @return - size of the output, in percent (100% = no compression)
*/ */
int handleFile(int fd, const char *name, int compression_mode, int level, const char **compName) int handleFile(int fd, const char *name, int compression_mode, int level, const char **compName)
{ {
@ -282,11 +316,13 @@ int handleFile(int fd, const char *name, int compression_mode, int level, const
return size ? (int) ((csize * 100) / size) : 100; return size ? (int) ((csize * 100) / size) : 100;
} }
//Write final dummy header with FLAG_LASTFILE set. /**
* Write final dummy header with FLAG_LASTFILE set.
*/
void finishArchive() void finishArchive()
{ {
EspFsHeader h; EspFsHeader h;
h.magic = htole32(ESPFS_MAGIC); // ('E' << 0) + ('S' << 8) + ('f' << 16) + ('s' << 24); h.magic = htole32(ESPFS_MAGIC);
h.flags = FLAG_LASTFILE; h.flags = FLAG_LASTFILE;
h.compression = COMPRESS_NONE; h.compression = COMPRESS_NONE;
h.nameLen = 0; h.nameLen = 0;
@ -295,62 +331,13 @@ void finishArchive()
write(s_outFd, &h, sizeof(EspFsHeader)); write(s_outFd, &h, sizeof(EspFsHeader));
} }
static size_t espfs_parse_filesize = -1; /**
static int espfs_parse_fd = -1; * Queue a file for adding to the archive.
* Appends it to the `s_inputFiles` linked list.
void parseEspfsFileAndShowItsContents(const char *filename) *
{ * @param name - file name to add
int rv; */
fprintf(stderr, "Parsing: %s\n", filename); void queueInputFile(const char *name)
FILE *f = fopen(filename, "r");
if (!f) {
perror(filename);
exit(1);
}
int fd = fileno(f);
espfs_parse_filesize = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
espfs_parse_fd = fd;
rv = espFsInit();
if (rv != 0) {
fprintf(stderr, "Fail to init FS\n");
exit(1);
}
EspFsWalk walk;
espFsWalkInit(&walk);
EspFsHeader header;
uint32_t offset;
char namebuf[1024];
while (espFsWalkNext(&walk, &header, namebuf, 1024, &offset)) {
fprintf(stderr, "at %04x: \"%s\", flags: %02x, comp: %s, compLen: %d, plainLen: %d\n", offset, namebuf, header.flags,
header.compression == 1 ? "HS" : "None", header.fileLenComp, header.fileLenDecomp);
}
fclose(f);
}
int httpdPlatEspfsRead(void *dest, uint32_t offset, size_t len)
{
fprintf(stderr, "FS read @ %d, len %d\n", offset, len);
if (offset + len > espfs_parse_filesize) {
fprintf(stderr, "Read out fo range!\n");
return -1;
}
lseek(espfs_parse_fd, offset, SEEK_SET);
read(espfs_parse_fd, dest, len);
return 0;
}
void queueInputFile(char *name)
{ {
fprintf(stderr, "INFILE: %s\n", name); fprintf(stderr, "INFILE: %s\n", name);
@ -385,20 +372,22 @@ int main(int argc, char **argv)
int c; int c;
char *outfile = NULL; char *outfile = NULL;
char *parseFile = NULL; char *parseFile = NULL;
char *stripPath = NULL;
while (1) { while (1) {
int option_index = 0; int option_index = 0;
static struct option long_options[] = { static struct option long_options[] = {
{"parse", required_argument, 0, 'p'}, {"parse", required_argument, 0, 'p'},
{"compress", required_argument, 0, 'c'}, {"compress", required_argument, 0, 'c'},
{"gzip", no_argument, 0, 'z'}, {"gzip", no_argument, 0, 'z'},
{"gzip-all", no_argument, 0, 'G'}, {"gzip-all", no_argument, 0, 'G'},
{"level", required_argument, 0, 'l'}, {"level", required_argument, 0, 'l'},
{"gzip-exts", required_argument, 0, 'g'}, {"gzip-exts", required_argument, 0, 'g'},
{"input", required_argument, 0, 'i'}, {"input", required_argument, 0, 'i'},
{"output", required_argument, 0, 'o'}, {"output", required_argument, 0, 'o'},
{"help", no_argument, 0, 'h'}, {"strip-path", required_argument, 0, 'S'},
{0, 0, 0, 0} {"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
}; };
c = getopt_long(argc, argv, "c:l:g:zGhp:i:o:0123456789", c = getopt_long(argc, argv, "c:l:g:zGhp:i:o:0123456789",
@ -423,6 +412,10 @@ int main(int argc, char **argv)
parseFile = strdup(optarg); parseFile = strdup(optarg);
break; break;
case 'S':
stripPath = strdup(optarg);
break;
case 'c': case 'c':
compType = atoi(optarg); compType = atoi(optarg);
break; break;
@ -434,11 +427,7 @@ int main(int argc, char **argv)
case 'g': case 'g':
use_gzip = true; use_gzip = true;
if (!parseGzipExtensions(optarg)) { parseGzipExtensions(optarg);
fprintf(stderr, "Bad gzip extension list: %s\n", optarg);
err = 1;
goto show_help;
}
break; break;
case 'l': case 'l':
@ -473,7 +462,6 @@ int main(int argc, char **argv)
exit(0); exit(0);
} }
if (s_gzipExtensions == NULL && use_gzip) { if (s_gzipExtensions == NULL && use_gzip) {
parseGzipExtensions(strdup(DEFAULT_GZIP_EXTS)); parseGzipExtensions(strdup(DEFAULT_GZIP_EXTS));
} }
@ -514,14 +502,24 @@ int main(int argc, char **argv)
serr = stat(name, &statBuf); serr = stat(name, &statBuf);
if ((serr == 0) && S_ISREG(statBuf.st_mode)) { if ((serr == 0) && S_ISREG(statBuf.st_mode)) {
//Strip off './' or '/' madness. //Strip off './' or '/' madness.
realName = name; char *embeddedName = name;
if (name[0] == '.' && name[1] == '/') { realName += 2; }
if (realName[0] == '/') { realName++; }
f = open(name, O_RDONLY); f = open(name, O_RDONLY);
// relative path starting with ./, remove that
if (embeddedName[0] == '.' && embeddedName[1] == '/') {
embeddedName += 2;
}
// remove prefix
if (stripPath && 0 == strncmp(embeddedName, stripPath, strlen(stripPath))) {
embeddedName += strlen(stripPath);
}
// remove leading slash, if any
if (embeddedName[0] == '/') { embeddedName++; }
if (f > 0) { if (f > 0) {
const char *compName = "unknown"; const char *compName = "unknown";
rate = handleFile(f, realName, compType, compLvl, &compName); rate = handleFile(f, embeddedName, compType, compLvl, &compName);
fprintf(stderr, "%s (%d%%, %s)\n", realName, rate, compName); fprintf(stderr, "%s (%d%%, %s)\n", embeddedName, rate, compName);
close(f); close(f);
} else { } else {
perror(name); perror(name);
@ -550,7 +548,7 @@ int main(int argc, char **argv)
fprintf(stderr, "[-c|--compress COMPRESSOR]\n 0 - None, 1 - Heatshrink (default)\n"); fprintf(stderr, "[-c|--compress COMPRESSOR]\n 0 - None, 1 - Heatshrink (default)\n");
fprintf(stderr, "[-l|--level LEVEL] or [-0 through -9]\n compression level 1-9, higher is better but uses more RAM\n"); fprintf(stderr, "[-l|--level LEVEL] or [-0 through -9]\n compression level 1-9, higher is better but uses more RAM\n");
fprintf(stderr, "[-z|--gzip]\n use gzip for files with extensions matching "DEFAULT_GZIP_EXTS"\n"); fprintf(stderr, "[-z|--gzip]\n use gzip for files with extensions matching "DEFAULT_GZIP_EXTS"\n");
fprintf(stderr, "[-Z|--gzip-all]\n use gzip for all files\n"); fprintf(stderr, "[-G|--gzip-all]\n use gzip for all files\n");
fprintf(stderr, "[-g|--gzip-exts GZIPPED_EXTENSIONS]\n use gzip for files with custom extensions, comma-separated\n"); fprintf(stderr, "[-g|--gzip-exts GZIPPED_EXTENSIONS]\n use gzip for files with custom extensions, comma-separated\n");
fprintf(stderr, "[-i|--input FILE]\n Input file, can be multiple. Files can also be passed at the end without -i, or as lines on stdin if not specified by args\n"); fprintf(stderr, "[-i|--input FILE]\n Input file, can be multiple. Files can also be passed at the end without -i, or as lines on stdin if not specified by args\n");
fprintf(stderr, "[-o|--output FILE]\n Output file name; if not specified, outputs to stdout\n"); fprintf(stderr, "[-o|--output FILE]\n Output file name; if not specified, outputs to stdout\n");

@ -0,0 +1,67 @@
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "parsing.h"
#include "espfs.h"
static size_t espfs_parse_filesize = -1;
static int espfs_parse_fd = -1;
/**
* Parse an image file and show the files contained.
* This is a simple sanity test.
*
* @param[in] filename - image file to parse
*/
void parseEspfsFileAndShowItsContents(const char *filename)
{
int rv;
fprintf(stderr, "Parsing: %s\n", filename);
FILE *f = fopen(filename, "r");
if (!f) {
perror(filename);
exit(1);
}
int fd = fileno(f);
espfs_parse_filesize = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
espfs_parse_fd = fd;
rv = espFsInit();
if (rv != 0) {
fprintf(stderr, "Fail to init FS\n");
exit(1);
}
EspFsWalk walk;
espFsWalkInit(&walk);
EspFsHeader header;
uint32_t offset;
char namebuf[1024];
while (espFsWalkNext(&walk, &header, namebuf, 1024, &offset)) {
fprintf(stderr, "at %04x: \"%s\", flags: %02x, comp: %s, compLen: %d, plainLen: %d\n", offset, namebuf, header.flags,
header.compression == 1 ? "HS" : "None", header.fileLenComp, header.fileLenDecomp);
}
fclose(f);
}
int httpdPlatEspfsRead(void *dest, uint32_t offset, size_t len)
{
// fprintf(stderr, "FS read @ %d, len %d\n", offset, (int) len);
if (offset + len > espfs_parse_filesize) {
// fprintf(stderr, "Read out fo range!\n");
return -1;
}
lseek(espfs_parse_fd, offset, SEEK_SET);
read(espfs_parse_fd, dest, len);
return 0;
}

@ -0,0 +1,7 @@
/**
* The espfs archive parsing part of mkespfsimage
*/
#pragma once
void parseEspfsFileAndShowItsContents(const char *filename);

@ -0,0 +1,5 @@
mkespfsimage
*.o
.idea/
*.bin
*.a

@ -0,0 +1,51 @@
# If building for posix:
ifeq ($(PLATFORM),arm-freertos)
CC ?= arm-none-eabi-gcc
AR ?= arm-none-eabi-ar
PORT_SOURCES = src/port/httpd-freertos.c
else
# Probably posix
CC ?= cc
AR ?= ar
PORT_SOURCES = src/port/httpd-posix.c
endif
# additional C flags can be specified via the CFLAGS variable
LIB_FILE = libspritehttpd.a
LIB_SOURCES = ${PORT_SOURCES} \
lib/espfs/espfs.c \
lib/heatshrink/heatshrink_decoder.c \
src/utils/base64.c \
src/utils/sha1.c \
src/httpdespfs.c \
src/httpd.c \
src/httpd-loop.c \
src/cgiwebsocket.c
LIB_OBJS = ${LIB_SOURCES:.c=.o}
LIB_INCLUDES = -Iinclude -Ilib/heatshrink -Ilib/espfs
# TODO check what these mean
LIB_CFLAGS = -fPIC -Wall -Wextra -c
OBJ_DIR=./obj
OUT_DIR=.
all: $(LIB_FILE)
%.o: %.c
$(CC) -c $(LIB_INCLUDES) $(CFLAGS) $(LIB_CFLAGS) -o $@ $<
$(LIB_FILE): ${LIB_OBJS}
$(AR) rcs $@ $^
clean:
find . -type f -name '*.o' -delete
find . -type f -name '*.d' -delete
find . -type f -name '*.a' -delete
find . -type f -name '*.elf' -delete

@ -17,6 +17,7 @@ It's written for use with httpd, but doesn't need to be used as such.
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <endian.h>
#include "espfsformat.h" #include "espfsformat.h"
#include "espfs.h" #include "espfs.h"
@ -202,6 +203,12 @@ EspFsFile *espFsOpen(const char *fileName)
return NULL; return NULL;
} }
// Indians
h.magic = le32toh(h.magic);
h.nameLen = le16toh(h.nameLen);
h.fileLenComp = le32toh(h.fileLenComp);
h.fileLenDecomp = le32toh(h.fileLenDecomp);
if (h.magic != ESPFS_MAGIC) { if (h.magic != ESPFS_MAGIC) {
espfs_error("[EspFS] Magic mismatch. EspFS image broken."); espfs_error("[EspFS] Magic mismatch. EspFS image broken.");
return NULL; return NULL;
@ -220,6 +227,8 @@ EspFsFile *espFsOpen(const char *fileName)
namebuf[h.nameLen] = 0; // ensure it's terminated namebuf[h.nameLen] = 0; // ensure it's terminated
if (strcmp(namebuf, fileName) == 0) { if (strcmp(namebuf, fileName) == 0) {
espfs_dbg("[EspFS] Found matching file: name %s, len %d", namebuf, h.fileLenDecomp);
//Yay, this is the file we need! //Yay, this is the file we need!
return espFsOpenFromHeader(&h, hpos); return espFsOpenFromHeader(&h, hpos);
} }

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h>
#include "espfsformat.h" #include "espfsformat.h"
typedef enum { typedef enum {

@ -12,7 +12,7 @@ with the FLAG_LASTFILE flag set.
#define FLAG_GZIP (1<<1) #define FLAG_GZIP (1<<1)
#define COMPRESS_NONE 0 #define COMPRESS_NONE 0
#define COMPRESS_HEATSHRINK 1 #define COMPRESS_HEATSHRINK 1
#define ESPFS_MAGIC 0x73665345 /* ASCII ESfs - when read as little endian */ #define ESPFS_MAGIC 0x73665345 /* ASCII sequence of bytes 'E' 'S' 'f' 's' interpreted as as little endian uint32_t */
/* 16 bytes long for alignment */ /* 16 bytes long for alignment */
typedef struct { typedef struct {

@ -75,7 +75,7 @@ heatshrink_encoder *heatshrink_encoder_alloc(uint8_t window_sz2,
(lookahead_sz2 >= window_sz2)) { (lookahead_sz2 >= window_sz2)) {
return NULL; return NULL;
} }
/* Note: 2 * the window size is used because the buffer needs to fit /* Note: 2 * the window size is used because the buffer needs to fit
* (1 << window_sz2) bytes for the current input, and an additional * (1 << window_sz2) bytes for the current input, and an additional
* (1 << window_sz2) bytes for the previous buffer of input, which * (1 << window_sz2) bytes for the previous buffer of input, which
@ -135,7 +135,7 @@ void heatshrink_encoder_reset(heatshrink_encoder *hse) {
} }
HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse, HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse,
uint8_t *in_buf, size_t size, size_t *input_size) { const uint8_t *in_buf, size_t size, size_t *input_size) {
if ((hse == NULL) || (in_buf == NULL) || (input_size == NULL)) { if ((hse == NULL) || (in_buf == NULL) || (input_size == NULL)) {
return HSER_SINK_ERROR_NULL; return HSER_SINK_ERROR_NULL;
} }
@ -284,11 +284,11 @@ static HSE_state st_step_search(heatshrink_encoder *hse) {
if (hse->input_size - msi < lookahead_sz) { if (hse->input_size - msi < lookahead_sz) {
max_possible = hse->input_size - msi; max_possible = hse->input_size - msi;
} }
uint16_t match_length = 0; uint16_t match_length = 0;
uint16_t match_pos = find_longest_match(hse, uint16_t match_pos = find_longest_match(hse,
start, end, max_possible, &match_length); start, end, max_possible, &match_length);
if (match_pos == MATCH_NOT_FOUND) { if (match_pos == MATCH_NOT_FOUND) {
LOG("ss Match not found\n"); LOG("ss Match not found\n");
hse->match_scan_index++; hse->match_scan_index++;
@ -407,7 +407,7 @@ static void do_indexing(heatshrink_encoder *hse) {
#if HEATSHRINK_USE_INDEX #if HEATSHRINK_USE_INDEX
/* Build an index array I that contains flattened linked lists /* Build an index array I that contains flattened linked lists
* for the previous instances of every byte in the buffer. * for the previous instances of every byte in the buffer.
* *
* For example, if buf[200] == 'x', then index[200] will either * For example, if buf[200] == 'x', then index[200] will either
* be an offset i such that buf[i] == 'x', or a negative offset * be an offset i such that buf[i] == 'x', or a negative offset
* to indicate end-of-list. This significantly speeds up matching, * to indicate end-of-list. This significantly speeds up matching,
@ -416,7 +416,7 @@ static void do_indexing(heatshrink_encoder *hse) {
* Future optimization options: * Future optimization options:
* 1. Since any negative value represents end-of-list, the other * 1. Since any negative value represents end-of-list, the other
* 15 bits could be used to improve the index dynamically. * 15 bits could be used to improve the index dynamically.
* *
* 2. Likewise, the last lookahead_sz bytes of the index will * 2. Likewise, the last lookahead_sz bytes of the index will
* not be usable, so temporary data could be stored there to * not be usable, so temporary data could be stored there to
* dynamically improve the index. * dynamically improve the index.
@ -489,7 +489,7 @@ static uint16_t find_longest_match(heatshrink_encoder *hse, uint16_t start,
} }
pos = hsi->index[pos]; pos = hsi->index[pos];
} }
#else #else
for (int16_t pos=end - 1; pos - (int16_t)start >= 0; pos--) { for (int16_t pos=end - 1; pos - (int16_t)start >= 0; pos--) {
uint8_t * const pospoint = &buf[pos]; uint8_t * const pospoint = &buf[pos];
if ((pospoint[match_maxlen] == needlepoint[match_maxlen]) if ((pospoint[match_maxlen] == needlepoint[match_maxlen])
@ -509,7 +509,7 @@ static uint16_t find_longest_match(heatshrink_encoder *hse, uint16_t start,
} }
} }
#endif #endif
const size_t break_even_point = const size_t break_even_point =
(1 + HEATSHRINK_ENCODER_WINDOW_BITS(hse) + (1 + HEATSHRINK_ENCODER_WINDOW_BITS(hse) +
HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse)); HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse));
@ -588,9 +588,9 @@ static void push_literal_byte(heatshrink_encoder *hse, output_info *oi) {
static void save_backlog(heatshrink_encoder *hse) { static void save_backlog(heatshrink_encoder *hse) {
size_t input_buf_sz = get_input_buffer_size(hse); size_t input_buf_sz = get_input_buffer_size(hse);
uint16_t msi = hse->match_scan_index; uint16_t msi = hse->match_scan_index;
/* Copy processed data to beginning of buffer, so it can be /* Copy processed data to beginning of buffer, so it can be
* used for future matches. Don't bother checking whether the * used for future matches. Don't bother checking whether the
* input is less than the maximum size, because if it isn't, * input is less than the maximum size, because if it isn't,
@ -601,7 +601,7 @@ static void save_backlog(heatshrink_encoder *hse) {
memmove(&hse->buffer[0], memmove(&hse->buffer[0],
&hse->buffer[input_buf_sz - rem], &hse->buffer[input_buf_sz - rem],
shift_sz); shift_sz);
hse->match_scan_index = 0; hse->match_scan_index = 0;
hse->input_size -= (uint16_t)input_buf_sz - rem; hse->input_size -= (uint16_t)input_buf_sz - rem;
} }

@ -93,7 +93,7 @@ void heatshrink_encoder_reset(heatshrink_encoder *hse);
* INPUT_SIZE is set to the number of bytes actually sunk (in case a * INPUT_SIZE is set to the number of bytes actually sunk (in case a
* buffer was filled.). */ * buffer was filled.). */
HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse, HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse,
uint8_t *in_buf, size_t size, size_t *input_size); const uint8_t *in_buf, size_t size, size_t *input_size);
/* Poll for output from the encoder, copying at most OUT_BUF_SIZE bytes into /* Poll for output from the encoder, copying at most OUT_BUF_SIZE bytes into
* OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */ * OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */

@ -12,6 +12,8 @@ Thanks to my collague at Espressif for writing the foundations of this code.
#include <netinet/in.h> #include <netinet/in.h>
#include <unistd.h> #include <unistd.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <fcntl.h>
#include <errno.h>
#include "logging.h" #include "logging.h"
static int httpPort; static int httpPort;
@ -27,9 +29,17 @@ struct HttpdConnType {
static HttpdConnType s_rconn[HTTPD_MAX_CONNECTIONS]; static HttpdConnType s_rconn[HTTPD_MAX_CONNECTIONS];
static int fd_is_valid(int fd)
{
return fcntl(fd, F_GETFD) != -1 || errno != EBADF;
}
int httpdConnSendData(ConnTypePtr conn, char *buff, int len) int httpdConnSendData(ConnTypePtr conn, char *buff, int len)
{ {
conn->needWriteDoneNotif = 1; conn->needWriteDoneNotif = 1;
if (!fd_is_valid(conn->fd)) {
return -1;
}
return (write(conn->fd, buff, len) >= 0); return (write(conn->fd, buff, len) >= 0);
} }
@ -83,6 +93,12 @@ void platHttpServerTask(void *pvParameters)
} }
} while (listenfd == -1); } while (listenfd == -1);
/* https://stackoverflow.com/questions/5592747/bind-error-while-recreating-socket */
int yes=1;
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) {
perror("setsockopt");
}
/* Bind to the local port */ /* Bind to the local port */
do { do {
ret = bind(listenfd, (struct sockaddr *) &server_addr, sizeof(server_addr)); ret = bind(listenfd, (struct sockaddr *) &server_addr, sizeof(server_addr));

@ -146,7 +146,10 @@ serveStaticFile(HttpdConnData *connData, const char *filepath)
} }
len = espFsRead(file, buff, FILE_CHUNK_LEN); len = espFsRead(file, buff, FILE_CHUNK_LEN);
if (len > 0) { httpdSend(connData, buff, len); } if (len > 0) {
espfs_dbg("[EspFS] Read file chunk: %d bytes", len);
httpdSend(connData, buff, len);
}
if (len != FILE_CHUNK_LEN) { if (len != FILE_CHUNK_LEN) {
//We're done. //We're done.
espFsClose(file); espFsClose(file);
Loading…
Cancel
Save