mirror of https://github.com/omar-polo/gmid.git
added support for location blocks
This commit is contained in:
parent
501e489c90
commit
c8b7433918
|
@ -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
15
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
|
||||
|
|
16
gmid.h
16
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*);
|
||||
|
|
1
lex.l
1
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;
|
||||
|
|
3
mime.c
3
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;
|
||||
|
|
48
parse.y
48
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 <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;
|
||||
}
|
||||
;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
61
server.c
61
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");
|
||||
|
|
Loading…
Reference in New Issue