Support multiple web roots

This commit is contained in:
Sergey Lyubka 2021-09-20 12:24:44 +01:00
parent 2b48e8b03a
commit 2139fbc4b7
4 changed files with 149 additions and 66 deletions

View File

@ -915,7 +915,8 @@ enable SSI, set a `-DMG_ENABLE_SSI=1` build flag.
Parameters:
- `c` - connection to use
- `hm` - http message, that should be served
- `opts` - serve options
- `opts` - serve options. Note that `opts.root_dir` can optionally accept
extra comma-separated `uri=path` pairs, see example below
Return value: none
@ -928,7 +929,7 @@ void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
struct mg_http_serve_opts opts;
memset(&opts, 0, sizeof(opts));
opts.root_dir = "/my_root";
opts.root_dir = "/var/www,/conf=/etc"; // Serve /var/www. URIs starting with /conf are served from /etc
mg_http_serve_dir(c, hm, &opts);
}
}

View File

@ -1457,54 +1457,80 @@ static void remove_double_dots(char *s) {
}
// Resolve requested file into `path` and return its fs->stat() result
static int uri_to_path2(struct mg_connection *c, struct mg_http_message *hm,
struct mg_fs *fs, struct mg_str url, struct mg_str dir,
char *path, size_t path_size) {
int flags = 0, tmp;
// Append URI to the root_dir, and sanitize it
size_t n = (size_t) snprintf(path, path_size, "%.*s", (int) dir.len, dir.ptr);
if (n > path_size) n = path_size;
path[path_size - 1] = '\0';
if ((fs->stat(path, NULL, NULL) & MG_FS_DIR) == 0) {
mg_http_reply(c, 400, "", "Invalid web root [%.*s]\n", (int) dir.len,
dir.ptr);
} else {
if (n + 2 < path_size) path[n++] = '/', path[n] = '\0';
mg_url_decode(hm->uri.ptr + url.len, hm->uri.len - url.len, path + n,
path_size - n, 0);
path[path_size - 1] = '\0'; // Double-check
remove_double_dots(path);
n = strlen(path);
LOG(LL_DEBUG, ("--> %s", path));
while (n > 0 && path[n - 1] == '/') path[--n] = 0; // Trim trailing slashes
flags = fs->stat(path, NULL, NULL); // Does it exist?
if (flags == 0) {
mg_http_reply(c, 404, "", "Not found\n"); // Does not exist, doh
} else if (flags & MG_FS_DIR) {
if (((snprintf(path + n, path_size - n, "/index.html") > 0 &&
(tmp = fs->stat(path, NULL, NULL)) != 0) ||
(snprintf(path + n, path_size - n, "/index.shtml") > 0 &&
(tmp = fs->stat(path, NULL, NULL)) != 0))) {
flags = tmp;
} else {
path[n] = '\0'; // Remove appended index file name
}
}
}
return flags;
}
static int uri_to_path(struct mg_connection *c, struct mg_http_message *hm,
struct mg_http_serve_opts *opts, char *path,
size_t path_size) {
struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
int flags = 0, tmp;
// Append URI to the root_dir, and sanitize it
size_t n = (size_t) snprintf(path, path_size, "%s", opts->root_dir);
if (n > path_size) n = path_size;
mg_url_decode(hm->uri.ptr, hm->uri.len, path + n, path_size - n, 0);
path[path_size - 1] = '\0'; // Double-check
remove_double_dots(path);
n = strlen(path);
while (n > 0 && path[n - 1] == '/') path[--n] = 0; // Strip trailing slashes
flags = fs->stat(path, NULL, NULL); // Does it exist?
if (flags == 0) {
mg_http_reply(c, 404, "", "Not found\n"); // Does not exist, doh
} else if (flags & MG_FS_DIR) {
if (((snprintf(path + n, path_size - n, "/index.html") > 0 &&
(tmp = fs->stat(path, NULL, NULL)) != 0) ||
(snprintf(path + n, path_size - n, "/index.shtml") > 0 &&
(tmp = fs->stat(path, NULL, NULL)) != 0))) {
flags = tmp;
} else {
path[n] = '\0'; // Remove appended index file name
}
struct mg_str k, v, s = mg_str(opts->root_dir), u = {0, 0}, p = {0, 0};
while (mg_commalist(&s, &k, &v)) {
if (v.len == 0) v = k, k = mg_str("/");
if (hm->uri.len < k.len) continue;
if (mg_strcmp(k, mg_str_n(hm->uri.ptr, k.len)) != 0) continue;
u = k, p = v;
}
return flags;
return uri_to_path2(c, hm, fs, u, p, path, path_size);
}
void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
struct mg_http_serve_opts *opts) {
char path[MG_PATH_MAX] = "";
const char *sp = opts->ssi_pattern;
#if 0
struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
if ((fs->stat(opts->root_dir, NULL, NULL) & MG_FS_DIR) == 0) {
mg_http_reply(c, 400, "", "Invalid web root [%s]\n", opts->root_dir);
} else {
int flags = uri_to_path(c, hm, opts, path, sizeof(path));
if (flags == 0) return;
LOG(LL_DEBUG, ("%.*s %s %d", (int) hm->uri.len, hm->uri.ptr, path, flags));
if (flags & MG_FS_DIR) {
listdir(c, hm, opts, path);
} else if (sp != NULL && mg_globmatch(sp, strlen(sp), path, strlen(path))) {
mg_http_serve_ssi(c, opts->root_dir, path);
} else {
mg_http_serve_file(c, hm, path, opts);
}
#endif
int flags = uri_to_path(c, hm, opts, path, sizeof(path));
if (flags == 0) return;
LOG(LL_DEBUG, ("%.*s %s %d", (int) hm->uri.len, hm->uri.ptr, path, flags));
if (flags & MG_FS_DIR) {
listdir(c, hm, opts, path);
} else if (sp != NULL && mg_globmatch(sp, strlen(sp), path, strlen(path))) {
mg_http_serve_ssi(c, opts->root_dir, path);
} else {
mg_http_serve_file(c, hm, path, opts);
}
#if 0
}
#endif
}
static bool mg_is_url_safe(int c) {

View File

@ -707,54 +707,80 @@ static void remove_double_dots(char *s) {
}
// Resolve requested file into `path` and return its fs->stat() result
static int uri_to_path2(struct mg_connection *c, struct mg_http_message *hm,
struct mg_fs *fs, struct mg_str url, struct mg_str dir,
char *path, size_t path_size) {
int flags = 0, tmp;
// Append URI to the root_dir, and sanitize it
size_t n = (size_t) snprintf(path, path_size, "%.*s", (int) dir.len, dir.ptr);
if (n > path_size) n = path_size;
path[path_size - 1] = '\0';
if ((fs->stat(path, NULL, NULL) & MG_FS_DIR) == 0) {
mg_http_reply(c, 400, "", "Invalid web root [%.*s]\n", (int) dir.len,
dir.ptr);
} else {
if (n + 2 < path_size) path[n++] = '/', path[n] = '\0';
mg_url_decode(hm->uri.ptr + url.len, hm->uri.len - url.len, path + n,
path_size - n, 0);
path[path_size - 1] = '\0'; // Double-check
remove_double_dots(path);
n = strlen(path);
LOG(LL_DEBUG, ("--> %s", path));
while (n > 0 && path[n - 1] == '/') path[--n] = 0; // Trim trailing slashes
flags = fs->stat(path, NULL, NULL); // Does it exist?
if (flags == 0) {
mg_http_reply(c, 404, "", "Not found\n"); // Does not exist, doh
} else if (flags & MG_FS_DIR) {
if (((snprintf(path + n, path_size - n, "/index.html") > 0 &&
(tmp = fs->stat(path, NULL, NULL)) != 0) ||
(snprintf(path + n, path_size - n, "/index.shtml") > 0 &&
(tmp = fs->stat(path, NULL, NULL)) != 0))) {
flags = tmp;
} else {
path[n] = '\0'; // Remove appended index file name
}
}
}
return flags;
}
static int uri_to_path(struct mg_connection *c, struct mg_http_message *hm,
struct mg_http_serve_opts *opts, char *path,
size_t path_size) {
struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
int flags = 0, tmp;
// Append URI to the root_dir, and sanitize it
size_t n = (size_t) snprintf(path, path_size, "%s", opts->root_dir);
if (n > path_size) n = path_size;
mg_url_decode(hm->uri.ptr, hm->uri.len, path + n, path_size - n, 0);
path[path_size - 1] = '\0'; // Double-check
remove_double_dots(path);
n = strlen(path);
while (n > 0 && path[n - 1] == '/') path[--n] = 0; // Strip trailing slashes
flags = fs->stat(path, NULL, NULL); // Does it exist?
if (flags == 0) {
mg_http_reply(c, 404, "", "Not found\n"); // Does not exist, doh
} else if (flags & MG_FS_DIR) {
if (((snprintf(path + n, path_size - n, "/index.html") > 0 &&
(tmp = fs->stat(path, NULL, NULL)) != 0) ||
(snprintf(path + n, path_size - n, "/index.shtml") > 0 &&
(tmp = fs->stat(path, NULL, NULL)) != 0))) {
flags = tmp;
} else {
path[n] = '\0'; // Remove appended index file name
}
struct mg_str k, v, s = mg_str(opts->root_dir), u = {0, 0}, p = {0, 0};
while (mg_commalist(&s, &k, &v)) {
if (v.len == 0) v = k, k = mg_str("/");
if (hm->uri.len < k.len) continue;
if (mg_strcmp(k, mg_str_n(hm->uri.ptr, k.len)) != 0) continue;
u = k, p = v;
}
return flags;
return uri_to_path2(c, hm, fs, u, p, path, path_size);
}
void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
struct mg_http_serve_opts *opts) {
char path[MG_PATH_MAX] = "";
const char *sp = opts->ssi_pattern;
#if 0
struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
if ((fs->stat(opts->root_dir, NULL, NULL) & MG_FS_DIR) == 0) {
mg_http_reply(c, 400, "", "Invalid web root [%s]\n", opts->root_dir);
} else {
int flags = uri_to_path(c, hm, opts, path, sizeof(path));
if (flags == 0) return;
LOG(LL_DEBUG, ("%.*s %s %d", (int) hm->uri.len, hm->uri.ptr, path, flags));
if (flags & MG_FS_DIR) {
listdir(c, hm, opts, path);
} else if (sp != NULL && mg_globmatch(sp, strlen(sp), path, strlen(path))) {
mg_http_serve_ssi(c, opts->root_dir, path);
} else {
mg_http_serve_file(c, hm, path, opts);
}
#endif
int flags = uri_to_path(c, hm, opts, path, sizeof(path));
if (flags == 0) return;
LOG(LL_DEBUG, ("%.*s %s %d", (int) hm->uri.len, hm->uri.ptr, path, flags));
if (flags & MG_FS_DIR) {
listdir(c, hm, opts, path);
} else if (sp != NULL && mg_globmatch(sp, strlen(sp), path, strlen(path))) {
mg_http_serve_ssi(c, opts->root_dir, path);
} else {
mg_http_serve_file(c, hm, path, opts);
}
#if 0
}
#endif
}
static bool mg_is_url_safe(int c) {

View File

@ -1555,8 +1555,38 @@ static void test_ws_fragmentation(void) {
ASSERT(mgr.conns == NULL);
}
static void h7(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
struct mg_http_serve_opts opts;
memset(&opts, 0, sizeof(opts));
opts.root_dir = "./test/data,/foo=./src";
mg_http_serve_dir(c, hm, &opts);
}
(void) fn_data;
}
static void test_rewrites(void) {
char buf[FETCH_BUF_SIZE];
const char *url = "http://LOCALHOST:12358";
const char *expected = "#define MG_VERSION \"" MG_VERSION "\"\n";
struct mg_mgr mgr;
mg_mgr_init(&mgr);
ASSERT(mg_http_listen(&mgr, url, h7, NULL) != NULL);
ASSERT(fetch(&mgr, buf, url, "GET /a.txt HTTP/1.0\n\n") == 200);
ASSERT(cmpbody(buf, "hello\n") == 0);
ASSERT(fetch(&mgr, buf, url, "GET /foo/version.h HTTP/1.0\n\n") == 200);
ASSERT(cmpbody(buf, expected) == 0);
ASSERT(fetch(&mgr, buf, url, "GET /foo HTTP/1.0\n\n") == 200);
// printf("-->[%s]\n", buf);
// exit(0);
mg_mgr_free(&mgr);
ASSERT(mgr.conns == NULL);
}
int main(void) {
mg_log_set("3");
test_rewrites();
test_check_ip_acl();
test_udp();
test_pipe();