diff --git a/data.go b/data.go index 284da4559..d590c6cec 100644 --- a/data.go +++ b/data.go @@ -337,9 +337,14 @@ func (r Request) String() string { // http://localhost/http://example.com/image.jpg func NewRequest(r *http.Request, baseURL *url.URL) (*Request, error) { var err error + var additionalQuery string req := &Request{Original: r} - path := r.URL.EscapedPath()[1:] // strip leading slash + path := r.URL.EscapedPath()[1:] // strip leading slash + decodedPath, err, additionalQuery := decodeURL(path) // Conditionally decode url path param if it is url encoded this enables us to process image urls with query params built in + if err == nil { + path = decodedPath + } req.URL, err = parseURL(path) if err != nil || !req.URL.IsAbs() { // first segment should be options @@ -370,7 +375,7 @@ func NewRequest(r *http.Request, baseURL *url.URL) (*Request, error) { } // query string is always part of the remote URL - req.URL.RawQuery = r.URL.RawQuery + req.URL.RawQuery = combineQueries(r.URL.RawQuery, additionalQuery) return req, nil } @@ -382,3 +387,45 @@ func parseURL(s string) (*url.URL, error) { s = reCleanedURL.ReplaceAllString(s, "$1://$2") return url.Parse(s) } + +var reIsEncodedUrl = regexp.MustCompile(`^https?%`) + +func decodeURL(s string) (string, error, string) { + var startsWithHttp = strings.HasPrefix(s, "http") + var prefix = "" + var u = s + + if !startsWithHttp && strings.Contains(s, "http") { + var parts = strings.SplitN(s, "http", 2) + u = "http" + parts[1] + prefix = parts[0] + } + + var isUrlEncoded = reIsEncodedUrl.MatchString(u) + + if isUrlEncoded { + u, err := url.QueryUnescape(u) + if err != nil { + return u, err, "" + } + + var parsed, err2 = url.Parse(u) + if err2 != nil { + return u, err2, "" + } + + return prefix + u, err2, parsed.RawQuery + } else { + return s, nil, "" + } +} + +func combineQueries(a string, b string) string { + if a == "" { + return b + } else if b == "" { + return a + } else { + return a + "&" + b + } +} diff --git a/data_test.go b/data_test.go index 80d3c3dd2..aa9073833 100644 --- a/data_test.go +++ b/data_test.go @@ -156,6 +156,43 @@ func TestNewRequest(t *testing.T) { "http://localhost/http://example.com/%2C", "http://example.com/%2C", emptyOptions, false, }, + // URI Encoded cases + { + "http://localhost/1x2/http%3A%2F%2Fexample.com%2Ffoo", + "http://example.com/foo", Options{Width: 1, Height: 2}, false, + }, + { + "http://localhost/1x2/http%3A%2F%2Fexample.com%2Fhttp%2Fstuff", + "http://example.com/http/stuff", Options{Width: 1, Height: 2}, false, + }, + { + "http://localhost/http%3A%2F%2Fexample.com%2Ffoo", + "http://example.com/foo", emptyOptions, false, + }, + { + "http://localhost/http%3A%2F%2Fexample.com%2Ffoo", + "http://example.com/foo", emptyOptions, false, + }, + { + "http://localhost/http%3A%2Fexample.com%2Ffoo", + "http://example.com/foo", emptyOptions, false, + }, + { + "http://localhost/http%3A%2F%2F%2Fexample.com%2Ffoo", + "http://example.com/foo", emptyOptions, false, + }, + { + "http://localhost//http%3A%2F%2Fexample.com%2Ffoo", + "http://example.com/foo", emptyOptions, false, + }, + { + "http://localhost/http%3A%2F%2Fexample.com%2Ffoo%3Ftest%3D1%26test%3D2", + "http://example.com/foo?test=1&test=2", emptyOptions, false, + }, + { + "http://localhost/1x2/http%3A%2F%2Fexample.com%2Ffoo%3Ftest%3D1%26test%3D2", + "http://example.com/foo?test=1&test=2", Options{Width: 1, Height: 2}, false, + }, } for _, tt := range tests {