I know a patch was sent to the list a few months ago to add URL
rewriting support to httpd, but it doesn't seem to have made its way
into the tree. It's unclear whether that's because URL rewriting support
without using redirection is being considered featuritis, it fell
through the cracks, or what, but I thought I'd propose an alternative
that is simpler in terms of functionality and mimics the behavior of the
"block return code uri" option.

Note that query strings in requests are ignored when using the new "pass
rewrite uri" option in order to make the option match the "block return
code uri" one. This means that a shim is needed when using query string
variables from requests since the $QUERY_STRING macro is encoded when it
gets expanded. I don't know what the best way to address this is, if
anything, but three possibilities come to mind: 1) have something like a
$RAW_QUERY_STRING macro, 2) always append the query string from the
request, or 3) add an "[no] append query" option after the rewrite
options. For what it's worth, Apache can be configured to either always
ignore or always append the query string depending on (confusingly) the
rewrite options and/or the value of the rewrite URL, and nginx always
appends the request query string unless the rewrite URL ends in a "?".

This diff needs the other diff I recently sent to the list to make the
$DOCUMENT_URI macro work correctly when using FastCGI.

For those interested, here's the thread containing the other patch that
was sent a few months ago:
http://marc.info/?t=144743058100004&r=1&w=2

Index: http.h
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/http.h,v
retrieving revision 1.13
diff -u -p -r1.13 http.h
--- http.h      11 Jun 2015 18:49:09 -0000      1.13
+++ http.h      14 Apr 2016 04:26:03 -0000
@@ -242,8 +242,9 @@ struct http_descriptor {
        int                      http_chunked;
        char                    *http_version;
 
-       /* Rewritten path remains NULL if not used */
+       /* Rewritten path and query remain NULL if not used */
        char                    *http_path_alias;
+       char                    *http_query_alias;
 
        /* A tree of headers and attached lists for repeated headers. */
        struct kv               *http_lastheader;
Index: httpd.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/httpd.conf.5,v
retrieving revision 1.68
diff -u -p -r1.68 httpd.conf.5
--- httpd.conf.5        19 Jul 2015 05:17:27 -0000      1.68
+++ httpd.conf.5        14 Apr 2016 04:26:03 -0000
@@ -319,6 +319,8 @@ The pattern may contain captures that ca
 .Ar uri
 of an enclosed
 .Ic block return
+or
+.Ic pass rewrite
 option.
 .It Oo Ic no Oc Ic log Op Ar option
 Set the specified logging options.
@@ -373,10 +375,23 @@ Enable or disable logging to
 .Xr syslog 3
 instead of the log files.
 .El
-.It Ic pass
+.It Ic pass Op Ic rewrite Ar uri
 Disable any previous
 .Ic block
 in a location.
+If the optional
+.Ar uri
+argument is specified,
+.Xr httpd 8
+will use
+.Ar uri
+instead of the matched location string when determining what
+to load.
+The
+.Ar uri
+may contain predefined macros as explained in the
+.Ic block return
+option.
 .It Ic root Ar option
 Configure the document root and options for the request path.
 Valid options are:
Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/parse.y,v
retrieving revision 1.77
diff -u -p -r1.77 parse.y
--- parse.y     22 Nov 2015 13:27:13 -0000      1.77
+++ parse.y     14 Apr 2016 04:26:03 -0000
@@ -134,7 +134,7 @@ typedef struct {
 %token LOCATION LOG LOGDIR MATCH MAXIMUM NO NODELAY ON PORT PREFORK PROTOCOLS
 %token REQUEST REQUESTS ROOT SACK SERVER SOCKET STRIP STYLE SYSLOG TCP TIMEOUT
 %token TLS TYPE TYPES HSTS MAXAGE SUBDOMAINS DEFAULT PRELOAD
-%token ERROR INCLUDE AUTHENTICATE WITH BLOCK DROP RETURN PASS
+%token ERROR INCLUDE AUTHENTICATE WITH BLOCK DROP RETURN PASS REWRITE
 %token <v.string>      STRING
 %token  <v.number>     NUMBER
 %type  <v.port>        port
@@ -913,6 +913,8 @@ filter              : block RETURN NUMBER optstring 
                                        free($4);
                                        YYERROR;
                                }
+
+                               free(srv_conf->return_uri);
                                srv_conf->return_uri = $4;
                                srv_conf->return_uri_len = strlen($4) + 1;
                        }
@@ -925,10 +927,14 @@ filter            : block RETURN NUMBER optstring 
                        /* Forbidden */
                        srv_conf->return_code = 403;
                }
