commit c8b74339185123feebb6164b91f500f1930e45ff from: Omar Polo date: Sun Jan 24 14:11:40 2021 UTC added support for location blocks commit - 501e489c90eeddec3f29b014864f57e840ea1fa8 commit + c8b74339185123feebb6164b91f500f1930e45ff blob - 499526a5665c84462c8ba072d688d92c99b2d8b5 blob + b7f05157b697387c34a2aa1d1ea441f66636fa41 --- ChangeLog +++ ChangeLog @@ -1,5 +1,7 @@ 2021-01-24 Omar Polo + * parse.y (vhost): added support for location blocks + * server.c (send_dir): make the directory index customizable 2021-01-23 Omar Polo blob - e51768c9d681249ae8c76b238fb5506a8c4e02db blob + 70ece216eb65b5deaaf34bce45113e2314463af6 --- gmid.1 +++ gmid.1 @@ -181,6 +181,21 @@ parameter will be added in the response. Set the directory index file. If not specified, it defaults to .Pa index.gmi +.It Ic location Pa path Brq ... +Specify server configuration rules for a specific location. +The +.Pa path +argument will be matched against the request path with shell globbing +rules. +In case of multiple location statements in the same context, the last +matching location will be put into effect. +Therefore is advisable to match for a generic paths first and for more +specific ones later on. +A +.Ic location +section may include most of the server configuration rules +except +.Ic cert , Ic key , Ic root , Ic location No and Ic CGI . .El .Sh CGI When CGI scripts are enabled for a directory, a request for an blob - 3df7bfb4bb72151709bcaed4c2ef346d47d159d1 blob + 139338e0f1048ebcbffaa6b2200d1cf1cc2f7647 --- gmid.h +++ gmid.h @@ -49,6 +49,7 @@ #define MAX_USERS 64 #define HOSTSLEN 64 +#define LOCLEN 32 #define LOGE(c, fmt, ...) logs(LOG_ERR, c, fmt, __VA_ARGS__) #define LOGW(c, fmt, ...) logs(LOG_WARNING, c, fmt, __VA_ARGS__) @@ -56,16 +57,21 @@ #define LOGI(c, fmt, ...) logs(LOG_INFO, c, fmt, __VA_ARGS__) #define LOGD(c, fmt, ...) logs(LOG_DEBUG, c, fmt, __VA_ARGS__) +struct location { + char *match; + char *lang; + char *default_mime; + char *index; +}; + struct vhost { const char *domain; const char *cert; const char *key; const char *dir; const char *cgi; - char *lang; int dirfd; - char *default_mime; - char *index; + struct location locations[LOCLEN]; }; extern struct vhost hosts[HOSTSLEN]; @@ -160,6 +166,7 @@ void parse_conf(const char*); void load_vhosts(struct tls_config*); int make_socket(int, int); int listener_main(void); +void init_config(void); void usage(const char*); /* provided by lex/yacc */ @@ -175,6 +182,9 @@ void load_default_mime(struct mime*); const char *mime(struct vhost*, const char*); /* server.c */ +const char *vhost_lang(struct vhost*, const char*); +const char *vhost_default_mime(struct vhost*, const char*); +const char *vhost_index(struct vhost*, const char*); int check_path(struct client*, const char*, int*); void open_file(struct pollfd*, struct client*); void check_for_cgi(char *, char*, struct pollfd*, struct client*); blob - 0932bc3ba0af1fe2db1b68a5721a3e72b1f918a1 blob + b785796f7bf9fb2390aa06179bed58b0cd242d52 --- lex.l +++ lex.l @@ -60,6 +60,7 @@ default return TDEFAULT; type return TTYPE; server return TSERVER; +location return TLOCATION; cert return TCERT; key return TKEY; root return TROOT; blob - 81bc2efba34f8142a4f82c1ce9f273fa6692a15c blob + f8fdd67e54afb7e57d9e0575b552a98258b264fd --- mime.c +++ mime.c @@ -97,8 +97,7 @@ mime(struct vhost *host, const char *path) const char *def, *ext; struct etm *t; - if ((def = host->default_mime) == NULL) - def = "application/octet-stream"; + def = vhost_default_mime(host, path); if ((ext = path_ext(path)) == NULL) return def; blob - 2d8f0b855acc9ea0c12421c60a7ab408b4e6b807 blob + e7883a9d1705124c010c75d6c852d3090d06c8a2 --- parse.y +++ parse.y @@ -30,6 +30,9 @@ struct vhost *host = &hosts[0]; size_t ihost = 0; +struct location *loc = &hosts[0].locations[0]; +size_t iloc = 0; + extern void yyerror(const char*); %} @@ -43,7 +46,7 @@ extern void yyerror(const char*); } %token TDAEMON TIPV6 TPORT TPROTOCOLS TMIME TDEFAULT TTYPE TSERVER -%token TCERT TKEY TROOT TCGI TLANG +%token TLOCATION TCERT TKEY TROOT TCGI TLANG %token TERR %token TSTRING @@ -72,15 +75,20 @@ vhosts : /* empty */ | vhosts vhost ; -vhost : TSERVER TSTRING '{' servopts '}' { +vhost : TSERVER TSTRING '{' servopts locations '}' { + host->locations[0].match = (char*)"*"; host->domain = $2; if (host->cert == NULL || host->key == NULL || host->dir == NULL) errx(1, "invalid vhost definition: %s", $2); + if (++ihost == HOSTSLEN) errx(1, "too much vhosts defined"); - host++; + + host++; + loc = &host->locations[0]; + iloc = 0; } | error '}' { yyerror("error in server directive"); } ; @@ -98,16 +106,36 @@ servopt : TCERT TSTRING { host->cert = $2; } if (*host->cgi == '/') host->cgi++; } - | TDEFAULT TTYPE TSTRING { - free(host->default_mime); - host->default_mime = $3; + | locopt + ; + +locations : /* empty */ + | locations location + ; + +location : TLOCATION TSTRING '{' locopts '}' { + loc->match = $2; + if (++iloc == LOCLEN) + errx(1, "too much location rules defined"); + loc++; } + | error '}' + ; + +locopts : /* empty */ + | locopts locopt + ; + +locopt : TDEFAULT TTYPE TSTRING { + free(loc->default_mime); + loc->default_mime = $3; + } | TLANG TSTRING { - free(host->lang); - host->lang = $2; + free(loc->lang); + loc->lang = $2; } | TINDEX TSTRING { - free(host->index); - host->index = $2; + free(loc->index); + loc->index = $2; } ; blob - 195cb0d91ba2f7d1e27f9ebb15a030869aeaf490 blob + 3c9c572fb8c337ede611f0ea7a7818e5e8b73d03 --- regress/Makefile +++ regress/Makefile @@ -35,6 +35,7 @@ testdata: fill-file ./sha testdata/index.gmi testdata/index.gmi.sha cp hello slow err invalid serve-bigfile testdata/ mkdir testdata/dir + cp hello testdata/dir cp testdata/index.gmi testdata/dir/foo.gmi runtime: testdata cert.pem blob - 6456746896c5007c4069412fc1389420f767482b blob + 8cf2afb1ee11f9c4d717dd411e42e23e37d4c7fb --- regress/runtime +++ regress/runtime @@ -191,3 +191,17 @@ echo OK GET /dir/ with custom index check "should be running" quit + +config '' 'location "/dir/" { default type "text/plain" index "hello" }' +checkconf +run + +eq "$(head /dir/hello)" "20 text/plain" "Unexpected head for /" +echo OK GET /dir/hello with location and default type + +eq "$(head /dir/)" "20 text/plain" "Unexpected head for /dir" +eq "$(get /dir/|tail -1)" 'echo "# hello world"' "Unexpected body for /dir/" +echo OK GET /dir/ with location and custom index + +check "should be running" +quit blob - 59760b3804a3f834b19d459916cb04956b9b30f9 blob + 48a7701556fc2f134906f2d8c9ec6f7c73ca42b2 --- server.c +++ server.c @@ -29,6 +29,54 @@ int connected_clients; +const char * +vhost_lang(struct vhost *v, const char *path) +{ + struct location *loc; + const char *lang = NULL; + + for (loc = v->locations; loc->match != NULL; ++loc) { + if (!fnmatch(loc->match, path, 0)) { + if (loc->lang != NULL) + lang = loc->lang; + } + } + + return lang; +} + +const char * +vhost_default_mime(struct vhost *v, const char *path) +{ + struct location *loc; + const char *default_mime = "application/octet-stream"; + + for (loc = v->locations; loc->match != NULL; ++loc) { + if (!fnmatch(loc->match, path, 0)) { + if (loc->default_mime != NULL) + default_mime = loc->default_mime; + } + } + + return default_mime; +} + +const char * +vhost_index(struct vhost *v, const char *path) +{ + struct location *loc; + const char *index = "index.gmi"; + + for (loc = v->locations; loc->match != NULL; ++loc) { + if (!fnmatch(loc->match, path, 0)) { + if (loc->index != NULL) + index = loc->index; + } + } + + return index; +} + int check_path(struct client *c, const char *path, int *fd) { @@ -255,17 +303,20 @@ void start_reply(struct pollfd *pfd, struct client *c, int code, const char *meta) { char buf[1030]; /* status + ' ' + max reply len + \r\n\0 */ + const char *lang; size_t len; c->code = code; c->meta = meta; c->state = S_INITIALIZING; + lang = vhost_lang(c->host, c->iri.path); + snprintf(buf, sizeof(buf), "%d ", code); strlcat(buf, meta, sizeof(buf)); - if (!strcmp(meta, "text/gemini") && c->host->lang != NULL) { + if (!strcmp(meta, "text/gemini") && lang != NULL) { strlcat(buf, "; lang=", sizeof(buf)); - strlcat(buf, c->host->lang, sizeof(buf)); + strlcat(buf, lang, sizeof(buf)); } len = strlcat(buf, "\r\n", sizeof(buf)); @@ -386,7 +437,6 @@ void send_dir(struct pollfd *fds, struct client *c) { size_t len; - const char *index = "index.gmi"; /* guard against a re-entrant call: open_file -> send_dir -> * open_file -> send_dir. This can happen only if: @@ -416,9 +466,8 @@ send_dir(struct pollfd *fds, struct client *c) if (!ends_with(c->sbuf, "/")) strlcat(c->sbuf, "/", sizeof(c->sbuf)); - if (c->host->index != NULL) - index = c->host->index; - len = strlcat(c->sbuf, index, sizeof(c->sbuf)); + len = strlcat(c->sbuf, vhost_index(c->host, c->iri.path), + sizeof(c->sbuf)); if (len >= sizeof(c->sbuf)) { start_reply(fds, c, TEMP_FAILURE, "internal server error");