Skip to content

Commit 2139fbc

Browse files
committed
Support multiple web roots
1 parent 2b48e8b commit 2139fbc

4 files changed

Lines changed: 147 additions & 64 deletions

File tree

docs/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -915,7 +915,8 @@ enable SSI, set a `-DMG_ENABLE_SSI=1` build flag.
915915
Parameters:
916916
- `c` - connection to use
917917
- `hm` - http message, that should be served
918-
- `opts` - serve options
918+
- `opts` - serve options. Note that `opts.root_dir` can optionally accept
919+
extra comma-separated `uri=path` pairs, see example below
919920

920921
Return value: none
921922

@@ -928,7 +929,7 @@ void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
928929
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
929930
struct mg_http_serve_opts opts;
930931
memset(&opts, 0, sizeof(opts));
931-
opts.root_dir = "/my_root";
932+
opts.root_dir = "/var/www,/conf=/etc"; // Serve /var/www. URIs starting with /conf are served from /etc
932933
mg_http_serve_dir(c, hm, &opts);
933934
}
934935
}

mongoose.c

Lines changed: 57 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1457,54 +1457,80 @@ static void remove_double_dots(char *s) {
14571457
}
14581458

14591459
// Resolve requested file into `path` and return its fs->stat() result
1460-
static int uri_to_path(struct mg_connection *c, struct mg_http_message *hm,
1461-
struct mg_http_serve_opts *opts, char *path,
1462-
size_t path_size) {
1463-
struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
1460+
static int uri_to_path2(struct mg_connection *c, struct mg_http_message *hm,
1461+
struct mg_fs *fs, struct mg_str url, struct mg_str dir,
1462+
char *path, size_t path_size) {
14641463
int flags = 0, tmp;
14651464
// Append URI to the root_dir, and sanitize it
1466-
size_t n = (size_t) snprintf(path, path_size, "%s", opts->root_dir);
1465+
size_t n = (size_t) snprintf(path, path_size, "%.*s", (int) dir.len, dir.ptr);
14671466
if (n > path_size) n = path_size;
1468-
mg_url_decode(hm->uri.ptr, hm->uri.len, path + n, path_size - n, 0);
1469-
path[path_size - 1] = '\0'; // Double-check
1470-
remove_double_dots(path);
1471-
n = strlen(path);
1472-
while (n > 0 && path[n - 1] == '/') path[--n] = 0; // Strip trailing slashes
1473-
flags = fs->stat(path, NULL, NULL); // Does it exist?
1474-
if (flags == 0) {
1475-
mg_http_reply(c, 404, "", "Not found\n"); // Does not exist, doh
1476-
} else if (flags & MG_FS_DIR) {
1477-
if (((snprintf(path + n, path_size - n, "/index.html") > 0 &&
1478-
(tmp = fs->stat(path, NULL, NULL)) != 0) ||
1479-
(snprintf(path + n, path_size - n, "/index.shtml") > 0 &&
1480-
(tmp = fs->stat(path, NULL, NULL)) != 0))) {
1481-
flags = tmp;
1482-
} else {
1483-
path[n] = '\0'; // Remove appended index file name
1467+
path[path_size - 1] = '\0';
1468+
if ((fs->stat(path, NULL, NULL) & MG_FS_DIR) == 0) {
1469+
mg_http_reply(c, 400, "", "Invalid web root [%.*s]\n", (int) dir.len,
1470+
dir.ptr);
1471+
} else {
1472+
if (n + 2 < path_size) path[n++] = '/', path[n] = '\0';
1473+
mg_url_decode(hm->uri.ptr + url.len, hm->uri.len - url.len, path + n,
1474+
path_size - n, 0);
1475+
path[path_size - 1] = '\0'; // Double-check
1476+
remove_double_dots(path);
1477+
n = strlen(path);
1478+
LOG(LL_DEBUG, ("--> %s", path));
1479+
while (n > 0 && path[n - 1] == '/') path[--n] = 0; // Trim trailing slashes
1480+
flags = fs->stat(path, NULL, NULL); // Does it exist?
1481+
if (flags == 0) {
1482+
mg_http_reply(c, 404, "", "Not found\n"); // Does not exist, doh
1483+
} else if (flags & MG_FS_DIR) {
1484+
if (((snprintf(path + n, path_size - n, "/index.html") > 0 &&
1485+
(tmp = fs->stat(path, NULL, NULL)) != 0) ||
1486+
(snprintf(path + n, path_size - n, "/index.shtml") > 0 &&
1487+
(tmp = fs->stat(path, NULL, NULL)) != 0))) {
1488+
flags = tmp;
1489+
} else {
1490+
path[n] = '\0'; // Remove appended index file name
1491+
}
14841492
}
14851493
}
14861494
return flags;
14871495
}
14881496

1497+
static int uri_to_path(struct mg_connection *c, struct mg_http_message *hm,
1498+
struct mg_http_serve_opts *opts, char *path,
1499+
size_t path_size) {
1500+
struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
1501+
struct mg_str k, v, s = mg_str(opts->root_dir), u = {0, 0}, p = {0, 0};
1502+
while (mg_commalist(&s, &k, &v)) {
1503+
if (v.len == 0) v = k, k = mg_str("/");
1504+
if (hm->uri.len < k.len) continue;
1505+
if (mg_strcmp(k, mg_str_n(hm->uri.ptr, k.len)) != 0) continue;
1506+
u = k, p = v;
1507+
}
1508+
return uri_to_path2(c, hm, fs, u, p, path, path_size);
1509+
}
1510+
14891511
void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
14901512
struct mg_http_serve_opts *opts) {
14911513
char path[MG_PATH_MAX] = "";
14921514
const char *sp = opts->ssi_pattern;
1515+
#if 0
14931516
struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
14941517
if ((fs->stat(opts->root_dir, NULL, NULL) & MG_FS_DIR) == 0) {
14951518
mg_http_reply(c, 400, "", "Invalid web root [%s]\n", opts->root_dir);
14961519
} else {
1497-
int flags = uri_to_path(c, hm, opts, path, sizeof(path));
1498-
if (flags == 0) return;
1499-
LOG(LL_DEBUG, ("%.*s %s %d", (int) hm->uri.len, hm->uri.ptr, path, flags));
1500-
if (flags & MG_FS_DIR) {
1501-
listdir(c, hm, opts, path);
1502-
} else if (sp != NULL && mg_globmatch(sp, strlen(sp), path, strlen(path))) {
1503-
mg_http_serve_ssi(c, opts->root_dir, path);
1504-
} else {
1505-
mg_http_serve_file(c, hm, path, opts);
1506-
}
1520+
#endif
1521+
int flags = uri_to_path(c, hm, opts, path, sizeof(path));
1522+
if (flags == 0) return;
1523+
LOG(LL_DEBUG, ("%.*s %s %d", (int) hm->uri.len, hm->uri.ptr, path, flags));
1524+
if (flags & MG_FS_DIR) {
1525+
listdir(c, hm, opts, path);
1526+
} else if (sp != NULL && mg_globmatch(sp, strlen(sp), path, strlen(path))) {
1527+
mg_http_serve_ssi(c, opts->root_dir, path);
1528+
} else {
1529+
mg_http_serve_file(c, hm, path, opts);
15071530
}
1531+
#if 0
1532+
}
1533+
#endif
15081534
}
15091535

15101536
static bool mg_is_url_safe(int c) {

src/http.c

Lines changed: 57 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -707,54 +707,80 @@ static void remove_double_dots(char *s) {
707707
}
708708

709709
// Resolve requested file into `path` and return its fs->stat() result
710-
static int uri_to_path(struct mg_connection *c, struct mg_http_message *hm,
711-
struct mg_http_serve_opts *opts, char *path,
712-
size_t path_size) {
713-
struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
710+
static int uri_to_path2(struct mg_connection *c, struct mg_http_message *hm,
711+
struct mg_fs *fs, struct mg_str url, struct mg_str dir,
712+
char *path, size_t path_size) {
714713
int flags = 0, tmp;
715714
// Append URI to the root_dir, and sanitize it
716-
size_t n = (size_t) snprintf(path, path_size, "%s", opts->root_dir);
715+
size_t n = (size_t) snprintf(path, path_size, "%.*s", (int) dir.len, dir.ptr);
717716
if (n > path_size) n = path_size;
718-
mg_url_decode(hm->uri.ptr, hm->uri.len, path + n, path_size - n, 0);
719-
path[path_size - 1] = '\0'; // Double-check
720-
remove_double_dots(path);
721-
n = strlen(path);
722-
while (n > 0 && path[n - 1] == '/') path[--n] = 0; // Strip trailing slashes
723-
flags = fs->stat(path, NULL, NULL); // Does it exist?
724-
if (flags == 0) {
725-
mg_http_reply(c, 404, "", "Not found\n"); // Does not exist, doh
726-
} else if (flags & MG_FS_DIR) {
727-
if (((snprintf(path + n, path_size - n, "/index.html") > 0 &&
728-
(tmp = fs->stat(path, NULL, NULL)) != 0) ||
729-
(snprintf(path + n, path_size - n, "/index.shtml") > 0 &&
730-
(tmp = fs->stat(path, NULL, NULL)) != 0))) {
731-
flags = tmp;
732-
} else {
733-
path[n] = '\0'; // Remove appended index file name
717+
path[path_size - 1] = '\0';
718+
if ((fs->stat(path, NULL, NULL) & MG_FS_DIR) == 0) {
719+
mg_http_reply(c, 400, "", "Invalid web root [%.*s]\n", (int) dir.len,
720+
dir.ptr);
721+
} else {
722+
if (n + 2 < path_size) path[n++] = '/', path[n] = '\0';
723+
mg_url_decode(hm->uri.ptr + url.len, hm->uri.len - url.len, path + n,
724+
path_size - n, 0);
725+
path[path_size - 1] = '\0'; // Double-check
726+
remove_double_dots(path);
727+
n = strlen(path);
728+
LOG(LL_DEBUG, ("--> %s", path));
729+
while (n > 0 && path[n - 1] == '/') path[--n] = 0; // Trim trailing slashes
730+
flags = fs->stat(path, NULL, NULL); // Does it exist?
731+
if (flags == 0) {
732+
mg_http_reply(c, 404, "", "Not found\n"); // Does not exist, doh
733+
} else if (flags & MG_FS_DIR) {
734+
if (((snprintf(path + n, path_size - n, "/index.html") > 0 &&
735+
(tmp = fs->stat(path, NULL, NULL)) != 0) ||
736+
(snprintf(path + n, path_size - n, "/index.shtml") > 0 &&
737+
(tmp = fs->stat(path, NULL, NULL)) != 0))) {
738+
flags = tmp;
739+
} else {
740+
path[n] = '\0'; // Remove appended index file name
741+
}
734742
}
735743
}
736744
return flags;
737745
}
738746

747+
static int uri_to_path(struct mg_connection *c, struct mg_http_message *hm,
748+
struct mg_http_serve_opts *opts, char *path,
749+
size_t path_size) {
750+
struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
751+
struct mg_str k, v, s = mg_str(opts->root_dir), u = {0, 0}, p = {0, 0};
752+
while (mg_commalist(&s, &k, &v)) {
753+
if (v.len == 0) v = k, k = mg_str("/");
754+
if (hm->uri.len < k.len) continue;
755+
if (mg_strcmp(k, mg_str_n(hm->uri.ptr, k.len)) != 0) continue;
756+
u = k, p = v;
757+
}
758+
return uri_to_path2(c, hm, fs, u, p, path, path_size);
759+
}
760+
739761
void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
740762
struct mg_http_serve_opts *opts) {
741763
char path[MG_PATH_MAX] = "";
742764
const char *sp = opts->ssi_pattern;
765+
#if 0
743766
struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
744767
if ((fs->stat(opts->root_dir, NULL, NULL) & MG_FS_DIR) == 0) {
745768
mg_http_reply(c, 400, "", "Invalid web root [%s]\n", opts->root_dir);
746769
} else {
747-
int flags = uri_to_path(c, hm, opts, path, sizeof(path));
748-
if (flags == 0) return;
749-
LOG(LL_DEBUG, ("%.*s %s %d", (int) hm->uri.len, hm->uri.ptr, path, flags));
750-
if (flags & MG_FS_DIR) {
751-
listdir(c, hm, opts, path);
752-
} else if (sp != NULL && mg_globmatch(sp, strlen(sp), path, strlen(path))) {
753-
mg_http_serve_ssi(c, opts->root_dir, path);
754-
} else {
755-
mg_http_serve_file(c, hm, path, opts);
756-
}
770+
#endif
771+
int flags = uri_to_path(c, hm, opts, path, sizeof(path));
772+
if (flags == 0) return;
773+
LOG(LL_DEBUG, ("%.*s %s %d", (int) hm->uri.len, hm->uri.ptr, path, flags));
774+
if (flags & MG_FS_DIR) {
775+
listdir(c, hm, opts, path);
776+
} else if (sp != NULL && mg_globmatch(sp, strlen(sp), path, strlen(path))) {
777+
mg_http_serve_ssi(c, opts->root_dir, path);
778+
} else {
779+
mg_http_serve_file(c, hm, path, opts);
780+
}
781+
#if 0
757782
}
783+
#endif
758784
}
759785

760786
static bool mg_is_url_safe(int c) {

test/unit_test.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1555,8 +1555,38 @@ static void test_ws_fragmentation(void) {
15551555
ASSERT(mgr.conns == NULL);
15561556
}
15571557

1558+
static void h7(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
1559+
if (ev == MG_EV_HTTP_MSG) {
1560+
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
1561+
struct mg_http_serve_opts opts;
1562+
memset(&opts, 0, sizeof(opts));
1563+
opts.root_dir = "./test/data,/foo=./src";
1564+
mg_http_serve_dir(c, hm, &opts);
1565+
}
1566+
(void) fn_data;
1567+
}
1568+
1569+
static void test_rewrites(void) {
1570+
char buf[FETCH_BUF_SIZE];
1571+
const char *url = "http://LOCALHOST:12358";
1572+
const char *expected = "#define MG_VERSION \"" MG_VERSION "\"\n";
1573+
struct mg_mgr mgr;
1574+
mg_mgr_init(&mgr);
1575+
ASSERT(mg_http_listen(&mgr, url, h7, NULL) != NULL);
1576+
ASSERT(fetch(&mgr, buf, url, "GET /a.txt HTTP/1.0\n\n") == 200);
1577+
ASSERT(cmpbody(buf, "hello\n") == 0);
1578+
ASSERT(fetch(&mgr, buf, url, "GET /foo/version.h HTTP/1.0\n\n") == 200);
1579+
ASSERT(cmpbody(buf, expected) == 0);
1580+
ASSERT(fetch(&mgr, buf, url, "GET /foo HTTP/1.0\n\n") == 200);
1581+
// printf("-->[%s]\n", buf);
1582+
// exit(0);
1583+
mg_mgr_free(&mgr);
1584+
ASSERT(mgr.conns == NULL);
1585+
}
1586+
15581587
int main(void) {
15591588
mg_log_set("3");
1589+
test_rewrites();
15601590
test_check_ip_acl();
15611591
test_udp();
15621592
test_pipe();

0 commit comments

Comments
 (0)