Add FS API layer

This commit is contained in:
Sergey Lyubka committed 2021-07-28 21:11:07 +01:00
1 parent b98556a4dc
commit 9346122c29
10 files changed
+660 -309

No files matched your search

+95 -85
View File
@@ -420,73 +420,83 @@ static void static_cb(struct mg_connection *c, int ev, void *ev_data,
(void) ev_data;
}
static const char *guess_content_type(const char *filename) {
size_t n = strlen(filename);
#define MIME_ENTRY(_ext, _type) \
{ _ext, sizeof(_ext) - 1, _type }
const struct {
const char *ext;
size_t ext_len;
const char *type;
} * t, types[] = {
MIME_ENTRY("html", "text/html; charset=utf-8"),
MIME_ENTRY("htm", "text/html; charset=utf-8"),
MIME_ENTRY("css", "text/css; charset=utf-8"),
MIME_ENTRY("js", "text/javascript; charset=utf-8"),
MIME_ENTRY("gif", "image/gif"),
MIME_ENTRY("png", "image/png"),
MIME_ENTRY("woff", "font/woff"),
MIME_ENTRY("ttf", "font/ttf"),
MIME_ENTRY("aac", "audio/aac"),
MIME_ENTRY("avi", "video/x-msvideo"),
MIME_ENTRY("azw", "application/vnd.amazon.ebook"),
MIME_ENTRY("bin", "application/octet-stream"),
MIME_ENTRY("bmp", "image/bmp"),
MIME_ENTRY("bz", "application/x-bzip"),
MIME_ENTRY("bz2", "application/x-bzip2"),
MIME_ENTRY("csv", "text/csv"),
MIME_ENTRY("doc", "application/msword"),
MIME_ENTRY("epub", "application/epub+zip"),
MIME_ENTRY("exe", "application/octet-stream"),
MIME_ENTRY("gz", "application/gzip"),
MIME_ENTRY("ico", "image/x-icon"),
MIME_ENTRY("json", "application/json"),
MIME_ENTRY("mid", "audio/mid"),
MIME_ENTRY("mjs", "text/javascript"),
MIME_ENTRY("mov", "video/quicktime"),
MIME_ENTRY("mp3", "audio/mpeg"),
MIME_ENTRY("mp4", "video/mp4"),
MIME_ENTRY("mpeg", "video/mpeg"),
MIME_ENTRY("mpg", "video/mpeg"),
MIME_ENTRY("ogg", "application/ogg"),
MIME_ENTRY("pdf", "application/pdf"),
MIME_ENTRY("rar", "application/rar"),
MIME_ENTRY("rtf", "application/rtf"),
MIME_ENTRY("shtml", "text/html; charset=utf-8"),
MIME_ENTRY("svg", "image/svg+xml"),
MIME_ENTRY("tar", "application/tar"),
MIME_ENTRY("tgz", "application/tar-gz"),
MIME_ENTRY("txt", "text/plain; charset=utf-8"),
MIME_ENTRY("wasm", "application/wasm"),
MIME_ENTRY("wav", "audio/wav"),
MIME_ENTRY("weba", "audio/webm"),
MIME_ENTRY("webm", "video/webm"),
MIME_ENTRY("webp", "image/webp"),
MIME_ENTRY("xls", "application/excel"),
MIME_ENTRY("xml", "application/xml"),
MIME_ENTRY("xsl", "application/xml"),
MIME_ENTRY("zip", "application/zip"),
MIME_ENTRY("3gp", "video/3gpp"),
MIME_ENTRY("7z", "application/x-7z-compressed"),
{NULL, 0, NULL},
};
static struct mg_str guess_content_type(struct mg_str path, const char *extra) {
// clang-format off
struct mimeentry { struct mg_str extension, value; };
#define MIME_ENTRY(a, b) {{a, sizeof(a) - 1 }, { b, sizeof(b) - 1 }}
// clang-format on
const struct mimeentry tab[] = {
MIME_ENTRY("html", "text/html; charset=utf-8"),
MIME_ENTRY("htm", "text/html; charset=utf-8"),
MIME_ENTRY("css", "text/css; charset=utf-8"),
MIME_ENTRY("js", "text/javascript; charset=utf-8"),
MIME_ENTRY("gif", "image/gif"),
MIME_ENTRY("png", "image/png"),
MIME_ENTRY("woff", "font/woff"),
MIME_ENTRY("ttf", "font/ttf"),
MIME_ENTRY("aac", "audio/aac"),
MIME_ENTRY("avi", "video/x-msvideo"),
MIME_ENTRY("azw", "application/vnd.amazon.ebook"),
MIME_ENTRY("bin", "application/octet-stream"),
MIME_ENTRY("bmp", "image/bmp"),
MIME_ENTRY("bz", "application/x-bzip"),
MIME_ENTRY("bz2", "application/x-bzip2"),
MIME_ENTRY("csv", "text/csv"),
MIME_ENTRY("doc", "application/msword"),
MIME_ENTRY("epub", "application/epub+zip"),
MIME_ENTRY("exe", "application/octet-stream"),
MIME_ENTRY("gz", "application/gzip"),
MIME_ENTRY("ico", "image/x-icon"),
MIME_ENTRY("json", "application/json"),
MIME_ENTRY("mid", "audio/mid"),
MIME_ENTRY("mjs", "text/javascript"),
MIME_ENTRY("mov", "video/quicktime"),
MIME_ENTRY("mp3", "audio/mpeg"),
MIME_ENTRY("mp4", "video/mp4"),
MIME_ENTRY("mpeg", "video/mpeg"),
MIME_ENTRY("mpg", "video/mpeg"),
MIME_ENTRY("ogg", "application/ogg"),
MIME_ENTRY("pdf", "application/pdf"),
MIME_ENTRY("rar", "application/rar"),
MIME_ENTRY("rtf", "application/rtf"),
MIME_ENTRY("shtml", "text/html; charset=utf-8"),
MIME_ENTRY("svg", "image/svg+xml"),
MIME_ENTRY("tar", "application/tar"),
MIME_ENTRY("tgz", "application/tar-gz"),
MIME_ENTRY("txt", "text/plain; charset=utf-8"),
MIME_ENTRY("wasm", "application/wasm"),
MIME_ENTRY("wav", "audio/wav"),
MIME_ENTRY("weba", "audio/webm"),
MIME_ENTRY("webm", "video/webm"),
MIME_ENTRY("webp", "image/webp"),
MIME_ENTRY("xls", "application/excel"),
MIME_ENTRY("xml", "application/xml"),
MIME_ENTRY("xsl", "application/xml"),
MIME_ENTRY("zip", "application/zip"),
MIME_ENTRY("3gp", "video/3gpp"),
MIME_ENTRY("7z", "application/x-7z-compressed"),
MIME_ENTRY("7z", "application/x-7z-compressed"),
{{0, 0}, {0, 0}},
};
size_t i = 0;
struct mg_str k, v, s = mg_str(extra);
for (t = types; t->ext != NULL; t++) {
if (n < t->ext_len + 2) continue;
if (mg_ncasecmp(t->ext, &filename[n - t->ext_len], t->ext_len)) continue;
return t->type;
// Shrink path to its extension only
while (i < path.len && path.ptr[path.len - i - 1] != '.') i++;
path.ptr += path.len - i;
path.len = i;
// Process user-provided mime type overrides, if any
while (mg_next_comma_entry(&s, &k, &v)) {
if (mg_strcmp(path, k) == 0) return v;
}
return "text/plain; charset=utf-8";
// Process built-in mime types
for (i = 0; tab[i].extension.ptr != NULL; i++) {
if (mg_strcmp(path, tab[i].extension) == 0) return tab[i].value;
}
return mg_str("text/plain; charset=utf-8");
}
static int getrange(struct mg_str *s, int64_t *a, int64_t *b) {
@@ -510,7 +520,7 @@ static int getrange(struct mg_str *s, int64_t *a, int64_t *b) {
}
void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
const char *path, const char *mime, const char *hdrs) {
const char *path, struct mg_http_serve_opts *opts) {
struct mg_str *inm = mg_http_get_header(hm, "If-None-Match");
struct stat st;
char etag[64];
@@ -528,6 +538,7 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
int n, status = 200;
char range[100] = "";
int64_t r1 = 0, r2 = 0, cl = st.st_size;
struct mg_str mime = guess_content_type(mg_str(path), opts->mime_types);
// Handle Range header
struct mg_str *rh = mg_http_get_header(hm, "Range");
@@ -558,10 +569,10 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
}
mg_printf(c,
"HTTP/1.1 %d %s\r\nContent-Type: %s\r\n"
"HTTP/1.1 %d %s\r\nContent-Type: %.*s\r\n"
"Etag: %s\r\nContent-Length: " MG_INT64_FMT "\r\n%s%s\r\n",
status, mg_http_status_code_str(status), mime, etag, cl, range,
hdrs ? hdrs : "");
status, mg_http_status_code_str(status), (int) mime.len, mime.ptr,
etag, cl, range, opts->extra_headers ? opts->extra_headers : "");
if (mg_vcasecmp(&hm->method, "HEAD") == 0) {
fclose(fp);
} else {
@@ -865,40 +876,39 @@ static bool uri_to_local_path(struct mg_connection *c,
void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
struct mg_http_serve_opts *opts) {
char root_dir[MG_PATH_MAX], full_path[sizeof(root_dir)];
char root_dir[MG_PATH_MAX], path[sizeof(root_dir)];
bool is_index = false, exists;
struct stat st;
root_dir[0] = full_path[0] = '\0';
root_dir[0] = path[0] = '\0';
if (!uri_to_local_path(c, hm, opts, root_dir, sizeof(root_dir), full_path,
sizeof(full_path), &is_index))
if (!uri_to_local_path(c, hm, opts, root_dir, sizeof(root_dir), path,
sizeof(path), &is_index))
return;
exists = stat(full_path, &st) == 0;
exists = stat(path, &st) == 0;
#if MG_ENABLE_SSI
if (is_index && !exists) {
char *p = full_path + strlen(full_path);
while (p > full_path && p[-1] != '/') p--;
strncpy(p, "index.shtml", (size_t)(&full_path[sizeof(full_path)] - p - 2));
full_path[sizeof(full_path) - 1] = '\0';
exists = stat(full_path, &st) == 0;
char *p = path + strlen(path);
while (p > path && p[-1] != '/') p--;
strncpy(p, "index.shtml", (size_t)(&path[sizeof(path)] - p - 2));
path[sizeof(path) - 1] = '\0';
exists = stat(path, &st) == 0;
}
#endif
if (is_index && !exists) {
#if MG_ENABLE_DIRECTORY_LISTING
listdir(c, hm, opts, full_path);
listdir(c, hm, opts, path);
#else
mg_http_reply(c, 403, "", "%s", "Directory listing not supported");
#endif
#if MG_ENABLE_SSI
} else if (opts->ssi_pattern != NULL &&
mg_globmatch(opts->ssi_pattern, strlen(opts->ssi_pattern),
full_path, strlen(full_path))) {
mg_http_serve_ssi(c, root_dir, full_path);
mg_globmatch(opts->ssi_pattern, strlen(opts->ssi_pattern), path,
strlen(path))) {
mg_http_serve_ssi(c, root_dir, path);
#endif
} else {
mg_http_serve_file(c, hm, full_path, guess_content_type(full_path),
opts->extra_headers);
mg_http_serve_file(c, hm, path, opts);
}
}