On Sat, Aug 1, 2009 at 8:22 PM, James Lee <jlee@thestaticvoid.com> wrote:
Hello,

I have set up two virtual servers where one redirects to another,
similar to:

> <server foo.com>
>         port = 80
>         listen = 0.0.0.0
>         docroot = /docs/foo.com
>         allowed_scripts = cgi
>         dir_listings = true
> </server>
>
> <server foo.org>
>         port = 80
>         listen = 0.0.0.0
>         <redirect>
>                 / = foo.com
>         </redirect>
> <server>

The problem is, when someone requests http://foo.org/bar/baz.html they
get redirected to http://foo.com//bar/baz.html (note the extra slash).
This is due to the way yaws parses the right side of "/ = foo.com".  In
yaws_api:parse_url/5, we see that:

> parse_url(host, Strict, U, Str, Ack) ->
>     case Str of
>         [] ->
>             U#url{host = lists:reverse(Ack),
>                   path = "/"
>                  };
>         [$/|Tail] ->
>             U2 = U#url{host = lists:reverse(Ack)},
>             parse_url(path, Strict, U2, Tail,"/");
> ...

if the function gets to the end of the string and it hasn't read a '/',
it will return and just put a '/' in the path component of the url.
Then the yaws_server:deliver_302_map/4 function comes along and simply
appends the url ("http://foo.com/") with the request ("/bar/baz.html").

I think the behavior of the parse_url function is incorrect, but
hesitate to change it there due to how widely used it is.  It's hard to
tell if a change there would break anything.

I worked with James offline to develop a patch, which you can find below. James also provided enough details so I could add a regression test for this problem. I've pushed these changes along with some general test cleanup to github.

Thanks, James, for reporting this and for your help with correcting it.

--steve


diff --git a/src/yaws_server.erl b/src/yaws_server.erl
index 7fc54de..e70c205 100644
--- a/src/yaws_server.erl
+++ b/src/yaws_server.erl
@@ -2110,10 +2110,16 @@ deliver_302_map(CliSock, Req, Arg,
     {P, Q} = yaws:split_at(DecPath, $?),
     LocPath = yaws_api:format_partial_url(URL, get(sc)),
     Loc = if
-              Mode == append, Q == [] ->
-                  ["Location: ", LocPath, P, "\r\n"];
-              Mode == append, Q /= [] ->
-                  ["Location: ", LocPath, P, "?", Q, "\r\n"];
+              Mode == append ->
+                  Newpath = filename:join([URL#url.path ++ P]),
+                  NLocPath = yaws_api:format_partial_url(
+                               URL#url{path = Newpath}, get(sc)),
+                  case Q of
+                      [] ->
+                          ["Location: ", NLocPath, "\r\n"];
+                      _Q ->
+                          ["Location: ", NLocPath, "?", Q, "\r\n"]
+                  end;
               Mode == noappend,Q == [] ->
                   ["Location: ", LocPath, "\r\n"];
               Mode == noappend,Q /= [] ->