Add "location" keyword to specify path-specific configuration in
authorreyk <reyk@openbsd.org>
Wed, 30 Jul 2014 10:05:14 +0000 (10:05 +0000)
committerreyk <reyk@openbsd.org>
Wed, 30 Jul 2014 10:05:14 +0000 (10:05 +0000)
servers, for example auto index for a sub-directory only.  Internally,
a "location" is just a special type of a "virtual" server.

etc/examples/httpd.conf
usr.sbin/httpd/config.c
usr.sbin/httpd/httpd.conf.5
usr.sbin/httpd/httpd.h
usr.sbin/httpd/parse.y
usr.sbin/httpd/server.c
usr.sbin/httpd/server_http.c

index 826f026..9c95139 100644 (file)
@@ -1,4 +1,4 @@
-# $OpenBSD: httpd.conf,v 1.3 2014/07/29 16:17:28 reyk Exp $
+# $OpenBSD: httpd.conf,v 1.4 2014/07/30 10:05:14 reyk Exp $
 
 # Macros
 ext_addr="egress"
@@ -9,6 +9,9 @@ ext_addr="egress"
 # Servers
 server "default" {
        listen on $ext_addr port 80
+       location "/pub/*" {
+               directory auto index
+       }
 }
 
 # A name-based "virtual" server on the same address
index 762a7a3..9e9d2c3 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: config.c,v 1.5 2014/07/25 23:30:58 reyk Exp $ */
+/*     $OpenBSD: config.c,v 1.6 2014/07/30 10:05:14 reyk Exp $ */
 
 /*
  * Copyright (c) 2011 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -185,7 +185,8 @@ config_setserver(struct httpd *env, struct server *srv)
                iov[c].iov_base = &s;
                iov[c++].iov_len = sizeof(s);
 
-               if (id == PROC_SERVER) {
+               if (id == PROC_SERVER &&
+                   (srv->srv_conf.flags & SRVFLAG_LOCATION) == 0) {
                        /* XXX imsg code will close the fd after 1st call */
                        n = -1;
                        proc_range(ps, id, &n, &m);
