[prev in list] [next in list] [prev in thread] [next in thread] 

List:       openbsd-tech
Subject:    httpd request rewrite
From:       Reyk Floeter <reyk () openbsd ! org>
Date:       2018-05-29 16:48:31
Message-ID: 20180529164831.GA79845 () autobahn ! atexit ! net
[Download RAW message or body]

Hi,

it's about time.

	server "default" {
	        listen on * port 80
	        location match "/de/(.*)" {
	                request rewrite "/ch/%1"
	        }
	}

You can also you the macros as in the "block return" external
redirects.  So maybe something like:

	server "default" {
	        listen on * port 80
	        location match "/(.*)" {
	                request rewrite "/$HTTP_HOST/%1"
	        }
	}

Tests? OK?

Please note that this diff intentionally breaks the "root strip"
option because it changes the grammar to "request strip".  "root
strip" was semantically wrong but we didn't have a better place to put
it.  An current.html entry can be made for the required grammar change.

Reyk

Index: usr.sbin/httpd/config.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/config.c,v
retrieving revision 1.54
diff -u -p -u -p -r1.54 config.c
--- usr.sbin/httpd/config.c	19 May 2018 13:56:56 -0000	1.54
+++ usr.sbin/httpd/config.c	29 May 2018 16:35:29 -0000
@@ -476,6 +476,13 @@ config_getserver_config(struct httpd *en
 			    &parent->default_type, sizeof(struct media_type));
 		}
 
+		f = SRVFLAG_PATH_REWRITE|SRVFLAG_NO_PATH_REWRITE;
+		if ((srv_conf->flags & f) == 0) {
+			srv_conf->flags |= parent->flags & f;
+			(void)strlcpy(srv_conf->path, parent->path,
+			    sizeof(srv_conf->path));
+		}
+
 		f = SRVFLAG_SERVER_HSTS;
 		srv_conf->flags |= parent->flags & f;
 		srv_conf->hsts_max_age = parent->hsts_max_age;
Index: usr.sbin/httpd/httpd.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/httpd.conf.5,v
retrieving revision 1.95
diff -u -p -u -p -r1.95 httpd.conf.5
--- usr.sbin/httpd/httpd.conf.5	23 May 2018 19:02:50 -0000	1.95
+++ usr.sbin/httpd/httpd.conf.5	29 May 2018 16:35:29 -0000
@@ -198,6 +198,8 @@ argument can be used with return codes i
 .Sq Location:
 header for redirection to a specified URI.
 .Pp
+It is possible to rewrite the request to redirect it to a different
+external location.
 The
 .Ar uri
 may contain predefined macros that will be expanded at runtime:
@@ -396,10 +398,10 @@ the
 using pattern matching instead of shell globbing rules,
 see
 .Xr patterns 7 .
-The pattern may contain captures that can be used in the
-.Ar uri
-of an enclosed
+The pattern may contain captures that can be used in an enclosed
 .Ic block return
+or
+.Ic request rewrite
 option.
 .It Oo Ic no Oc Ic log Op Ar option
 Set the specified logging options.
@@ -458,12 +460,31 @@ instead of the log files.
 Disable any previous
 .Ic block
 in a location.
-.It Ic root Ar option
-Configure the document root and options for the request path.
+.It Ic request Ar option
+Configure the options for the request path.
 Valid options are:
 .Bl -tag -width Ds
-.It Ar directory
-Set the document root of the server.
+.It Oo Ic no Oc Ic rewrite Ar path
+Enable or disable rewriting of the request.
+Unlike the redirection with
+.Ic block return ,
+this will change the request path internally before
+.Nm httpd
+makes a final decision about the matching location.
+The
+.Ar path
+argument may contain predefined macros that will be expanded at runtime.
+See the
+.Ic block return
+option for the list of supported macros.
+.It Ic strip Ar number
+Strip
+.Ar number
+path components from the beginning of the request path before looking
+up the stripped-down path at the document root.
+.El
+.It Ic root Ar directory
+Configure the document root of the server.
 The
 .Ar directory
 is a pathname within the
