commit - 501e489c90eeddec3f29b014864f57e840ea1fa8
commit + c8b74339185123feebb6164b91f500f1930e45ff
blob - 499526a5665c84462c8ba072d688d92c99b2d8b5
blob + b7f05157b697387c34a2aa1d1ea441f66636fa41
--- ChangeLog
+++ ChangeLog
2021-01-24 Omar Polo <op@omarpolo.com>
+ * parse.y (vhost): added support for location blocks
+
* server.c (send_dir): make the directory index customizable
2021-01-23 Omar Polo <op@omarpolo.com>
blob - e51768c9d681249ae8c76b238fb5506a8c4e02db
blob + 70ece216eb65b5deaaf34bce45113e2314463af6
--- gmid.1
+++ gmid.1
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
#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__)
#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];
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 */
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
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
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
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*);
%}
}
%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 <str> TSTRING
| 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"); }
;
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
./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
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
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)
{
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));
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:
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");