mirror of
https://github.com/cesanta/mongoose.git
synced 2026-11-07 16:01:30 +00:00
Add FS API layer
This commit is contained in:
1 parent
b98556a4dc
commit
9346122c29
10 files changed
+660
-309
No files matched your search
+95
-85
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user