@@ -327,10 +327,23 @@ func (r Request) String() string {
327327// NewRequest parses an http.Request into an imageproxy Request. Options and
328328// the remote image URL are specified in the request path, formatted as:
329329// /{options}/{remote_url}. Options may be omitted, so a request path may
330- // simply contain /{remote_url}. The remote URL must either be:
330+ // simply contain /{remote_url}.
331331//
332- // - an absolute "http" or "https" URL, not be URL encoded, with optional query string, or
333- // - base64 encoded (URL safe, no padding).
332+ // The remote URL may be included in plain text without any encoding,
333+ // percent-encoded (aka URL encoded), or base64 encoded (URL safe, no padding).
334+ //
335+ // When no encoding is used, any URL query string is treated as part of the remote URL.
336+ // For example, given the proxy URL of `http://localhost/x/http://example.com/?id=1`,
337+ // the remote URL is `http://example.com/?id=1`.
338+ //
339+ // When percent-encoding is used, the full URL must be encoded.
340+ // Any query string on the proxy URL is NOT included as part of the remote URL.
341+ // Percent-encoded URLs must be absolute URLs;
342+ // they cannot be relative URLs used with a default base URL.
343+ //
344+ // When base64 encoding is used, the full URL must be encoded.
345+ // Any query string on the proxy URL is NOT included as part of the remote URL.
346+ // Base64 encoded URLs may be relative URLs used with a default base URL.
334347//
335348// Assuming an imageproxy server running on localhost, the following are all
336349// valid imageproxy requests:
@@ -339,11 +352,12 @@ func (r Request) String() string {
339352// http://localhost/100x200,r90/http://example.com/image.jpg?foo=bar
340353// http://localhost//http://example.com/image.jpg
341354// http://localhost/http://example.com/image.jpg
355+ // http://localhost/x/http%3A%2F%2Fexample.com%2Fimage.jpg
342356// http://localhost/100x200/aHR0cDovL2V4YW1wbGUuY29tL2ltYWdlLmpwZw
343357func NewRequest (r * http.Request , baseURL * url.URL ) (* Request , error ) {
344358 var err error
345359 req := & Request {Original : r }
346- var enc bool // whether the remote URL was base64 encoded
360+ var enc bool // whether the remote URL was base64 or URL encoded
347361
348362 path := r .URL .EscapedPath ()[1 :] // strip leading slash
349363 req .URL , enc , err = parseURL (path , baseURL )
@@ -376,14 +390,15 @@ func NewRequest(r *http.Request, baseURL *url.URL) (*Request, error) {
376390 }
377391
378392 if ! enc {
379- // if the remote URL was not base64- encoded,
393+ // if the remote URL was not base64 or URL encoded,
380394 // then the query string is part of the remote URL
381395 req .URL .RawQuery = r .URL .RawQuery
382396 }
383397 return req , nil
384398}
385399
386400var reCleanedURL = regexp .MustCompile (`^(https?):/+([^/])` )
401+ var reIsEncodedURL = regexp .MustCompile (`^(?i)https?%3A%2F` )
387402
388403// parseURL parses s as a URL, handling URLs that have been munged by
389404// path.Clean or a webserver that collapses multiple slashes.
@@ -406,6 +421,14 @@ func parseURL(s string, baseURL *url.URL) (_ *url.URL, enc bool, _ error) {
406421 }
407422 }
408423
424+ // If the string looks like a URL encoded absolute HTTP(S) URL, decode it.
425+ if reIsEncodedURL .MatchString (s ) {
426+ if u , err := url .PathUnescape (s ); err == nil {
427+ enc = true
428+ s = u
429+ }
430+ }
431+
409432 s = reCleanedURL .ReplaceAllString (s , "$1://$2" )
410433 u , err := url .Parse (s )
411434 return u , enc , err
0 commit comments