My website generator is a little stupid at times. It generates
files with .html suffixes, but urls without them.
I worked around this with some redirects, but it never felt
quite right doing an extra round trip. Therefore, I added
internal redirects, processing the rewrite before responding to
an http request.
This introduces new syntax to the config file, allowing you to
do:
location match "^(/foo/bar/[%w]+)$" {
rewrite-to "/baz/%1.html"
}
Because we don't know what the paths should be relative
to, all paths rewritten must be absolute.
It seems like someone else may find it useful[1], so
I'm submitting it. I've been running a slightly older
version of this on https://myrlang.org for the last
day or two, and it's been uneventful. The difference
is that the syntax used to piggy back off the "block"
action => 'block internal return 302 "path"'.
This doesn't currently support chained rewrites. I think
that it wouldn't be hard to add if it's needed.
Ok?
[1] https://github.com/reyk/httpd/issues/27
==========================================
diff --git usr.sbin/httpd/config.c usr.sbin/httpd/config.c
index 3c31c3d4cd3..7d2982af7b9 100644
--- usr.sbin/httpd/config.c
+++ usr.sbin/httpd/config.c
@@ -448,7 +448,7 @@ config_getserver_config(struct httpd *env, struct server
*srv,
sizeof(srv_conf->errorlog));
}
- f = SRVFLAG_BLOCK|SRVFLAG_NO_BLOCK;
+ f = SRVFLAG_BLOCK|SRVFLAG_NO_BLOCK|SRVFLAG_REWRITE;
if ((srv_conf->flags & f) == 0) {
free(srv_conf->return_uri);
srv_conf->flags |= parent->flags & f;
diff --git usr.sbin/httpd/httpd.conf.5 usr.sbin/httpd/httpd.conf.5
index a3c97629de3..3a00a750537 100644
--- usr.sbin/httpd/httpd.conf.5
+++ usr.sbin/httpd/httpd.conf.5
@@ -454,6 +454,14 @@ instead of the log files.
Disable any previous
.Ic block
in a location.
+.It Ic rewrite-to Ar path
+The current request path is rewritten to
+.Ar path .
+using the same macro expansions as
+.Cm block return
+rules. After macros are substituted, the resulting paths must be
+absolute, beginning with a slash. Rewriting is not done
+recursively.
.It Ic root Ar option
Configure the document root and options for the request path.
Valid options are:
diff --git usr.sbin/httpd/httpd.h usr.sbin/httpd/httpd.h
index 05cbb8e3550..477115ec92d 100644
--- usr.sbin/httpd/httpd.h
+++ usr.sbin/httpd/httpd.h
@@ -394,6 +394,7 @@ SPLAY_HEAD(client_tree, client);
#define SRVFLAG_SERVER_MATCH 0x00200000
#define SRVFLAG_SERVER_HSTS 0x00400000
#define SRVFLAG_DEFAULT_TYPE 0x00800000
+#define SRVFLAG_REWRITE 0x01000000
#define SRVFLAG_BITS \
"\10\01INDEX\02NO_INDEX\03AUTO_INDEX\04NO_AUTO_INDEX" \
diff --git usr.sbin/httpd/parse.y usr.sbin/httpd/parse.y
index fcf1938c42d..4072ee5b532 100644
--- usr.sbin/httpd/parse.y
+++ usr.sbin/httpd/parse.y
@@ -134,7 +134,7 @@ typedef struct {
%token LISTEN LOCATION LOG LOGDIR MATCH MAXIMUM NO NODELAY OCSP ON
PORT PREFORK
%token PROTOCOLS REQUESTS ROOT SACK SERVER SOCKET STRIP STYLE SYSLOG
TCP TICKET
%token TIMEOUT TLS TYPE TYPES HSTS MAXAGE SUBDOMAINS DEFAULT PRELOAD
REQUEST
-%token ERROR INCLUDE AUTHENTICATE WITH BLOCK DROP RETURN PASS
+%token ERROR INCLUDE AUTHENTICATE WITH BLOCK DROP RETURN REWRITE PASS
%token <v.string> STRING
%token <v.number> NUMBER
%type <v.port> port
@@ -986,6 +986,11 @@ filter : block RETURN NUMBER optstring {
srv_conf->return_uri_len = strlen($4) + 1;
}
}
+ | REWRITE STRING {
+ srv_conf->flags |= SRVFLAG_REWRITE;
+ srv_conf->return_uri = $2;
+ srv_conf->return_uri_len = strlen($2) + 1;
+ }
| block DROP {
/* No return code, silently drop the connection */
srv_conf->return_code = 0;
@@ -1255,6 +1260,7 @@ lookup(char *s)
{ "request", REQUEST },
{ "requests", REQUESTS },
{ "return", RETURN },
+ { "rewrite-to", REWRITE },
{ "root", ROOT },
{ "sack", SACK },
{ "server", SERVER },
diff --git usr.sbin/httpd/server_http.c usr.sbin/httpd/server_http.c
index e64de0d2f9c..c9ea4771037 100644
--- usr.sbin/httpd/server_http.c
+++ usr.sbin/httpd/server_http.c
@@ -1162,10 +1162,34 @@ server_expand_http(struct client *clt, const char *val,
char *buf,
return (buf);
}
+static int
+server_set_path(struct http_descriptor *desc, char *input)
+{
+ char path[PATH_MAX];
+
+ if (input == NULL || url_decode(input) == NULL)
+ return -1;
+ if (canonicalize_path(input, path, sizeof(path)) == NULL)
+ return (-1);
+ free(desc->http_path);
+ if ((desc->http_path = strdup(path)) == NULL)
+ return(-1);
+ return (0);
+}
+
+static int
+server_rewrite(struct client *clt, struct http_descriptor *desc, char *input)
+{
+ char path[PATH_MAX];
+
+ if (server_expand_http(clt, input, path, sizeof(path)) == NULL)
+ return -1;
+ return (server_set_path(desc, path));
+}
+
int
server_response(struct httpd *httpd, struct client *clt)
{
- char path[PATH_MAX];
char hostname[HOST_NAME_MAX+1];
struct http_descriptor *desc = clt->clt_descreq;
struct http_descriptor *resp = clt->clt_descresp;
@@ -1178,12 +1202,7 @@ server_response(struct httpd *httpd, struct client *clt)
const char *errstr = NULL;
/* Canonicalize the request path */
- if (desc->http_path == NULL ||
- url_decode(desc->http_path) == NULL ||
- canonicalize_path(desc->http_path, path, sizeof(path)) == NULL)
- goto fail;
- free(desc->http_path);
- if ((desc->http_path = strdup(path)) == NULL)
+ if (server_set_path(desc, desc->http_path) == -1)
goto fail;
key.kv_key = "Host";
@@ -1284,6 +1303,10 @@ server_response(struct httpd *httpd, struct client *clt)
/* Now search for the location */
srv_conf = server_getlocation(clt, desc->http_path);
+ /* If we have an internal redirection, rewrite the URL */
+ if (srv_conf->flags & SRVFLAG_REWRITE)
+ if (server_rewrite(clt, desc, srv_conf->return_uri) == -1)
+ goto fail;
if (srv_conf->flags & SRVFLAG_BLOCK) {
server_abort_http(clt, srv_conf->return_code,
srv_conf->return_uri);