@@ -216,6 +217,7 @@ config_getserver_config(struct httpd *env, struct server *srv,
 #endif
        struct server_config    *srv_conf;
        u_int8_t                *p = imsg->data;
+       u_int                    f;
 
        if ((srv_conf = calloc(1, sizeof(*srv_conf))) == NULL)
                return (-1);
@@ -223,11 +225,37 @@ config_getserver_config(struct httpd *env, struct server *srv,
        IMSG_SIZE_CHECK(imsg, srv_conf);
        memcpy(srv_conf, p, sizeof(*srv_conf));
 
-       TAILQ_INSERT_TAIL(&srv->srv_hosts, srv_conf, entry);
+       if (srv_conf->flags & SRVFLAG_LOCATION) {
+               /* Inherit configuration from the parent */
+               f = SRVFLAG_INDEX|SRVFLAG_NO_INDEX;
+               if ((srv_conf->flags & f) == 0) {
+                       srv_conf->flags |= srv->srv_conf.flags & f;
+                       (void)strlcpy(srv_conf->index, srv->srv_conf.index,
+                           sizeof(srv_conf->index));
+               }
+
+               f = SRVFLAG_AUTO_INDEX|SRVFLAG_NO_AUTO_INDEX;
+               if ((srv_conf->flags & f) == 0)
+                       srv_conf->flags |= srv->srv_conf.flags & f;
+
+               f = SRVFLAG_DOCROOT;
+               if ((srv_conf->flags & f) == 0) {
+                       (void)strlcpy(srv_conf->docroot,
+                           srv->srv_conf.docroot,
+                           sizeof(srv_conf->docroot));
+               }
+
+               DPRINTF("%s: %s %d received location \"%s\", parent \"%s\"",
+                   __func__, ps->ps_title[privsep_process], ps->ps_instance,
+                   srv_conf->location, srv->srv_conf.name);
+       } else {
+               /* Add a new "virtual" server */
+               DPRINTF("%s: %s %d received server \"%s\", parent \"%s\"",
+                   __func__, ps->ps_title[privsep_process], ps->ps_instance,
+                   srv_conf->name, srv->srv_conf.name);
+       }
 
-       DPRINTF("%s: %s %d received configuration \"%s\", parent \"%s\"",
-           __func__, ps->ps_title[privsep_process], ps->ps_instance,
-           srv_conf->name, srv->srv_conf.name);
+       TAILQ_INSERT_TAIL(&srv->srv_hosts, srv_conf, entry);
 
        return (0);
 }
@@ -247,6 +275,14 @@ config_getserver(struct httpd *env, struct imsg *imsg)
        memcpy(&srv_conf, p, sizeof(srv_conf));
        s = sizeof(srv_conf);
 
+       if (srv_conf.flags & SRVFLAG_LOCATION) {
+               if ((srv = server_byname(srv_conf.name)) == NULL) {
+                       log_warnx("%s: invalid location", __func__);
+                       return (-1);
+               }
+               return (config_getserver_config(env, srv, imsg));
+       }
+
        /* Check if server with matching listening socket already exists */
        if ((srv = server_byaddr((struct sockaddr *)
            &srv_conf.ss, srv_conf.port)) != NULL) {
index 140e9bf..9c056da 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: httpd.conf.5,v 1.9 2014/07/30 09:51:40 reyk Exp $
+.\"    $OpenBSD: httpd.conf.5,v 1.10 2014/07/30 10:05:14 reyk Exp $
 .\"
 .\" Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
 .\"
@@ -130,6 +130,16 @@ Disable the directory index.
 will neither display nor generate a directory index.
 .It Ic listen on Ar address Ic port Ar number
 Set the listen address and port.
+.It Ic location Ar path { ... }
+Specify server configuration rules for a specific location.
+The
+.Ar path
+argument will be matched against the URL path with shell globbing rules.
+A location section may include all of the server configuration rules
+except
+.Ic listen on
+and
+.Ic location .
 .It Ic root Ar directory
 Set the document root of the server.
 The
index 081e638..c153132 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: httpd.h,v 1.17 2014/07/29 16:17:28 reyk Exp $ */
+/*     $OpenBSD: httpd.h,v 1.18 2014/07/30 10:05:14 reyk Exp $ */
 
 /*
  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -276,9 +276,11 @@ SPLAY_HEAD(client_tree, client);
 #define SRVFLAG_NO_INDEX       0x02
 #define SRVFLAG_AUTO_INDEX     0x04
 #define SRVFLAG_NO_AUTO_INDEX  0x08
+#define SRVFLAG_DOCROOT                0x10
+#define SRVFLAG_LOCATION       0x20
 
 #define SRVFLAG_BITS                                           \
-       "\10\01INDEX\02NO_INDEX\03AUTO_INDEX\04NO_AUTO_INDEX"
+       "\10\01INDEX\02NO_INDEX\03AUTO_INDEX\04NO_AUTO_INDEX\05LOCATION"
 
 #define TCPFLAG_NODELAY                0x01
 #define TCPFLAG_NNODELAY       0x02
@@ -299,6 +301,7 @@ struct server_config {
        char                     name[MAXHOSTNAMELEN];
        char                     docroot[MAXPATHLEN];
        char                     index[NAME_MAX];
+       char                     location[NAME_MAX];
 
        in_port_t                port;
        struct sockaddr_storage  ss;
@@ -399,6 +402,8 @@ int  server_bufferevent_write(struct client *, void *, size_t);
 void    server_inflight_dec(struct client *, const char *);
 struct server *
         server_byaddr(struct sockaddr *, in_port_t);
+struct server *
+        server_byname(const char *);
 
 SPLAY_PROTOTYPE(client_tree, client, clt_nodes, server_client_cmp);
 
index 97485e3..4844272 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: parse.y,v 1.8 2014/07/29 16:17:28 reyk Exp $  */
+/*     $OpenBSD: parse.y,v 1.9 2014/07/30 10:05:14 reyk Exp $  */
 
 /*
  * Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -94,7 +94,7 @@ static int             errors = 0;
 static int              loadcfg = 0;
 uint32_t                last_server_id = 0;
 
-static struct server   *srv = NULL;
+static struct server   *srv = NULL, *parentsrv = NULL;
 struct serverlist       servers;
 struct media_type       media;
 
@@ -126,8 +126,8 @@ typedef struct {
 
 %}
 
-%token ALL AUTO DIRECTORY INDEX LISTEN LOG NO ON PORT PREFORK ROOT SERVER
-%token TYPES UPDATES VERBOSE
+%token ALL AUTO DIRECTORY INDEX LISTEN LOCATION LOG NO ON PORT PREFORK ROOT
+%token SERVER TYPES UPDATES VERBOSE
 %token ERROR INCLUDE
 %token <v.string>      STRING
 %token  <v.number>     NUMBER
@@ -229,9 +229,11 @@ server             : SERVER STRING         {
                                YYERROR;
                        }
                        srv = s;
-               } '{' optnl serveropts_l '}'    {
+
                        SPLAY_INIT(&srv->srv_clients);
                        TAILQ_INSERT_TAIL(conf->sc_servers, srv, srv_entry);
+               } '{' optnl serveropts_l '}'    {
+                       srv = NULL;
                }
                ;
 
@@ -244,6 +246,12 @@ serveroptsl        : LISTEN ON STRING port {
                        struct address          *h;
                        struct server           *s;
 
+                       if (parentsrv != NULL) {
+                               yyerror("listen %s inside location", $3);
+                               free($3);
+                               YYERROR;
+                       }
+
                        if (srv->srv_conf.ss.ss_family != AF_UNSPEC) {
                                yyerror("listen address already specified");
                                free($3);
@@ -279,9 +287,72 @@ serveroptsl        : LISTEN ON STRING port {
                                YYERROR;
                        }
                        free($2);
+                       srv->srv_conf.flags |= SRVFLAG_DOCROOT;
                }
                | DIRECTORY dirflags
                | DIRECTORY '{' dirflags_l '}'
+               | LOCATION STRING               {
+                       struct server   *s;
+
+                       if (parentsrv != NULL) {
+                               yyerror("location %s inside location", $2);
+                               free($2);
+                               YYERROR;
+                       }
+
+                       if (!loadcfg) {
+                               free($2);
+                               YYACCEPT;
+                       }
+
+                       TAILQ_FOREACH(s, conf->sc_servers, srv_entry)
+                               if (strcmp(s->srv_conf.name,
+                                   srv->srv_conf.name) == 0 &&
+                                   strcmp(s->srv_conf.location, $2) == 0)
+                                       break;
+                       if (s != NULL) {
+                               yyerror("location %s defined twice", $2);
+                               free($2);
+                               YYERROR;
+                       }
+
+                       if ((s = calloc(1, sizeof (*s))) == NULL)
+                               fatal("out of memory");
+
+                       if (strlcpy(s->srv_conf.location, $2,
+                           sizeof(s->srv_conf.location)) >=
+                           sizeof(s->srv_conf.location)) {
+                               yyerror("server location truncated");
+                               free($2);
+                               free(s);
+                               YYERROR;
+                       }
+                       free($2);
+
+                       if (strlcpy(s->srv_conf.name, srv->srv_conf.name,
+                           sizeof(s->srv_conf.name)) >=
+                           sizeof(s->srv_conf.name)) {
+                               yyerror("server name truncated");
+                               free(s);
+                               YYERROR;
+                       }
+
+                       s->srv_conf.id = ++last_server_id;
+                       s->srv_conf.flags = SRVFLAG_LOCATION;
+
+                       if (last_server_id == INT_MAX) {
+                               yyerror("too many servers/locations defined");
+                               free(s);
+                               YYERROR;
+                       }
+                       parentsrv = srv;
+                       srv = s;
+                       SPLAY_INIT(&srv->srv_clients);
+                       TAILQ_INSERT_TAIL(conf->sc_servers, srv, srv_entry);
+               } '{' optnl serveropts_l '}'    {
+                       srv = parentsrv;
+                       parentsrv = NULL;
+               }
                ;
 
 dirflags_l     : dirflags comma dirflags_l
@@ -451,6 +522,7 @@ lookup(char *s)
                { "include",            INCLUDE },
                { "index",              INDEX },
                { "listen",             LISTEN },
+               { "location",           LOCATION },
                { "log",                LOG },
                { "no",                 NO },
                { "on",                 ON },
index cff35e0..2b23011 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: server.c,v 1.15 2014/07/29 16:38:34 reyk Exp $        */
+/*     $OpenBSD: server.c,v 1.16 2014/07/30 10:05:14 reyk Exp $        */
 
 /*
  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -96,6 +96,9 @@ server_shutdown(void)
 int
 server_privinit(struct server *srv)
 {
+       if (srv->srv_conf.flags & SRVFLAG_LOCATION)
+               return (0);
+
        log_debug("%s: adding server %s", __func__, srv->srv_conf.name);
 
        if ((srv->srv_s = server_socket_listen(&srv->srv_conf.ss,
@@ -196,6 +199,19 @@ server_byaddr(struct sockaddr *addr, in_port_t port)
        return (NULL);
 }
 
+struct server *
+server_byname(const char *name)
+{
+       struct server   *srv;
+
+       TAILQ_FOREACH(srv, env->sc_servers, srv_entry) {
+               if (strcmp(srv->srv_conf.name, name) == 0)
+                       return (srv);
+       }
+
+       return (NULL);
+}
+
 int
 server_socket_af(struct sockaddr_storage *ss, in_port_t port)
 {
index bfc5272..05e8bf5 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: server_http.c,v 1.19 2014/07/25 23:25:38 reyk Exp $   */
+/*     $OpenBSD: server_http.c,v 1.20 2014/07/30 10:05:14 reyk Exp $   */
 
 /*
  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -565,7 +565,8 @@ server_http_host(struct sockaddr_storage *ss, char *buf, size_t len)
 void
 server_abort_http(struct client *clt, u_int code, const char *msg)
 {
-       struct server_config    *srv_conf = clt->clt_srv_conf;
+       struct server           *srv = clt->clt_srv;
+       struct server_config    *srv_conf = &srv->srv_conf;
        struct bufferevent      *bev = clt->clt_bev;
        const char              *httperr = NULL, *text = "";
        char                    *httpmsg, *extraheader = NULL;
@@ -716,8 +717,11 @@ server_response(struct httpd *httpd, struct client *clt)
        if (host != NULL) {
                /* XXX maybe better to turn srv_hosts into a tree */
                TAILQ_FOREACH(srv_conf, &srv->srv_hosts, entry) {
-                       if (fnmatch(srv_conf->name, host->kv_value,
-                           FNM_CASEFOLD) == 0) {
+                       if (((srv_conf->flags & SRVFLAG_LOCATION) &&
+                           fnmatch(srv_conf->location,
+                           desc->http_path, FNM_CASEFOLD) == 0) ||
+                           (fnmatch(srv_conf->name, host->kv_value,
+                           FNM_CASEFOLD) == 0)) {
                                /* Replace host configuration */
                                clt->clt_srv_conf = srv_conf;
                                srv_conf = NULL;