mirror of
https://github.com/cesanta/mongoose.git
synced 2024-11-27 20:59:00 +08:00
Fix #1211 - adjust ESP32 example to serve FS
This commit is contained in:
parent
6827972f71
commit
457d76b049
@ -9,7 +9,7 @@ example: main/main.c Makefile
|
|||||||
COMPORT ?= /dev/cu.SLAB_USBtoUART
|
COMPORT ?= /dev/cu.SLAB_USBtoUART
|
||||||
ESPTOOL ?= esptool.py
|
ESPTOOL ?= esptool.py
|
||||||
flash:
|
flash:
|
||||||
cd build && $(ESPTOOL) --chip esp32 -p $(COMPORT) -b 460800 --before=default_reset --after=hard_reset write_flash --flash_mode dio --flash_freq 40m --flash_size 2MB 0x8000 partition_table/partition-table.bin 0x1000 bootloader/bootloader.bin 0x10000 mongoose-esp32-example.bin
|
cd build && $(ESPTOOL) --chip esp32 -p $(COMPORT) -b 460800 --before=default_reset --after=hard_reset write_flash --flash_mode dio --flash_freq 40m --flash_size 2MB 0x8000 partition_table/partition-table.bin 0x1000 bootloader/bootloader.bin 0x100000 mongoose-esp32-example.bin
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb mongoose mongoose_* mongoose.* build sdkconfig
|
rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb mongoose mongoose_* mongoose.* build sdkconfig
|
||||||
|
@ -2,3 +2,4 @@ idf_component_register(SRCS "main.c"
|
|||||||
"wifi.c"
|
"wifi.c"
|
||||||
"../../../mongoose.c"
|
"../../../mongoose.c"
|
||||||
INCLUDE_DIRS "../../..")
|
INCLUDE_DIRS "../../..")
|
||||||
|
component_compile_options(-DMG_ENABLE_DIRECTORY_LISTING=1 -DMG_ENABLE_LINES)
|
||||||
|
@ -1,54 +1,53 @@
|
|||||||
// Copyright (c) 2020 Cesanta Software Limited
|
// Copyright (c) 2020 Cesanta Software Limited
|
||||||
// All rights reserved
|
// All rights reserved
|
||||||
|
|
||||||
|
#include "esp_spiffs.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
|
||||||
#define WIFI_SSID "WIFI_NETWORK" // SET THIS!
|
#define WIFI_SSID "WIFI_NETWORK" // SET THIS!
|
||||||
#define WIFI_PASS "WIFI_PASSWORD" // SET THIS!
|
#define WIFI_PASS "WIFI_PASSWORD" // SET THIS!
|
||||||
|
#define FS_ROOT "/spiffs"
|
||||||
|
|
||||||
#define SERVER_URL "http://0.0.0.0:80"
|
// Event handler for an server (accepted) connection. Implemented endpoints:
|
||||||
#define CLIENT_URL "http://info.cern.ch"
|
// /api/stats - return JSON object with ESP32 stats (free RAM)
|
||||||
|
// any other - serve files from the filesystem
|
||||||
// Event handler for an server (accepted) connection
|
|
||||||
static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||||
if (ev == MG_EV_HTTP_MSG) {
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
mg_http_reply(c, 200, "", "Hello from ESP!\n");
|
struct mg_http_message *hm = ev_data;
|
||||||
|
if (mg_http_match_uri(hm, "/api/stats")) {
|
||||||
|
mg_http_reply(c, 200, "", "{\"ram\": %lu}\n", xPortGetFreeHeapSize());
|
||||||
|
} else {
|
||||||
|
struct mg_http_serve_opts opts = {.root_dir = FS_ROOT};
|
||||||
|
mg_http_serve_dir(c, hm, &opts);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event handler for a client connection - fetch the first web page in history
|
// SPIFFS is flat, so tell Mongoose that the FS root is a directory
|
||||||
// To enable TLS for HTTP,
|
// This cludge is not required for filesystems with directory support
|
||||||
// 1. Copy "ca.pem" file to the ESP32 flash FS
|
bool mg_is_dir(const char *path) {
|
||||||
// 2. Add TLS init snippet for the connection, see examples/http-client
|
return strcmp(path, FS_ROOT) == 0;
|
||||||
static void cb2(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|
||||||
if (ev == MG_EV_CONNECT) {
|
|
||||||
struct mg_str s = mg_url_host(CLIENT_URL);
|
|
||||||
mg_printf(c, "GET / HTTP/1.0\r\nHost: %.*s\r\n\r\n", (int) s.len, s.ptr);
|
|
||||||
} else if (ev == MG_EV_HTTP_MSG) {
|
|
||||||
struct mg_http_message *hm = ev_data; // Print HTTP response
|
|
||||||
LOG(LL_INFO, ("Fetched:\n%.*s", (int) hm->message.len, hm->message.ptr));
|
|
||||||
c->is_closing = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called after we're connected to WiFi network
|
|
||||||
static void run_mongoose(void) {
|
|
||||||
struct mg_mgr mgr;
|
|
||||||
mg_log_set("3");
|
|
||||||
mg_mgr_init(&mgr);
|
|
||||||
mg_http_listen(&mgr, SERVER_URL, cb, &mgr); // Listening server
|
|
||||||
mg_http_connect(&mgr, CLIENT_URL, cb2, &mgr); // Example client
|
|
||||||
LOG(LL_INFO, ("Starting Mongoose web server v%s", MG_VERSION));
|
|
||||||
for (;;) mg_mgr_poll(&mgr, 1000);
|
|
||||||
mg_mgr_free(&mgr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void app_main(void) {
|
void app_main(void) {
|
||||||
|
// Mount filesystem
|
||||||
|
esp_vfs_spiffs_conf_t conf = {
|
||||||
|
.base_path = FS_ROOT, .max_files = 20, .format_if_mount_failed = true};
|
||||||
|
int res = esp_vfs_spiffs_register(&conf);
|
||||||
|
LOG(res == ESP_OK ? LL_INFO : LL_ERROR, ("FS %s, %d", conf.base_path, res));
|
||||||
|
mg_file_printf(FS_ROOT "/hello.txt", "%s", "hello from ESP");
|
||||||
|
|
||||||
// Setup wifi. This function is implemented in wifi.c
|
// Setup wifi. This function is implemented in wifi.c
|
||||||
// It blocks until connected to the configured WiFi network
|
// It blocks until connected to the configured WiFi network
|
||||||
void wifi_init(const char *ssid, const char *pass);
|
void wifi_init(const char *ssid, const char *pass);
|
||||||
wifi_init(WIFI_SSID, WIFI_PASS);
|
wifi_init(WIFI_SSID, WIFI_PASS);
|
||||||
|
|
||||||
// Done connecting to WiFi, now start HTTP server
|
// Connected to WiFi, now start HTTP server
|
||||||
run_mongoose();
|
struct mg_mgr mgr;
|
||||||
|
mg_log_set("3");
|
||||||
|
mg_mgr_init(&mgr);
|
||||||
|
mg_http_listen(&mgr, "http://0.0.0.0:80", cb, &mgr); // Listening server
|
||||||
|
LOG(LL_INFO, ("Starting Mongoose web server v%s", MG_VERSION));
|
||||||
|
for (;;) mg_mgr_poll(&mgr, 1000); // Infinite event loop
|
||||||
}
|
}
|
||||||
|
5
examples/esp32/partitions.csv
Normal file
5
examples/esp32/partitions.csv
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
|
nvs, data, nvs, 0x9000, 0x6000,
|
||||||
|
phy_init, data, phy, 0xf000, 0x1000,
|
||||||
|
storage, data, spiffs, 0x10000, 0x10000,
|
||||||
|
factory, app, factory, 0x100000, 1M,
|
|
3
examples/esp32/sdkconfig.defaults
Normal file
3
examples/esp32/sdkconfig.defaults
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||||
|
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||||
|
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
106
mongoose.c
106
mongoose.c
@ -483,7 +483,7 @@ int mg_http_get_var(const struct mg_str *buf, const char *name, char *dst,
|
|||||||
s = (const char *) memchr(p, '&', (size_t)(e - p));
|
s = (const char *) memchr(p, '&', (size_t)(e - p));
|
||||||
if (s == NULL) s = e;
|
if (s == NULL) s = e;
|
||||||
len = mg_url_decode(p, (size_t)(s - p), dst, dst_len, 1);
|
len = mg_url_decode(p, (size_t)(s - p), dst, dst_len, 1);
|
||||||
if (len == -1) len = -3; // Failed to decode
|
if (len < 0) len = -3; // Failed to decode
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -511,7 +511,7 @@ int mg_url_decode(const char *src, size_t src_len, char *dst, size_t dst_len,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (j < dst_len) dst[j] = '\0'; // Null-terminate the destination
|
if (j < dst_len) dst[j] = '\0'; // Null-terminate the destination
|
||||||
return i >= src_len ? (int) j : -1;
|
return i >= src_len && j < dst_len ? (int) j : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mg_http_get_request_len(const unsigned char *buf, size_t buf_len) {
|
int mg_http_get_request_len(const unsigned char *buf, size_t buf_len) {
|
||||||
@ -819,21 +819,23 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
MG_ARCH == MG_ARCH_FREERTOS
|
MG_ARCH == MG_ARCH_FREERTOS
|
||||||
char *realpath(const char *src, char *dst) {
|
char *realpath(const char *src, char *dst) {
|
||||||
int len = strlen(src);
|
int len = strlen(src);
|
||||||
if (len > PATH_MAX - 1) len = PATH_MAX - 1;
|
if (len > MG_PATH_MAX - 1) len = MG_PATH_MAX - 1;
|
||||||
strncpy(dst, src, len);
|
strncpy(dst, src, len);
|
||||||
dst[len] = '\0';
|
dst[len] = '\0';
|
||||||
|
LOG(LL_DEBUG, ("[%s] -> [%s]", src, dst));
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Try to avoid dirent API
|
// Allow user to override this function
|
||||||
static int mg_is_dir(const char *path) {
|
bool mg_is_dir(const char *path) WEAK;
|
||||||
|
bool mg_is_dir(const char *path) {
|
||||||
#if MG_ARCH == MG_ARCH_FREERTOS
|
#if MG_ARCH == MG_ARCH_FREERTOS
|
||||||
struct FF_STAT st;
|
struct FF_STAT st;
|
||||||
return (ff_stat(path, &st) == 0) && (st.st_mode & FF_IFDIR);
|
return (ff_stat(path, &st) == 0) && (st.st_mode & FF_IFDIR);
|
||||||
#else
|
#else
|
||||||
struct stat st;
|
mg_stat_t st;
|
||||||
return (stat(path, &st) == 0) && (st.st_mode & S_IFDIR);
|
return mg_stat(path, &st) == 0 && S_ISDIR(st.st_mode);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -978,7 +980,7 @@ static void printdirentry(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
|
|
||||||
static void listdir(struct mg_connection *c, struct mg_http_message *hm,
|
static void listdir(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
char *dir) {
|
char *dir) {
|
||||||
char path[PATH_MAX + 1], *p = &dir[strlen(dir) - 1], tmp[10];
|
char path[MG_PATH_MAX], *p = &dir[strlen(dir) - 1], tmp[10];
|
||||||
struct dirent *dp;
|
struct dirent *dp;
|
||||||
DIR *dirp;
|
DIR *dirp;
|
||||||
|
|
||||||
@ -997,14 +999,17 @@ static void listdir(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
(int) hm->uri.len, hm->uri.ptr, (int) hm->uri.len, hm->uri.ptr);
|
(int) hm->uri.len, hm->uri.ptr, (int) hm->uri.len, hm->uri.ptr);
|
||||||
while ((dp = readdir(dirp)) != NULL) {
|
while ((dp = readdir(dirp)) != NULL) {
|
||||||
mg_stat_t st;
|
mg_stat_t st;
|
||||||
|
const char *sep = dp->d_name[0] == MG_DIRSEP ? "/" : "";
|
||||||
// Do not show current dir and hidden files
|
// Do not show current dir and hidden files
|
||||||
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue;
|
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue;
|
||||||
snprintf(path, sizeof(path), "%s/%s", dir, dp->d_name);
|
// SPIFFS can report "/foo.txt" in the dp->d_name
|
||||||
if (mg_stat(path, &st) != 0) {
|
if (snprintf(path, sizeof(path), "%s%s%s", dir, sep, dp->d_name) < 0) {
|
||||||
|
LOG(LL_ERROR, ("%s truncated", dp->d_name));
|
||||||
|
} else if (mg_stat(path, &st) != 0) {
|
||||||
LOG(LL_ERROR, ("%lu stat(%s): %d", c->id, path, errno));
|
LOG(LL_ERROR, ("%lu stat(%s): %d", c->id, path, errno));
|
||||||
continue;
|
} else {
|
||||||
|
printdirentry(c, hm, dp->d_name, &st);
|
||||||
}
|
}
|
||||||
printdirentry(c, hm, dp->d_name, &st);
|
|
||||||
}
|
}
|
||||||
closedir(dirp);
|
closedir(dirp);
|
||||||
mg_printf(c,
|
mg_printf(c,
|
||||||
@ -1022,59 +1027,70 @@ static void listdir(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
|
|
||||||
void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
|
void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
struct mg_http_serve_opts *opts) {
|
struct mg_http_serve_opts *opts) {
|
||||||
char path[PATH_MAX + 2], root[sizeof(path) - 2], real[sizeof(path) - 2];
|
char t1[MG_PATH_MAX], t2[sizeof(t1)];
|
||||||
path[0] = root[0] = real[0] = '\0';
|
t1[0] = t2[0] = '\0';
|
||||||
if (realpath(opts->root_dir, root) == NULL)
|
|
||||||
LOG(LL_DEBUG, ("realpath(%s): %d", opts->root_dir, errno));
|
if (realpath(opts->root_dir, t1) == NULL)
|
||||||
if (!mg_is_dir(root)) {
|
LOG(LL_ERROR, ("realpath(%s): %d", opts->root_dir, errno));
|
||||||
mg_http_reply(c, 400, "", "Bad web root [%s]\n", root);
|
|
||||||
|
LOG(LL_DEBUG, ("realpath(%s) -> [%s] %zu", opts->root_dir, t1, sizeof(t1)));
|
||||||
|
|
||||||
|
if (!mg_is_dir(t1)) {
|
||||||
|
mg_http_reply(c, 400, "", "Bad web root [%s]\n", t1);
|
||||||
} else {
|
} else {
|
||||||
char dec[PATH_MAX];
|
|
||||||
// NOTE(lsm): Xilinx snprintf does not 0-terminate the detination for
|
// NOTE(lsm): Xilinx snprintf does not 0-terminate the detination for
|
||||||
// the %.*s specifier, if the length is zero. Make sure hm->uri.len > 0
|
// the %.*s specifier, if the length is zero. Make sure hm->uri.len > 0
|
||||||
bool is_index = false;
|
bool is_index = false;
|
||||||
int ndec = mg_url_decode(hm->uri.ptr, hm->uri.len, dec, sizeof(dec), 0);
|
size_t n1 = strlen(t1), n2;
|
||||||
size_t n =
|
|
||||||
snprintf(path, sizeof(path), "%s%.*s", root, ndec < 0 ? 0 : ndec, dec);
|
mg_url_decode(hm->uri.ptr, hm->uri.len, t1 + n1, sizeof(t1) - n1, 0);
|
||||||
while (n > 0 && n < sizeof(path) && path[n - 1] == '/') path[--n] = 0;
|
t1[sizeof(t1) - 1] = '\0';
|
||||||
if (realpath(path, real) == NULL)
|
n2 = strlen(t1);
|
||||||
LOG(LL_DEBUG, ("realpath(%s): %d", path, errno));
|
while (n2 > 0 && t1[n2 - 1] == '/') t1[--n2] = 0;
|
||||||
// LOG(LL_INFO, ("[%s] [%s] [%s] [%s]", dir, root, path, real));
|
|
||||||
if (mg_is_dir(real)) {
|
if (realpath(t1, t2) == NULL)
|
||||||
strncat(real, "/index.html", sizeof(real) - strlen(real) - 1);
|
LOG(LL_ERROR, ("realpath(%s): %d", t1, errno));
|
||||||
real[sizeof(real) - 1] = '\0';
|
|
||||||
|
if (mg_is_dir(t2)) {
|
||||||
|
strncat(t2, "/index.html", sizeof(t2) - strlen(t2) - 1);
|
||||||
|
t2[sizeof(t2) - 1] = '\0';
|
||||||
is_index = true;
|
is_index = true;
|
||||||
}
|
}
|
||||||
if (strlen(real) < strlen(root) || memcmp(real, root, strlen(root)) != 0) {
|
|
||||||
|
LOG(LL_DEBUG, (" --> [%s] [%s] %zu", t1, t2, sizeof(t1)));
|
||||||
|
|
||||||
|
if (strlen(t2) < n1 || memcmp(t1, t2, n1) != 0) {
|
||||||
|
// Requested file is located outside root directory, fail
|
||||||
mg_http_reply(c, 404, "", "Not found %.*s\n", hm->uri.len, hm->uri.ptr);
|
mg_http_reply(c, 404, "", "Not found %.*s\n", hm->uri.len, hm->uri.ptr);
|
||||||
} else {
|
} else {
|
||||||
FILE *fp = fopen(real, "r");
|
FILE *fp = fopen(t2, "r");
|
||||||
#if MG_ENABLE_SSI
|
#if MG_ENABLE_SSI
|
||||||
if (is_index && fp == NULL) {
|
if (is_index && fp == NULL) {
|
||||||
char *p = real + strlen(real);
|
char *p = t2 + strlen(t2);
|
||||||
while (p > real && p[-1] != '/') p--;
|
while (p > t2 && p[-1] != '/') p--;
|
||||||
strncpy(p, "index.shtml", &real[sizeof(real)] - p - 2);
|
strncpy(p, "index.shtml", &t2[sizeof(t2)] - p - 2);
|
||||||
real[sizeof(real) - 1] = '\0';
|
t2[sizeof(t2) - 1] = '\0';
|
||||||
fp = fopen(real, "r");
|
fp = fopen(t2, "r");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if MG_ENABLE_HTTP_DEBUG_ENDPOINT
|
#if MG_ENABLE_HTTP_DEBUG_ENDPOINT
|
||||||
snprintf(c->label, sizeof(c->label) - 1, "<-F %s", real);
|
snprintf(c->label, sizeof(c->label) - 1, "<-F %s", t2);
|
||||||
#endif
|
#endif
|
||||||
if (is_index && fp == NULL) {
|
if (is_index && fp == NULL) {
|
||||||
#if MG_ENABLE_DIRECTORY_LISTING
|
#if MG_ENABLE_DIRECTORY_LISTING
|
||||||
listdir(c, hm, real);
|
listdir(c, hm, t2);
|
||||||
#else
|
#else
|
||||||
mg_http_reply(c, 403, "", "%s", "Directory listing not supported");
|
mg_http_reply(c, 403, "", "%s", "Directory listing not supported");
|
||||||
#endif
|
#endif
|
||||||
#if MG_ENABLE_SSI
|
#if MG_ENABLE_SSI
|
||||||
} else if (opts->ssi_pattern != NULL &&
|
} else if (opts->ssi_pattern != NULL &&
|
||||||
mg_globmatch(opts->ssi_pattern, strlen(opts->ssi_pattern),
|
mg_globmatch(opts->ssi_pattern, strlen(opts->ssi_pattern), t2,
|
||||||
real, strlen(real))) {
|
strlen(t2))) {
|
||||||
mg_http_serve_ssi(c, root, real);
|
t1[n1] = '\0';
|
||||||
|
mg_http_serve_ssi(c, t1, t2);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
mg_http_serve_file(c, hm, real, guess_content_type(real), NULL);
|
mg_http_serve_file(c, hm, t2, guess_content_type(t2), NULL);
|
||||||
}
|
}
|
||||||
if (fp != NULL) fclose(fp);
|
if (fp != NULL) fclose(fp);
|
||||||
}
|
}
|
||||||
@ -3058,7 +3074,7 @@ static char *mg_ssi(const char *path, const char *root, int depth) {
|
|||||||
if (intag && ch == '>' && buf[len - 1] == '-' && buf[len - 2] == '-') {
|
if (intag && ch == '>' && buf[len - 1] == '-' && buf[len - 2] == '-') {
|
||||||
buf[len++] = ch & 0xff;
|
buf[len++] = ch & 0xff;
|
||||||
if (sscanf(buf, "<!--#include file=\"%[^\"]", arg)) {
|
if (sscanf(buf, "<!--#include file=\"%[^\"]", arg)) {
|
||||||
char tmp[PATH_MAX], *p = (char *) path + strlen(path), *data;
|
char tmp[MG_PATH_MAX], *p = (char *) path + strlen(path), *data;
|
||||||
while (p > path && p[-1] != MG_DIRSEP && p[-1] != '/') p--;
|
while (p > path && p[-1] != MG_DIRSEP && p[-1] != '/') p--;
|
||||||
snprintf(tmp, sizeof(tmp), "%.*s%s", (int) (p - path), path, arg);
|
snprintf(tmp, sizeof(tmp), "%.*s%s", (int) (p - path), path, arg);
|
||||||
if (depth < MG_MAX_SSI_DEPTH &&
|
if (depth < MG_MAX_SSI_DEPTH &&
|
||||||
@ -3069,7 +3085,7 @@ static char *mg_ssi(const char *path, const char *root, int depth) {
|
|||||||
LOG(LL_ERROR, ("%s: file=%s error or too deep", path, arg));
|
LOG(LL_ERROR, ("%s: file=%s error or too deep", path, arg));
|
||||||
}
|
}
|
||||||
} else if (sscanf(buf, "<!--#include virtual=\"%[^\"]", arg)) {
|
} else if (sscanf(buf, "<!--#include virtual=\"%[^\"]", arg)) {
|
||||||
char tmp[PATH_MAX], *data;
|
char tmp[MG_PATH_MAX], *data;
|
||||||
snprintf(tmp, sizeof(tmp), "%s%s", root, arg);
|
snprintf(tmp, sizeof(tmp), "%s%s", root, arg);
|
||||||
if (depth < MG_MAX_SSI_DEPTH &&
|
if (depth < MG_MAX_SSI_DEPTH &&
|
||||||
(data = mg_ssi(tmp, root, depth + 1)) != NULL) {
|
(data = mg_ssi(tmp, root, depth + 1)) != NULL) {
|
||||||
@ -3755,7 +3771,7 @@ char *mg_file_read(const char *path) {
|
|||||||
bool mg_file_write(const char *path, const void *buf, size_t len) {
|
bool mg_file_write(const char *path, const void *buf, size_t len) {
|
||||||
bool result = false;
|
bool result = false;
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
char tmp[PATH_MAX];
|
char tmp[MG_PATH_MAX];
|
||||||
snprintf(tmp, sizeof(tmp), "%s.%d", path, rand());
|
snprintf(tmp, sizeof(tmp), "%s.%d", path, rand());
|
||||||
fp = fopen(tmp, "wb");
|
fp = fopen(tmp, "wb");
|
||||||
if (fp != NULL) {
|
if (fp != NULL) {
|
||||||
|
@ -120,8 +120,12 @@
|
|||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#define MG_DIRSEP '/'
|
#define MG_DIRSEP '/'
|
||||||
#define MG_INT64_FMT "%lld"
|
#define MG_INT64_FMT "%lld"
|
||||||
|
#ifndef MG_PATH_MAX
|
||||||
|
#define MG_PATH_MAX 128
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -351,6 +355,10 @@ typedef int socklen_t;
|
|||||||
#define MG_MAX_HTTP_HEADERS 40
|
#define MG_MAX_HTTP_HEADERS 40
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_PATH_MAX
|
||||||
|
#define MG_PATH_MAX PATH_MAX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -4,7 +4,11 @@
|
|||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#define MG_DIRSEP '/'
|
#define MG_DIRSEP '/'
|
||||||
#define MG_INT64_FMT "%lld"
|
#define MG_INT64_FMT "%lld"
|
||||||
|
#ifndef MG_PATH_MAX
|
||||||
|
#define MG_PATH_MAX 128
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -67,3 +67,7 @@
|
|||||||
#ifndef MG_MAX_HTTP_HEADERS
|
#ifndef MG_MAX_HTTP_HEADERS
|
||||||
#define MG_MAX_HTTP_HEADERS 40
|
#define MG_MAX_HTTP_HEADERS 40
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef MG_PATH_MAX
|
||||||
|
#define MG_PATH_MAX PATH_MAX
|
||||||
|
#endif
|
||||||
|
100
src/http.c
100
src/http.c
@ -63,7 +63,7 @@ int mg_http_get_var(const struct mg_str *buf, const char *name, char *dst,
|
|||||||
s = (const char *) memchr(p, '&', (size_t)(e - p));
|
s = (const char *) memchr(p, '&', (size_t)(e - p));
|
||||||
if (s == NULL) s = e;
|
if (s == NULL) s = e;
|
||||||
len = mg_url_decode(p, (size_t)(s - p), dst, dst_len, 1);
|
len = mg_url_decode(p, (size_t)(s - p), dst, dst_len, 1);
|
||||||
if (len == -1) len = -3; // Failed to decode
|
if (len < 0) len = -3; // Failed to decode
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,7 +91,7 @@ int mg_url_decode(const char *src, size_t src_len, char *dst, size_t dst_len,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (j < dst_len) dst[j] = '\0'; // Null-terminate the destination
|
if (j < dst_len) dst[j] = '\0'; // Null-terminate the destination
|
||||||
return i >= src_len ? (int) j : -1;
|
return i >= src_len && j < dst_len ? (int) j : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mg_http_get_request_len(const unsigned char *buf, size_t buf_len) {
|
int mg_http_get_request_len(const unsigned char *buf, size_t buf_len) {
|
||||||
@ -399,21 +399,23 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
MG_ARCH == MG_ARCH_FREERTOS
|
MG_ARCH == MG_ARCH_FREERTOS
|
||||||
char *realpath(const char *src, char *dst) {
|
char *realpath(const char *src, char *dst) {
|
||||||
int len = strlen(src);
|
int len = strlen(src);
|
||||||
if (len > PATH_MAX - 1) len = PATH_MAX - 1;
|
if (len > MG_PATH_MAX - 1) len = MG_PATH_MAX - 1;
|
||||||
strncpy(dst, src, len);
|
strncpy(dst, src, len);
|
||||||
dst[len] = '\0';
|
dst[len] = '\0';
|
||||||
|
LOG(LL_DEBUG, ("[%s] -> [%s]", src, dst));
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Try to avoid dirent API
|
// Allow user to override this function
|
||||||
static int mg_is_dir(const char *path) {
|
bool mg_is_dir(const char *path) WEAK;
|
||||||
|
bool mg_is_dir(const char *path) {
|
||||||
#if MG_ARCH == MG_ARCH_FREERTOS
|
#if MG_ARCH == MG_ARCH_FREERTOS
|
||||||
struct FF_STAT st;
|
struct FF_STAT st;
|
||||||
return (ff_stat(path, &st) == 0) && (st.st_mode & FF_IFDIR);
|
return (ff_stat(path, &st) == 0) && (st.st_mode & FF_IFDIR);
|
||||||
#else
|
#else
|
||||||
struct stat st;
|
mg_stat_t st;
|
||||||
return (stat(path, &st) == 0) && (st.st_mode & S_IFDIR);
|
return mg_stat(path, &st) == 0 && S_ISDIR(st.st_mode);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -558,7 +560,7 @@ static void printdirentry(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
|
|
||||||
static void listdir(struct mg_connection *c, struct mg_http_message *hm,
|
static void listdir(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
char *dir) {
|
char *dir) {
|
||||||
char path[PATH_MAX + 1], *p = &dir[strlen(dir) - 1], tmp[10];
|
char path[MG_PATH_MAX], *p = &dir[strlen(dir) - 1], tmp[10];
|
||||||
struct dirent *dp;
|
struct dirent *dp;
|
||||||
DIR *dirp;
|
DIR *dirp;
|
||||||
|
|
||||||
@ -577,14 +579,17 @@ static void listdir(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
(int) hm->uri.len, hm->uri.ptr, (int) hm->uri.len, hm->uri.ptr);
|
(int) hm->uri.len, hm->uri.ptr, (int) hm->uri.len, hm->uri.ptr);
|
||||||
while ((dp = readdir(dirp)) != NULL) {
|
while ((dp = readdir(dirp)) != NULL) {
|
||||||
mg_stat_t st;
|
mg_stat_t st;
|
||||||
|
const char *sep = dp->d_name[0] == MG_DIRSEP ? "/" : "";
|
||||||
// Do not show current dir and hidden files
|
// Do not show current dir and hidden files
|
||||||
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue;
|
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue;
|
||||||
snprintf(path, sizeof(path), "%s/%s", dir, dp->d_name);
|
// SPIFFS can report "/foo.txt" in the dp->d_name
|
||||||
if (mg_stat(path, &st) != 0) {
|
if (snprintf(path, sizeof(path), "%s%s%s", dir, sep, dp->d_name) < 0) {
|
||||||
|
LOG(LL_ERROR, ("%s truncated", dp->d_name));
|
||||||
|
} else if (mg_stat(path, &st) != 0) {
|
||||||
LOG(LL_ERROR, ("%lu stat(%s): %d", c->id, path, errno));
|
LOG(LL_ERROR, ("%lu stat(%s): %d", c->id, path, errno));
|
||||||
continue;
|
} else {
|
||||||
|
printdirentry(c, hm, dp->d_name, &st);
|
||||||
}
|
}
|
||||||
printdirentry(c, hm, dp->d_name, &st);
|
|
||||||
}
|
}
|
||||||
closedir(dirp);
|
closedir(dirp);
|
||||||
mg_printf(c,
|
mg_printf(c,
|
||||||
@ -602,59 +607,70 @@ static void listdir(struct mg_connection *c, struct mg_http_message *hm,
|
|||||||
|
|
||||||
void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
|
void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
struct mg_http_serve_opts *opts) {
|
struct mg_http_serve_opts *opts) {
|
||||||
char path[PATH_MAX + 2], root[sizeof(path) - 2], real[sizeof(path) - 2];
|
char t1[MG_PATH_MAX], t2[sizeof(t1)];
|
||||||
path[0] = root[0] = real[0] = '\0';
|
t1[0] = t2[0] = '\0';
|
||||||
if (realpath(opts->root_dir, root) == NULL)
|
|
||||||
LOG(LL_DEBUG, ("realpath(%s): %d", opts->root_dir, errno));
|
if (realpath(opts->root_dir, t1) == NULL)
|
||||||
if (!mg_is_dir(root)) {
|
LOG(LL_ERROR, ("realpath(%s): %d", opts->root_dir, errno));
|
||||||
mg_http_reply(c, 400, "", "Bad web root [%s]\n", root);
|
|
||||||
|
LOG(LL_DEBUG, ("realpath(%s) -> [%s] %zu", opts->root_dir, t1, sizeof(t1)));
|
||||||
|
|
||||||
|
if (!mg_is_dir(t1)) {
|
||||||
|
mg_http_reply(c, 400, "", "Bad web root [%s]\n", t1);
|
||||||
} else {
|
} else {
|
||||||
char dec[PATH_MAX];
|
|
||||||
// NOTE(lsm): Xilinx snprintf does not 0-terminate the detination for
|
// NOTE(lsm): Xilinx snprintf does not 0-terminate the detination for
|
||||||
// the %.*s specifier, if the length is zero. Make sure hm->uri.len > 0
|
// the %.*s specifier, if the length is zero. Make sure hm->uri.len > 0
|
||||||
bool is_index = false;
|
bool is_index = false;
|
||||||
int ndec = mg_url_decode(hm->uri.ptr, hm->uri.len, dec, sizeof(dec), 0);
|
size_t n1 = strlen(t1), n2;
|
||||||
size_t n =
|
|
||||||
snprintf(path, sizeof(path), "%s%.*s", root, ndec < 0 ? 0 : ndec, dec);
|
mg_url_decode(hm->uri.ptr, hm->uri.len, t1 + n1, sizeof(t1) - n1, 0);
|
||||||
while (n > 0 && n < sizeof(path) && path[n - 1] == '/') path[--n] = 0;
|
t1[sizeof(t1) - 1] = '\0';
|
||||||
if (realpath(path, real) == NULL)
|
n2 = strlen(t1);
|
||||||
LOG(LL_DEBUG, ("realpath(%s): %d", path, errno));
|
while (n2 > 0 && t1[n2 - 1] == '/') t1[--n2] = 0;
|
||||||
// LOG(LL_INFO, ("[%s] [%s] [%s] [%s]", dir, root, path, real));
|
|
||||||
if (mg_is_dir(real)) {
|
if (realpath(t1, t2) == NULL)
|
||||||
strncat(real, "/index.html", sizeof(real) - strlen(real) - 1);
|
LOG(LL_ERROR, ("realpath(%s): %d", t1, errno));
|
||||||
real[sizeof(real) - 1] = '\0';
|
|
||||||
|
if (mg_is_dir(t2)) {
|
||||||
|
strncat(t2, "/index.html", sizeof(t2) - strlen(t2) - 1);
|
||||||
|
t2[sizeof(t2) - 1] = '\0';
|
||||||
is_index = true;
|
is_index = true;
|
||||||
}
|
}
|
||||||
if (strlen(real) < strlen(root) || memcmp(real, root, strlen(root)) != 0) {
|
|
||||||
|
LOG(LL_DEBUG, (" --> [%s] [%s] %zu", t1, t2, sizeof(t1)));
|
||||||
|
|
||||||
|
if (strlen(t2) < n1 || memcmp(t1, t2, n1) != 0) {
|
||||||
|
// Requested file is located outside root directory, fail
|
||||||
mg_http_reply(c, 404, "", "Not found %.*s\n", hm->uri.len, hm->uri.ptr);
|
mg_http_reply(c, 404, "", "Not found %.*s\n", hm->uri.len, hm->uri.ptr);
|
||||||
} else {
|
} else {
|
||||||
FILE *fp = fopen(real, "r");
|
FILE *fp = fopen(t2, "r");
|
||||||
#if MG_ENABLE_SSI
|
#if MG_ENABLE_SSI
|
||||||
if (is_index && fp == NULL) {
|
if (is_index && fp == NULL) {
|
||||||
char *p = real + strlen(real);
|
char *p = t2 + strlen(t2);
|
||||||
while (p > real && p[-1] != '/') p--;
|
while (p > t2 && p[-1] != '/') p--;
|
||||||
strncpy(p, "index.shtml", &real[sizeof(real)] - p - 2);
|
strncpy(p, "index.shtml", &t2[sizeof(t2)] - p - 2);
|
||||||
real[sizeof(real) - 1] = '\0';
|
t2[sizeof(t2) - 1] = '\0';
|
||||||
fp = fopen(real, "r");
|
fp = fopen(t2, "r");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if MG_ENABLE_HTTP_DEBUG_ENDPOINT
|
#if MG_ENABLE_HTTP_DEBUG_ENDPOINT
|
||||||
snprintf(c->label, sizeof(c->label) - 1, "<-F %s", real);
|
snprintf(c->label, sizeof(c->label) - 1, "<-F %s", t2);
|
||||||
#endif
|
#endif
|
||||||
if (is_index && fp == NULL) {
|
if (is_index && fp == NULL) {
|
||||||
#if MG_ENABLE_DIRECTORY_LISTING
|
#if MG_ENABLE_DIRECTORY_LISTING
|
||||||
listdir(c, hm, real);
|
listdir(c, hm, t2);
|
||||||
#else
|
#else
|
||||||
mg_http_reply(c, 403, "", "%s", "Directory listing not supported");
|
mg_http_reply(c, 403, "", "%s", "Directory listing not supported");
|
||||||
#endif
|
#endif
|
||||||
#if MG_ENABLE_SSI
|
#if MG_ENABLE_SSI
|
||||||
} else if (opts->ssi_pattern != NULL &&
|
} else if (opts->ssi_pattern != NULL &&
|
||||||
mg_globmatch(opts->ssi_pattern, strlen(opts->ssi_pattern),
|
mg_globmatch(opts->ssi_pattern, strlen(opts->ssi_pattern), t2,
|
||||||
real, strlen(real))) {
|
strlen(t2))) {
|
||||||
mg_http_serve_ssi(c, root, real);
|
t1[n1] = '\0';
|
||||||
|
mg_http_serve_ssi(c, t1, t2);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
mg_http_serve_file(c, hm, real, guess_content_type(real), NULL);
|
mg_http_serve_file(c, hm, t2, guess_content_type(t2), NULL);
|
||||||
}
|
}
|
||||||
if (fp != NULL) fclose(fp);
|
if (fp != NULL) fclose(fp);
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ static char *mg_ssi(const char *path, const char *root, int depth) {
|
|||||||
if (intag && ch == '>' && buf[len - 1] == '-' && buf[len - 2] == '-') {
|
if (intag && ch == '>' && buf[len - 1] == '-' && buf[len - 2] == '-') {
|
||||||
buf[len++] = ch & 0xff;
|
buf[len++] = ch & 0xff;
|
||||||
if (sscanf(buf, "<!--#include file=\"%[^\"]", arg)) {
|
if (sscanf(buf, "<!--#include file=\"%[^\"]", arg)) {
|
||||||
char tmp[PATH_MAX], *p = (char *) path + strlen(path), *data;
|
char tmp[MG_PATH_MAX], *p = (char *) path + strlen(path), *data;
|
||||||
while (p > path && p[-1] != MG_DIRSEP && p[-1] != '/') p--;
|
while (p > path && p[-1] != MG_DIRSEP && p[-1] != '/') p--;
|
||||||
snprintf(tmp, sizeof(tmp), "%.*s%s", (int) (p - path), path, arg);
|
snprintf(tmp, sizeof(tmp), "%.*s%s", (int) (p - path), path, arg);
|
||||||
if (depth < MG_MAX_SSI_DEPTH &&
|
if (depth < MG_MAX_SSI_DEPTH &&
|
||||||
@ -28,7 +28,7 @@ static char *mg_ssi(const char *path, const char *root, int depth) {
|
|||||||
LOG(LL_ERROR, ("%s: file=%s error or too deep", path, arg));
|
LOG(LL_ERROR, ("%s: file=%s error or too deep", path, arg));
|
||||||
}
|
}
|
||||||
} else if (sscanf(buf, "<!--#include virtual=\"%[^\"]", arg)) {
|
} else if (sscanf(buf, "<!--#include virtual=\"%[^\"]", arg)) {
|
||||||
char tmp[PATH_MAX], *data;
|
char tmp[MG_PATH_MAX], *data;
|
||||||
snprintf(tmp, sizeof(tmp), "%s%s", root, arg);
|
snprintf(tmp, sizeof(tmp), "%s%s", root, arg);
|
||||||
if (depth < MG_MAX_SSI_DEPTH &&
|
if (depth < MG_MAX_SSI_DEPTH &&
|
||||||
(data = mg_ssi(tmp, root, depth + 1)) != NULL) {
|
(data = mg_ssi(tmp, root, depth + 1)) != NULL) {
|
||||||
|
@ -34,7 +34,7 @@ char *mg_file_read(const char *path) {
|
|||||||
bool mg_file_write(const char *path, const void *buf, size_t len) {
|
bool mg_file_write(const char *path, const void *buf, size_t len) {
|
||||||
bool result = false;
|
bool result = false;
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
char tmp[PATH_MAX];
|
char tmp[MG_PATH_MAX];
|
||||||
snprintf(tmp, sizeof(tmp), "%s.%d", path, rand());
|
snprintf(tmp, sizeof(tmp), "%s.%d", path, rand());
|
||||||
fp = fopen(tmp, "wb");
|
fp = fopen(tmp, "wb");
|
||||||
if (fp != NULL) {
|
if (fp != NULL) {
|
||||||
|
Loading…
Reference in New Issue
Block a user