@@ -472,12 +493,6 @@ root directory of
 .Nm httpd .
 If not specified, it defaults to
 .Pa /htdocs .
-.It Ic strip Ar number
-Strip
-.Ar number
-path components from the beginning of the request path before looking
-up the stripped-down path at the document root.
-.El
 .It Ic tcp Ar option
 Enable or disable the specified TCP/IP options; see
 .Xr tcp 4
@@ -715,6 +730,17 @@ server "example.com" {
 
 server "www.example.com" {
 	listen on 10.0.0.1 port 80
+}
+.Ed
+The request can also be rewritten with the
+.Ic request rewrite
+directive: 
+.Bd -literal -offset indent
+server "example.com" {
+	listen on * port 80
+	location match "/old/(.*)" {
+		request rewrite "/new/%1"
+	}
 }
 .Ed
 .Sh SEE ALSO
Index: usr.sbin/httpd/httpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/httpd.h,v
retrieving revision 1.137
diff -u -p -u -p -r1.137 httpd.h
--- usr.sbin/httpd/httpd.h	19 May 2018 13:56:56 -0000	1.137
+++ usr.sbin/httpd/httpd.h	29 May 2018 16:35:30 -0000
@@ -398,13 +398,15 @@ SPLAY_HEAD(client_tree, client);
 #define SRVFLAG_SERVER_MATCH	0x00200000
 #define SRVFLAG_SERVER_HSTS	0x00400000
 #define SRVFLAG_DEFAULT_TYPE	0x00800000
+#define SRVFLAG_PATH_REWRITE	0x01000000
+#define SRVFLAG_NO_PATH_REWRITE	0x02000000
 
 #define SRVFLAG_BITS							\
 	"\10\01INDEX\02NO_INDEX\03AUTO_INDEX\04NO_AUTO_INDEX"		\
 	"\05ROOT\06LOCATION\07FCGI\10NO_FCGI\11LOG\12NO_LOG\13SOCKET"	\
 	"\14SYSLOG\15NO_SYSLOG\16TLS\17ACCESS_LOG\20ERROR_LOG"		\
 	"\21AUTH\22NO_AUTH\23BLOCK\24NO_BLOCK\25LOCATION_MATCH"		\
-	"\26SERVER_MATCH\27SERVER_HSTS\30DEFAULT_TYPE"
+	"\26SERVER_MATCH\27SERVER_HSTS\30DEFAULT_TYPE\31PATH\32NO_PATH"
 
 #define TCPFLAG_NODELAY		0x01
 #define TCPFLAG_NNODELAY	0x02
@@ -470,8 +472,9 @@ struct server_config {
 	uint32_t		 parent_id;
 	char			 name[HOST_NAME_MAX+1];
 	char			 location[HTTPD_LOCATION_MAX];
-	char			 index[PATH_MAX];
 	char			 root[PATH_MAX];
+	char			 path[PATH_MAX];
+	char			 index[PATH_MAX];
 	char			 socket[PATH_MAX];
 	char			 accesslog[PATH_MAX];
 	char			 errorlog[PATH_MAX];
Index: usr.sbin/httpd/parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/parse.y,v
retrieving revision 1.99
diff -u -p -u -p -r1.99 parse.y
--- usr.sbin/httpd/parse.y	23 May 2018 19:11:48 -0000	1.99
+++ usr.sbin/httpd/parse.y	29 May 2018 16:35:31 -0000
@@ -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 PASS REWRITE
 %token	CA CLIENT CRL OPTIONAL
 %token	<v.string>	STRING
 %token  <v.number>	NUMBER
@@ -486,6 +486,7 @@ serveroptsl	: LISTEN ON STRING opttls po
 				YYERROR;
 			}
 		}