-               | PASS                          {
-                       srv_conf->flags &= ~SRVFLAG_BLOCK;
-                       srv_conf->flags |= SRVFLAG_NO_BLOCK;
+               | pass REWRITE optstring        {
+                       if ($3 != NULL) {
+                               free(srv_conf->return_uri);
+                               srv_conf->return_uri = $3;
+                               srv_conf->return_uri_len = strlen($3) + 1;
+                       }
                }
+               | pass
                ;
 
 block          : BLOCK                         {
@@ -937,6 +943,12 @@ block              : BLOCK                         {
                }
                ;
 
+pass           : PASS                          {
+                       srv_conf->flags &= ~SRVFLAG_BLOCK;
+                       srv_conf->flags |= SRVFLAG_NO_BLOCK;
+               }
+               ;
+
 optmatch       : /* empty */           { $$ = 0; }
                | MATCH                 { $$ = 1; }
                ;
@@ -1184,6 +1196,7 @@ lookup(char *s)
                { "request",            REQUEST },
                { "requests",           REQUESTS },
                { "return",             RETURN },
+               { "rewrite",            REWRITE },
                { "root",               ROOT },
                { "sack",               SACK },
                { "server",             SERVER },
Index: server_fcgi.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/server_fcgi.c,v
retrieving revision 1.67
diff -u -p -r1.67 server_fcgi.c
--- server_fcgi.c       23 Nov 2015 20:56:15 -0000      1.67
+++ server_fcgi.c       14 Apr 2016 04:26:03 -0000
@@ -97,7 +97,7 @@ server_fcgi(struct httpd *env, struct cl
        int                              pathlen;
        int                              fd = -1, ret;
        const char                      *stripped, *p, *alias, *errstr = NULL;
-       char                            *str, *script = NULL;
+       char                            *queryalias, *str, *script = NULL;
 
        if (srv_conf->socket[0] == ':') {
                struct sockaddr_storage  ss;
@@ -194,6 +194,10 @@ server_fcgi(struct httpd *env, struct cl
            ? desc->http_path_alias
            : desc->http_path;
 
+       queryalias = desc->http_query_alias != NULL
+           ? desc->http_query_alias
+           : desc->http_query;
+
        stripped = server_root_strip(alias, srv_conf->strip);
        if ((pathlen = asprintf(&script, "%s%s", srv_conf->root, stripped))
            == -1) {
@@ -242,8 +246,8 @@ server_fcgi(struct httpd *env, struct cl
                goto fail;
        }
 
-       if (desc->http_query)
-               if (fcgi_add_param(&param, "QUERY_STRING", desc->http_query,
+       if (queryalias)
+               if (fcgi_add_param(&param, "QUERY_STRING", queryalias,
                    clt) == -1) {
                        errstr = "failed to encode param";
                        goto fail;
Index: server_http.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/server_http.c,v
retrieving revision 1.106
diff -u -p -r1.106 server_http.c
--- server_http.c       8 Mar 2016 09:33:15 -0000       1.106
+++ server_http.c       14 Apr 2016 04:26:03 -0000
@@ -1040,6 +1040,7 @@ server_response(struct httpd *httpd, str
 {
        char                     path[PATH_MAX];
        char                     hostname[HOST_NAME_MAX+1];
+       char                     buf[IBUF_READ_SIZE];
        struct http_descriptor  *desc = clt->clt_descreq;
        struct http_descriptor  *resp = clt->clt_descresp;
        struct server           *srv = clt->clt_srv;
@@ -1157,7 +1158,28 @@ server_response(struct httpd *httpd, str
                server_abort_http(clt, srv_conf->return_code,
                    srv_conf->return_uri);
                return (-1);
-       } else if (srv_conf->flags & SRVFLAG_AUTH &&
+       } else if (srv_conf->return_uri) {
+               memset(buf, 0, sizeof(buf));
+
+               if (server_expand_http(clt, srv_conf->return_uri, buf,
+                   sizeof(buf)) == NULL)
+                       goto fail;
+
+               free(desc->http_path_alias);
+               if ((desc->http_path_alias = strdup(buf)) == NULL)
+                       goto fail;
+
+               free(desc->http_query_alias);
+               desc->http_query_alias = strchr(desc->http_path_alias, '?');
+               if (desc->http_query_alias != NULL) {
+                       *desc->http_query_alias++ = '\0';
+                       if ((desc->http_query_alias =
+                           strdup(desc->http_query_alias)) == NULL)
+                               goto fail;
+               }
+       }
+
+       if (srv_conf->flags & SRVFLAG_AUTH &&
            server_http_authenticate(srv_conf, clt) == -1) {
                server_abort_http(clt, 401, srv_conf->auth_realm);
                return (-1);

Reply via email to