added support for location blocks

This commit is contained in:
Omar Polo 2021-01-24 14:11:40 +00:00
parent 501e489c90
commit c8b7433918
9 changed files with 140 additions and 21 deletions

View File

@ -1,5 +1,7 @@
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>

15
gmid.1
View File

@ -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

16
gmid.h
View File

@ -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*);

1
lex.l
View File

@ -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;

3
mime.c
View File

@ -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;

48
parse.y
View File

@ -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 <str> 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;
}
;

View File

@ -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

View File

@ -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

View File

@ -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");