+		| request
 		| root
 		| directory
 		| logformat
@@ -804,7 +805,33 @@ rootflags	: STRING		{
 			free($1);
 			srv->srv_conf.flags |= SRVFLAG_ROOT;
 		}
-		| STRIP NUMBER		{
+		;
+
+request		: REQUEST requestflags
+		| REQUEST '{' optnl requestflags_l '}'
+		;
+
+requestflags_l	: requestflags optcommanl requestflags_l
+		| requestflags optnl
+		;
+
+requestflags	: REWRITE STRING		{
+			if (strlcpy(srv->srv_conf.path, $2,
+			    sizeof(srv->srv_conf.path)) >=
+			    sizeof(srv->srv_conf.path)) {
+				yyerror("request path too long");
+				free($2);
+				YYERROR;
+			}
+			free($2);
+			srv->srv_conf.flags |= SRVFLAG_PATH_REWRITE;
+			srv->srv_conf.flags &= ~SRVFLAG_NO_PATH_REWRITE;
+		}
+		| NO REWRITE			{
+			srv->srv_conf.flags |= SRVFLAG_NO_PATH_REWRITE;
+			srv->srv_conf.flags &= ~SRVFLAG_PATH_REWRITE;
+		}
+		| STRIP NUMBER			{
 			if ($2 < 0 || $2 > INT_MAX) {
 				yyerror("invalid strip number");
 				YYERROR;
@@ -1261,6 +1288,7 @@ lookup(char *s)
 		{ "request",		REQUEST },
 		{ "requests",		REQUESTS },
 		{ "return",		RETURN },
+		{ "rewrite",		REWRITE },
 		{ "root",		ROOT },
 		{ "sack",		SACK },
 		{ "server",		SERVER },
Index: usr.sbin/httpd/server_http.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/server_http.c,v
retrieving revision 1.119
diff -u -p -u -p -r1.119 server_http.c
--- usr.sbin/httpd/server_http.c	6 Apr 2018 13:02:07 -0000	1.119
+++ usr.sbin/httpd/server_http.c	29 May 2018 16:35:31 -0000
@@ -1180,10 +1180,13 @@ server_response(struct httpd *httpd, str
 	char			*hostval;
 	const char		*errstr = NULL;
 
-	/* Canonicalize the request path */
+	/* Decode the URL */
 	if (desc->http_path == NULL ||
-	    url_decode(desc->http_path) == NULL ||
-	    canonicalize_path(desc->http_path, path, sizeof(path)) == NULL)
+	    url_decode(desc->http_path) == NULL)
+		goto fail;
+
+	/* Canonicalize the request path */
+	if (canonicalize_path(desc->http_path, path, sizeof(path)) == NULL)
 		goto fail;
 	free(desc->http_path);
 	if ((desc->http_path = strdup(path)) == NULL)
@@ -1286,6 +1289,28 @@ server_response(struct httpd *httpd, str
 
 	/* Now search for the location */
 	srv_conf = server_getlocation(clt, desc->http_path);
+
+	/* Optional rewrite */
+	if (srv_conf->flags & SRVFLAG_PATH_REWRITE) {
+		if (server_expand_http(clt, srv_conf->path,
+		    path, sizeof(path)) == NULL)
+			goto fail;
+
+		/* Canonicalize the updated request path */
+		if (canonicalize_path(path,
+		    path, sizeof(path)) == NULL)
+			goto fail;
+
+		log_debug("%s: rewrote %s -> %s", __func__,
+		    desc->http_path, path);
+
+		free(desc->http_path);
+		if ((desc->http_path = strdup(path)) == NULL)
+			goto fail;
+
+		/* Now search for the updated location */
+		srv_conf = server_getlocation(clt, desc->http_path);
+	}
 
 	if (srv_conf->flags & SRVFLAG_BLOCK) {
 		server_abort_http(clt, srv_conf->return_code,

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic