Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb3439da3d |
@@ -38,40 +38,44 @@ func getImage(t *testing.T, fix *testFixtures, target string) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// generateSignedURL returns the /v1/image/ URL of req, signed with key and
|
// generateSignedURL returns the /v1/image/ URL of req, signed with key and
|
||||||
// expiring after ttl. An unset quality or fit of req is first set to 85 or
|
// expiring after ttl. It signs a copy of req, so req itself is not changed.
|
||||||
// cover, the values the route uses when a URL has no q or fit. The URL carries
|
// An unset quality or fit in the copy is set to 85 or cover, the values the
|
||||||
// q and fit next to sig and exp, so the route verifies it for the quality and
|
// route uses when a URL has no q or fit. The URL carries q and fit next to sig
|
||||||
// fit it was signed with.
|
// and exp, so the route verifies it for the quality and fit it was signed
|
||||||
|
// with.
|
||||||
func generateSignedURL(
|
func generateSignedURL(
|
||||||
key string, req *imagerequest.Request, ttl time.Duration,
|
key string, req *imagerequest.Request, ttl time.Duration,
|
||||||
) string {
|
) string {
|
||||||
if req.Transform.Quality == 0 {
|
signed := *req
|
||||||
req.Transform.Quality = 85
|
|
||||||
|
if signed.Transform.Quality == 0 {
|
||||||
|
signed.Transform.Quality = 85
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.Transform.FitMode == "" {
|
if signed.Transform.FitMode == "" {
|
||||||
req.Transform.FitMode = imagerequest.FitCover
|
signed.Transform.FitMode = imagerequest.FitCover
|
||||||
}
|
}
|
||||||
|
|
||||||
expires := time.Now().Add(ttl)
|
expires := time.Now().Add(ttl)
|
||||||
sig := signature.New(key).Sign(req, expires)
|
sig := signature.New(key).Sign(&signed, expires)
|
||||||
|
|
||||||
// A source query follows the source path after a "?". The image route
|
// A source query follows the source path after a "?". The image route
|
||||||
// reads the path decoded once, so the whole path is written in Go's own
|
// reads the path decoded once, so the whole path is written in Go's own
|
||||||
// percent-encoding of a URL path, which writes "?" as %3F and "%" as %25,
|
// percent-encoding of a URL path, which writes "?" as %3F and "%" as %25,
|
||||||
// and leaves "/", ";", ",", "=" and "&" as they are.
|
// and leaves "/", ";", ",", "=" and "&" as they are.
|
||||||
sourcePathAndQuery := req.Source.Path
|
sourcePathAndQuery := signed.Source.Path
|
||||||
if req.Source.Query != "" {
|
if signed.Source.Query != "" {
|
||||||
sourcePathAndQuery += "?" + req.Source.Query
|
sourcePathAndQuery += "?" + signed.Source.Query
|
||||||
}
|
}
|
||||||
|
|
||||||
unescapedPath := fmt.Sprintf("/v1/image/%s%s/%dx%d.%s",
|
unescapedPath := fmt.Sprintf("/v1/image/%s%s/%dx%d.%s",
|
||||||
req.Source.Host, sourcePathAndQuery,
|
signed.Source.Host, sourcePathAndQuery,
|
||||||
req.Transform.Size.Width, req.Transform.Size.Height, req.Transform.Format)
|
signed.Transform.Size.Width, signed.Transform.Size.Height,
|
||||||
|
signed.Transform.Format)
|
||||||
|
|
||||||
return fmt.Sprintf("%s?sig=%s&exp=%d&q=%d&fit=%s",
|
return fmt.Sprintf("%s?sig=%s&exp=%d&q=%d&fit=%s",
|
||||||
(&url.URL{Path: unescapedPath}).EscapedPath(), sig, expires.Unix(),
|
(&url.URL{Path: unescapedPath}).EscapedPath(), sig, expires.Unix(),
|
||||||
req.Transform.Quality, req.Transform.FitMode)
|
signed.Transform.Quality, signed.Transform.FitMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestHandleImage_SignatureCoversQualityAndFit signs a URL for quality 85
|
// TestHandleImage_SignatureCoversQualityAndFit signs a URL for quality 85
|
||||||
|
|||||||
@@ -126,9 +126,8 @@ func parseImageComponents(remainder string) (*Request, error) {
|
|||||||
return nil, ErrMissingHost
|
return nil, ErrMissingHost
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only the forms hostRegex describes are accepted. A "?", "#" or "@" in
|
// Later steps put the host unparsed into the allowlist check, the cache key
|
||||||
// the host would make Go's URL parser, which the allowlist check and the
|
// and the upstream URL, so only the forms hostRegex describes are accepted
|
||||||
// fetcher use, read a different host from the one allowlisted and cached
|
|
||||||
hostMatch := hostRegex.FindStringSubmatch(host)
|
hostMatch := hostRegex.FindStringSubmatch(host)
|
||||||
if hostMatch == nil {
|
if hostMatch == nil {
|
||||||
return nil, fmt.Errorf("%w: %q", ErrInvalidHost, host)
|
return nil, fmt.Errorf("%w: %q", ErrInvalidHost, host)
|
||||||
@@ -147,9 +146,8 @@ func parseImageComponents(remainder string) (*Request, error) {
|
|||||||
// host
|
// host
|
||||||
host = strings.ToLower(host)
|
host = strings.ToLower(host)
|
||||||
|
|
||||||
// Go's URL parser, which the fetcher uses, reads a "#" as the start of a
|
// A "#" starts a URL's fragment, so neither the source path nor the source
|
||||||
// fragment and drops the rest, so each text after it would be another
|
// query may hold one
|
||||||
// spelling, cached under its own keys, of one upstream request
|
|
||||||
if strings.Contains(path, "#") {
|
if strings.Contains(path, "#") {
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
`%w: the source path or query holds a "#"`, ErrInvalidPath)
|
`%w: the source path or query holds a "#"`, ErrInvalidPath)
|
||||||
|
|||||||
Reference in New Issue
Block a user