proxy: handle encoded path in URL

fix issue #1362

Signed-off-by: Tw <tw19881113@gmail.com>
This commit is contained in:
Tw 2017-01-26 12:23:50 +08:00
parent 91ff734327
commit c37481cc7b
3 changed files with 62 additions and 11 deletions

View File

@ -273,11 +273,6 @@ func createUpstreamRequest(r *http.Request) *http.Request {
outreq.Body = nil outreq.Body = nil
} }
// Restore URL Path if it has been modified
if outreq.URL.RawPath != "" {
outreq.URL.Opaque = outreq.URL.RawPath
}
// We are modifying the same underlying map from req (shallow // We are modifying the same underlying map from req (shallow
// copied above) so we only copy it if necessary. // copied above) so we only copy it if necessary.
copiedHeaders := false copiedHeaders := false

View File

@ -949,6 +949,26 @@ func TestProxyDirectorURL(t *testing.T) {
expectURL: `https://localhost:2021/t/mypath`, expectURL: `https://localhost:2021/t/mypath`,
without: "/test", without: "/test",
}, },
{
requestURL: `http://localhost:2020/%2C`,
targetURL: `https://localhost:2021/t/`,
expectURL: `https://localhost:2021/t/%2C`,
},
{
requestURL: `http://localhost:2020/%2C/`,
targetURL: `https://localhost:2021/t/`,
expectURL: `https://localhost:2021/t/%2C/`,
},
{
requestURL: `http://localhost:2020/test`,
targetURL: `https://localhost:2021/%2C`,
expectURL: `https://localhost:2021/%2C/test`,
},
{
requestURL: `http://localhost:2020/%2C`,
targetURL: `https://localhost:2021/%2C`,
expectURL: `https://localhost:2021/%2C/%2C`,
},
} { } {
targetURL, err := url.Parse(c.targetURL) targetURL, err := url.Parse(c.targetURL)
if err != nil { if err != nil {

View File

@ -18,7 +18,6 @@ import (
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
"path"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -89,6 +88,18 @@ func socketDial(hostName string) func(network, addr string) (conn net.Conn, err
} }
} }
func singleJoiningSlash(a, b string) string {
aslash := strings.HasSuffix(a, "/")
bslash := strings.HasPrefix(b, "/")
switch {
case aslash && bslash:
return a + b[1:]
case !aslash && !bslash && b != "":
return a + "/" + b
}
return a + b
}
// NewSingleHostReverseProxy returns a new ReverseProxy that rewrites // NewSingleHostReverseProxy returns a new ReverseProxy that rewrites
// URLs to the scheme, host, and base path provided in target. If the // URLs to the scheme, host, and base path provided in target. If the
// target's path is "/base" and the incoming request was for "/dir", // target's path is "/base" and the incoming request was for "/dir",
@ -119,12 +130,31 @@ func NewSingleHostReverseProxy(target *url.URL, without string, keepalive int) *
} }
} }
hadTrailingSlash := strings.HasSuffix(req.URL.Path, "/") // prefer returns val if it isn't empty, otherwise def
req.URL.Path = path.Join(target.Path, req.URL.Path) prefer := func(val, def string) string {
// path.Join will strip off the last /, so put it back if it was there. if val != "" {
if hadTrailingSlash && !strings.HasSuffix(req.URL.Path, "/") { return val
req.URL.Path = req.URL.Path + "/" }
return def
} }
// Make up the final URL by concatenating the request and target URL.
//
// If there is encoded part in request or target URL,
// the final URL should also be in encoded format.
// Here, we concatenate their encoded parts which are stored
// in URL.Opaque and URL.RawPath, if it is empty use
// URL.Path instead.
if req.URL.Opaque != "" || target.Opaque != "" {
req.URL.Opaque = singleJoiningSlash(
prefer(target.Opaque, target.Path),
prefer(req.URL.Opaque, req.URL.Path))
}
if req.URL.RawPath != "" || target.RawPath != "" {
req.URL.RawPath = singleJoiningSlash(
prefer(target.RawPath, target.Path),
prefer(req.URL.RawPath, req.URL.Path))
}
req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
// Trims the path of the socket from the URL path. // Trims the path of the socket from the URL path.
// This is done because req.URL passed to your proxied service // This is done because req.URL passed to your proxied service
@ -136,6 +166,12 @@ func NewSingleHostReverseProxy(target *url.URL, without string, keepalive int) *
// See comment on socketDial for the trim // See comment on socketDial for the trim
socketPrefix := target.String()[len("unix://"):] socketPrefix := target.String()[len("unix://"):]
req.URL.Path = strings.TrimPrefix(req.URL.Path, socketPrefix) req.URL.Path = strings.TrimPrefix(req.URL.Path, socketPrefix)
if req.URL.Opaque != "" {
req.URL.Opaque = strings.TrimPrefix(req.URL.Opaque, socketPrefix)
}
if req.URL.RawPath != "" {
req.URL.RawPath = strings.TrimPrefix(req.URL.RawPath, socketPrefix)
}
} }
if targetQuery == "" || req.URL.RawQuery == "" { if targetQuery == "" || req.URL.RawQuery == "" {