Hi,
I'd like to add an URI stripping option to httpd, which is similar to
apache/nginx's alias options:
root [strip number] directory
Set the document root of the server. The directory is a pathname
within the chroot(2) root directory of httpd. If not specified,
it defaults to /htdocs. If the strip option is set, number path
components are stripped from the beginning of the request URI
before looking up the stripped-down URI at directory.
for example:
location "/pub/OpenBSD/snapshots/amd64*" {
root strip 4 "/OpenBSD.amd64"
directory auto index
}
For serving php:
location "/wiki/" {
root strip 1 "/dokuwiki"
directory index "doku.php"
fastcgi socket "/tmp/doku.sock"
}
location "/wiki/*.php" {
root strip 1 "/dokuwiki"
fastcgi socket "/tmp/doku.sock"
}
location "/wiki/lib/*" {
root strip 1 "/dokuwiki"
directory no index
}
Comments? OKs?
Christopher
diff --git httpd.conf.5 httpd.conf.5
index 788d0a9..7776131 100644
--- httpd.conf.5
+++ httpd.conf.5
@@ -229,7 +229,7 @@ Enable or disable logging to
.Xr syslog 3
instead of the log files.
.El
-.It Ic root Ar directory
+.It Ic root Oo Ic strip Ar number Oc Ar directory
Set the document root of the server.
The
.Ar directory
@@ -239,6 +239,11 @@ root directory of
.Nm httpd .
If not specified, it defaults to
.Pa /htdocs .
+If the strip option is set,
+.Ar number
+path components are stripped from the beginning of the request URI before
+looking up the stripped-down URI at
+.Ar directory .
.It Ic ssl Ar option
Set the SSL configuration for the server.
These options are only used if SSL has been enabled via the listen directive.
diff --git httpd.h httpd.h
index 04b1f05..45505a1 100644
--- httpd.h
+++ httpd.h
@@ -383,6 +383,7 @@ struct server_config {
char *ssl_key_file;
u_int16_t flags;
+ u_int8_t strip;
u_int8_t tcpflags;
int tcpbufsiz;
int tcpbacklog;
diff --git parse.y parse.y
index 44cf90c..70e1cf7 100644
--- parse.y
+++ parse.y
@@ -128,12 +128,13 @@ typedef struct {
%token ACCESS AUTO BACKLOG BODY BUFFER CERTIFICATE CHROOT CIPHERS COMMON
%token COMBINED CONNECTION DIRECTORY ERR FCGI INDEX IP KEY LISTEN LOCATION
%token LOG MAXIMUM NO NODELAY ON PORT PREFORK REQUEST REQUESTS ROOT SACK
-%token SERVER SOCKET SSL STYLE SYSLOG TCP TIMEOUT TYPES
+%token SERVER SOCKET SSL STRIP STYLE SYSLOG TCP TIMEOUT TYPES
%token ERROR INCLUDE
%token <v.string> STRING
%token <v.number> NUMBER
%type <v.port> port
%type <v.number> optssl
+%type <v.number> optstrip
%type <v.tv> timeout
%type <v.string> numberstring
@@ -176,6 +177,10 @@ optssl : /*empty*/ { $$ = 0; }
| SSL { $$ = 1; }
;
+optstrip : /*empty*/ { $$ = 0; }
+ | STRIP NUMBER { $$ = $2; }
+ ;
+
main : PREFORK NUMBER {
if (loadcfg)
break;
@@ -333,16 +338,21 @@ serveroptsl : LISTEN ON STRING optssl port {
YYERROR;
}
} ssl
- | ROOT STRING {
- if (strlcpy(srv->srv_conf.root, $2,
+ | ROOT optstrip STRING {
+ if (strlcpy(srv->srv_conf.root, $3,
sizeof(srv->srv_conf.root)) >=
sizeof(srv->srv_conf.root)) {
yyerror("document root too long");
- free($2);
+ free($3);
YYERROR;
}
- free($2);
+ free($3);
srv->srv_conf.flags |= SRVFLAG_ROOT;
+ if ($2 < 0 || $2 > UINT8_MAX) {
+ yyerror("invalid strip number");
+ YYERROR;
+ }
+ srv->srv_conf.strip = $2;
}
| DIRECTORY dirflags
| DIRECTORY '{' dirflags_l '}'
@@ -848,6 +858,7 @@ lookup(char *s)
{ "server", SERVER },
{ "socket", SOCKET },
{ "ssl", SSL },
+ { "strip", STRIP },
{ "style", STYLE },
{ "syslog", SYSLOG },
{ "tcp", TCP },
diff --git server_fcgi.c server_fcgi.c
index fe97be0..1d591b8 100644
--- server_fcgi.c
+++ server_fcgi.c
@@ -101,9 +101,12 @@ server_fcgi(struct httpd *env, struct client *clt)
struct fcgi_begin_request_body *begin;
char hbuf[MAXHOSTNAMELEN];
size_t scriptlen;
- int pathlen;
+ int i, pathlen;
int fd = -1, ret;
- const char *errstr = NULL;
+ const char *stripped, *errstr = NULL;
+ const char *alias = desc->http_path_alias != NULL
+ ? desc->http_path_alias
+ : desc->http_path;
char *str, *p, *script = NULL;
if (srv_conf->socket[0] == ':') {
@@ -192,9 +195,14 @@ server_fcgi(struct httpd *env, struct client *clt)
h->type = FCGI_PARAMS;
h->content_len = param.total_len = 0;
- if ((pathlen = asprintf(&script, "%s%s", srv_conf->root,
- desc->http_path_alias != NULL ?
- desc->http_path_alias : desc->http_path)) == -1) {
+ /* First strip leading directories. Leading '/' is ignored. */
+ stripped = alias;
+ i = srv_conf->strip;
+ while (i > 0 && *stripped != '\0') if (*++stripped == '/') i--;
+
+ /* Then prepend root. */
+ if ((pathlen = asprintf(&script, "%s%s", srv_conf->root, stripped))
+ == -1) {
errstr = "failed to get script name";
goto fail;
}
@@ -214,9 +222,16 @@ server_fcgi(struct httpd *env, struct client *clt)
}
script[scriptlen] = '\0';
}
-
- if (fcgi_add_param(¶m, "SCRIPT_NAME",
- script + strlen(srv_conf->root), clt) == -1) {
+ /*
+ * calculate length of http SCRIPT_NAME:
+ * add stripped prefix length, remove local root length
+ */
+ scriptlen += (stripped - alias) - strlen(srv_conf->root);
+ if ((str = strndup(alias, scriptlen)) == NULL)
+ goto fail;
+ ret = fcgi_add_param(¶m, "SCRIPT_NAME", str, clt);
+ free(str);
+ if (ret == -1) {
errstr = "failed to encode param";
goto fail;
}
diff --git server_file.c server_file.c
index e8a487e..987f2b5 100644
--- server_file.c
+++ server_file.c
@@ -153,17 +153,24 @@ server_file(struct httpd *env, struct client *clt)
struct http_descriptor *desc = clt->clt_descreq;
struct server_config *srv_conf = clt->clt_srv_conf;
char path[MAXPATHLEN];
- const char *errstr = NULL;
- int ret = 500;
+ const char *stripped, *errstr = NULL;
+ int i, ret = 500;
if (srv_conf->flags & SRVFLAG_FCGI)
return (server_fcgi(env, clt));
- /* Request path is already canonicalized */
+ /*
+ * Request path is already canonicalized.
+ * First strip leading directories. Leading '/' is ignored.
+ */
+ stripped = desc->http_path_alias != NULL ?
+ desc->http_path_alias : desc->http_path;
+ i = srv_conf->strip;
+ while (i > 0 && *stripped != '\0') if (*++stripped == '/') i--;
+
+ /* Then prepend root. */
if ((size_t)snprintf(path, sizeof(path), "%s%s",
- srv_conf->root,
- desc->http_path_alias != NULL ?
- desc->http_path_alias : desc->http_path) >= sizeof(path)) {
+ srv_conf->root, stripped) >= sizeof(path)) {
errstr = desc->http_path;
goto abort;
}
@@ -279,7 +286,7 @@ server_file_index(struct httpd *env, struct client *clt,
struct stat *st)
int code = 500;
struct evbuffer *evb = NULL;
struct media_type *media;
- const char *style;
+ const char *stripped, *style;
struct tm tm;
time_t t, dir_mtime;
@@ -288,9 +295,17 @@ server_file_index(struct httpd *env, struct client *clt,
struct stat *st)
goto abort;
}
- /* Request path is already canonicalized */
+ /*
+ * Request path is already canonicalized.
+ * First strip leading directories. Leading '/' is ignored.
+ */
+ stripped = desc->http_path;
+ i = srv_conf->strip;
+ while (i > 0 && *stripped != '\0') if (*++stripped == '/') i--;
+
+ /* Then prepend root. */
if ((size_t)snprintf(path, sizeof(path), "%s%s",
- srv_conf->root, desc->http_path) >= sizeof(path))
+ srv_conf->root, stripped) >= sizeof(path))
goto abort;
/* Now open the file, should be readable or we have another problem */
--
http://gmerlin.de
OpenPGP: http://gmerlin.de/christopher.pub
F190 D013 8F01 AA53 E080 3F3C F17F B0A1 D44E 4FEE