summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDrew DeVault <sir@cmpwn.com>2020-10-25 21:46:01 -0400
committerDrew DeVault <sir@cmpwn.com>2020-10-25 21:46:01 -0400
commit8baeb5a51c4dfa03956887ade2ef77295f17c95e (patch)
tree533a043ed887483b905f6a684986ebaf9072d11c /src
parent1fe107875b05cc07cf62c714c0136026eef7b93a (diff)
downloadgmnisrv-8baeb5a51c4dfa03956887ade2ef77295f17c95e.tar.gz
gmnisrv-8baeb5a51c4dfa03956887ade2ef77295f17c95e.tar.xz
gmnisrv-8baeb5a51c4dfa03956887ade2ef77295f17c95e.zip
Initial implementation of a routing table
Diffstat (limited to 'src')
-rw-r--r--src/config.c91
-rw-r--r--src/serve.c53
2 files changed, 126 insertions, 18 deletions
diff --git a/src/config.c b/src/config.c
index 36060cf..693766e 100644
--- a/src/config.c
+++ b/src/config.c
@@ -21,6 +21,21 @@ gmnisrv_config_get_host(struct gmnisrv_config *conf, const char *hostname)
return NULL;
}
+struct gmnisrv_route *
+gmnisrv_host_get_route(struct gmnisrv_host *host,
+ enum gmnisrv_routing routing, const char *spec)
+{
+ struct gmnisrv_route *route = host->routes;
+ while (route) {
+ if (route->routing == routing
+ && strcmp(route->spec, spec) == 0) {
+ return route;
+ }
+ route = route->next;
+ }
+ return NULL;
+}
+
static int
parse_listen(struct gmnisrv_config *conf, const char *value)
{
@@ -133,7 +148,29 @@ conf_ini_handler(void *user, const char *section,
return 0;
}
- struct gmnisrv_host *host = gmnisrv_config_get_host(conf, section);
+ const char *spec;
+ char hostname[1024 + 1];
+ enum gmnisrv_routing routing;
+ size_t hostln = strcspn(section, ":~");
+ switch (section[hostln]) {
+ case '\0':
+ routing = ROUTE_PATH;
+ spec = "/";
+ break;
+ case ':':
+ routing = ROUTE_PATH;
+ spec = &section[hostln + 1];
+ break;
+ case '~':
+ routing = ROUTE_REGEX;
+ spec = &section[hostln + 1];
+ break;
+ }
+ assert(hostln < sizeof(hostname));
+ strncpy(hostname, section, hostln);
+ hostname[hostln] = '\0';
+
+ struct gmnisrv_host *host = gmnisrv_config_get_host(conf, hostname);
if (!host) {
host = calloc(1, sizeof(struct gmnisrv_host));
assert(host);
@@ -142,33 +179,52 @@ conf_ini_handler(void *user, const char *section,
conf->hosts = host;
}
+ struct gmnisrv_route *route =
+ gmnisrv_host_get_route(host, routing, spec);
+ if (!route) {
+ route = calloc(1, sizeof(struct gmnisrv_route));
+ assert(route);
+ route->spec = strdup(spec);
+ route->routing = routing;
+ route->next = host->routes;
+ host->routes = route;
+
+ switch (route->routing) {
+ case ROUTE_PATH:
+ route->path = strdup(spec);
+ break;
+ case ROUTE_REGEX:
+ assert(0); // TODO
+ }
+ }
+
struct {
char *name;
char **value;
- } host_strvars[] = {
- { "root", &host->root },
- { "index", &host->index },
+ } route_strvars[] = {
+ { "root", &route->root },
+ { "index", &route->index },
};
struct {
char *name;
bool *value;
- } host_bvars[] = {
- { "autoindex", &host->autoindex },
+ } route_bvars[] = {
+ { "autoindex", &route->autoindex },
};
- for (size_t i = 0; i < sizeof(host_strvars) / sizeof(host_strvars[0]); ++i) {
- if (strcmp(host_strvars[i].name, name) != 0) {
+ for (size_t i = 0; i < sizeof(route_strvars) / sizeof(route_strvars[0]); ++i) {
+ if (strcmp(route_strvars[i].name, name) != 0) {
continue;
}
- *host_strvars[i].value = strdup(value);
+ *route_strvars[i].value = strdup(value);
return 1;
}
- for (size_t i = 0; i < sizeof(host_bvars) / sizeof(host_bvars[0]); ++i) {
- if (strcmp(host_bvars[i].name, name) != 0) {
+ for (size_t i = 0; i < sizeof(route_bvars) / sizeof(route_bvars[0]); ++i) {
+ if (strcmp(route_bvars[i].name, name) != 0) {
continue;
}
- *host_bvars[i].value =
+ *route_bvars[i].value =
strcasecmp(value, "yes") == 0 ||
strcasecmp(value, "true") == 0 ||
strcasecmp(value, "on") == 0;
@@ -233,8 +289,15 @@ config_finish(struct gmnisrv_config *conf)
while (host) {
struct gmnisrv_host *next = host->next;
free(host->hostname);
- free(host->root);
- free(host->index);
+
+ struct gmnisrv_route *route = host->routes;
+ while (route) {
+ struct gmnisrv_route *rnext = route->next;
+ free(route->root);
+ free(route->index);
+ free(route);
+ route = rnext;
+ }
free(host);
host = next;
}
diff --git a/src/serve.c b/src/serve.c
index b798e7b..18c2993 100644
--- a/src/serve.c
+++ b/src/serve.c
@@ -117,15 +117,60 @@ internal_error:
goto exit;
}
+static bool
+route_match(struct gmnisrv_route *route, const char *path, const char **revised)
+{
+ switch (route->routing) {
+ case ROUTE_PATH:;
+ size_t l = strlen(route->path);
+ if (strncmp(path, route->path, l) != 0) {
+ return false;
+ }
+ if (route->path[l-1] != '/' && path[l] != '\0' && path[l] != '/') {
+ // Prevents path == "/foobar" from matching
+ // route == "/foo":
+ return false;
+ }
+ if (route->path[l-1] == '/') {
+ *revised = &path[l-1];
+ } else {
+ *revised = &path[l];
+ }
+ return true;
+ case ROUTE_REGEX:
+ assert(0); // TODO
+ }
+
+ assert(0); // Invariant
+}
+
void
serve_request(struct gmnisrv_client *client)
{
struct gmnisrv_host *host = client->host;
assert(host);
- assert(host->root); // TODO: reverse proxy support
+ struct gmnisrv_route *route = host->routes;
+ assert(route);
+
+ const char *url_path;
+ while (route) {
+ if (route_match(route, client->path, &url_path)) {
+ break;
+ }
+
+ route = route->next;
+ }
+
+ if (!route) {
+ client_submit_response(client,
+ GEMINI_STATUS_NOT_FOUND, "Not found", NULL);
+ return;
+ }
+
+ assert(route->root); // TODO: reverse proxy support
char path[PATH_MAX + 1];
- int n = snprintf(path, sizeof(path), "%s%s", host->root, client->path);
+ int n = snprintf(path, sizeof(path), "%s%s", route->root, url_path);
if ((size_t)n >= sizeof(path)) {
client_submit_response(client, GEMINI_STATUS_PERMANENT_FAILURE,
"Request path exceeds PATH_MAX", NULL);
@@ -142,12 +187,12 @@ serve_request(struct gmnisrv_client *client)
}
if (S_ISDIR(st.st_mode)) {
- if (host->autoindex) {
+ if (route->autoindex) {
serve_autoindex(client, path);
return;
} else {
strncat(path,
- host->index ? host->index : "index.gmi",
+ route->index ? route->index : "index.gmi",
sizeof(path) - 1);
}
} else if (S_ISLNK(st.st_mode)) {