Bug 49746

Summary: mod_rewrite urlencode option
Product: Apache httpd-2 Reporter: jhmartin <jhmartin>
Component: mod_rewriteAssignee: Apache HTTPD Bugs Mailing List <bugs>
Status: REOPENED ---    
Severity: enhancement CC: hendrik.harms
Priority: P4 Keywords: PatchAvailable
Version: 2.5-HEAD   
Target Milestone: ---   
Hardware: All   
OS: All   
Attachments: Add urlencode function.

Description jhmartin@toger.us 2010-08-12 15:07:43 UTC
I suggest adding a flag to mod_rewrite to optionally url-encode captured values.  

Something like:
RewriteRule /(.*) /some/other/url=&target=http://www.example.com/handler?foo=bar&target=$1&zed=zee [UE]

where UE causes the (.*) value to be urlencoded before being placed into $1.

This handles cases where you want to rewrite the request url into an argument of another url, and the requested url might have a multivalue query string in it.

Example:
http://www.example.com/foo/?bar=zed&foo=zee 
becomes
http://www.example.com/handler?foo=bar&target=%2Ffoo%2F%3Fbar%3Dzed%26foo%3Dzee&zed=zee
Comment 1 Dan Poirier 2010-08-12 15:28:17 UTC
This feature is already available.

http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html#mapfunc

Look for the internal map for "escape".
Comment 2 jhmartin@toger.us 2010-08-12 16:24:06 UTC
This does not appear to work for escaping query-string arguments, as the & sign is not being escaped:

(5) map lookup OK: map=escape key=foo=bar&zed=zee&fib/fib -> val=foo=bar&zed=zee&fib/fib

This is with
Apache/2.2.15 (Unix) mod_ssl/2.2.15 OpenSSL/1.0.0 

I'm using
RewriteMap escape int:escape
and
${escape:%{QUERY_STRING}} in the rewrite rule.

wget -O- "http://www.example.com/?foo=bar&zed=zee&fib/fib"

Perhaps there is use in adding another map function with a more comprehensive translation list?
Comment 3 jhmartin@toger.us 2010-08-13 12:03:13 UTC
Since it looks like the escape map really just calls ap_escape_uri, which is a macro for ap_escape_os_path; how about a htmlescape map that calls ap_escape_html?
Comment 4 Bob Ionescu 2010-08-14 04:55:07 UTC
> where UE causes the (.*) value to be urlencoded before being placed into $1.

That's already done by the B flag.
Comment 5 jhmartin@toger.us 2010-08-25 12:44:56 UTC
Ah didn't see that (was looking at 2.0).  Thank you.
Comment 6 jhmartin@toger.us 2010-08-25 13:34:31 UTC
I'm sorry, have to reopen again.  Neither int:escape or [B] handle the case where the original request had a query string, and I now want to package up the entire original request and pass it in the query string.

Example:

/foo/bar?zed=zee&ivy=true  Should be come /some/other/url?target=/foo/bar%3Fzed=zee%26ivy=true, or something along those lines.

With the current mechanisms, the & in the query string is not escaped by either int:escape or [B] (as there is no way to backreference query_string), meaning the servlet thinks everything after the first name/value pair is a separate argument and not part of target.

RewriteMap escape int:escape
RewriteRule /(.*) /some/url?resource=fibble&target=http://%{HTTP_HOST}/$1?${escape:%{QUERY_STRING}} [B,PT,L]

(2) init rewrite engine with requested uri /foo/bar
(3) applying pattern '/(.*)' to uri '/foo/bar'
(5) escaping backreference 'foo/bar' to 'foo%2fbar'
(5) map lookup OK: map=escape key=zed=zee&ivy=true -> val=zed=zee&ivy=true
(2) rewrite '/foo/bar' -> '/some/url?resource=fibble&target=http://127.0.0.1:26080/foo%2fbar?zed=zee&ivy=true'
(3) split uri=/some/url?resource=fibble&target=http://127.0.0.1:26080/foo%2fbar?zed=zee&ivy=true -> uri=/some/url, args=resource=fibble&target=http://127.0.0.1:26080/foo%2fbar?zed=zee&ivy=true
(2) forcing '/some/url' to get passed through to next API URI-to-filename handler
Comment 7 Philippe Lantin 2016-01-06 23:48:38 UTC
Created attachment 33414 [details]
Add urlencode function.

Patch against 2.2.31 that adds urlencode function from apr. Sample usage:

RewriteMap urlencode int:urlencode
RewriteRule /(.*) /some/url?resource=fibble&target=http://%{HTTP_HOST}/$1?${urlencode:%{QUERY_STRING}} [B,PT,L]
Comment 8 Hendrik Harms 2018-08-24 07:57:26 UTC
# using 2.4.33
# example for a redirect with encoded query
RewriteCond %{THE_REQUEST} " (/[^ ]+) "
RewriteRule .* /some/url?resource=fibble&target=%1 [R,B,NE,L,DPI,QSD]