Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d03600ab0 |
No files matched your search
@@ -289,7 +289,8 @@ path under `/v1/` answers 200, in maintenance mode too.
|
|||||||
- 400 for a token that does not decrypt.
|
- 400 for a token that does not decrypt.
|
||||||
- 400 for a token that asks for a size or fit that is not valid.
|
- 400 for a token that asks for a size or fit that is not valid.
|
||||||
- 410 once the token has expired.
|
- 410 once the token has expired.
|
||||||
- 400 for an `Accept` header that is not valid.
|
- 400 when the token's format is `auto` and the `Accept` header is not
|
||||||
|
valid.
|
||||||
- These answers, for the same causes as on `/v1/image/`:
|
- These answers, for the same causes as on `/v1/image/`:
|
||||||
- 406
|
- 406
|
||||||
- 403
|
- 403
|
||||||
|
|||||||
@@ -54,9 +54,12 @@ P2: security: per-origin rate limiting
|
|||||||
warn level.
|
warn level.
|
||||||
- The "image served" line carries the host, the path and `fetched_bytes` on
|
- The "image served" line carries the host, the path and `fetched_bytes` on
|
||||||
both routes. The debug line "encrypted image request" is gone.
|
both routes. The debug line "encrypted image request" is gone.
|
||||||
- A failed write is logged once, as "failed to write response" at error
|
- The debug line "failed to decrypt URL" is now "invalid encrypted URL". A
|
||||||
level. It carries the `request_id`, the host, the path, `served_bytes` and
|
token that decrypts to a payload that does not decode also gets it.
|
||||||
the error, and is not also logged as "image served".
|
- A failed write is logged once, as "failed to write response" at warn
|
||||||
|
level, since most often the client has gone away. It carries the
|
||||||
|
`request_id`, the host, the path, `served_bytes` and the error, and is not
|
||||||
|
also logged as "image served".
|
||||||
- 2026-10-26 every key derived from the signing key is derived in one place
|
- 2026-10-26 every key derived from the signing key is derived in one place
|
||||||
(closes #315): `seal.DeriveKeys` returns them as one `seal.Keys`, with their
|
(closes #315): `seal.DeriveKeys` returns them as one `seal.Keys`, with their
|
||||||
salts declared beside it. `internal/providers` calls it once at startup. Each
|
salts declared beside it. `internal/providers` calls it once at startup. Each
|
||||||
|
|||||||
@@ -190,8 +190,8 @@ func (s *Handlers) validateSignature(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parseExpires reads the exp query parameter, a Unix time in seconds. An exp
|
// parseExpires reads the exp query parameter, a Unix time in seconds. An exp
|
||||||
// missing from the URL gives the zero time, which the signature check takes
|
// missing from the URL gives the zero time, which the signature check refuses
|
||||||
// as no expiration. An exp in the URL that is not a whole number, an empty
|
// as a missing exp. An exp in the URL that is not a whole number, an empty
|
||||||
// one included, is an error naming exp and the value.
|
// one included, is an error naming exp and the value.
|
||||||
func parseExpires(query url.Values) (time.Time, error) {
|
func parseExpires(query url.Values) (time.Time, error) {
|
||||||
if !query.Has("exp") {
|
if !query.Has("exp") {
|
||||||
@@ -221,8 +221,9 @@ func (s *Handlers) HandleTooManyRequests() http.HandlerFunc {
|
|||||||
|
|
||||||
// respondImageError answers err, the image service's error for req, on both
|
// respondImageError answers err, the image service's error for req, on both
|
||||||
// image routes. It logs a 500 at error level, as pixa's own failure. It logs
|
// image routes. It logs a 500 at error level, as pixa's own failure. It logs
|
||||||
// every other answer at warn level, since the client, the upstream or a full
|
// the other answers at warn level, since the client, the upstream or a full
|
||||||
// limit caused it.
|
// limit caused them. The 415 for a source refused for its first bytes is not
|
||||||
|
// logged here, because the image service logs it.
|
||||||
func (s *Handlers) respondImageError(
|
func (s *Handlers) respondImageError(
|
||||||
w http.ResponseWriter, req *imagerequest.Request, err error,
|
w http.ResponseWriter, req *imagerequest.Request, err error,
|
||||||
) {
|
) {
|
||||||
@@ -394,9 +395,11 @@ func (s *Handlers) serveImage(
|
|||||||
// Stream the response
|
// Stream the response
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
|
// A failed write is most often a client that has gone away, not pixa's
|
||||||
|
// failure, so it is logged at warn level
|
||||||
servedBytes, err := io.Copy(w, resp.Content)
|
servedBytes, err := io.Copy(w, resp.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Error("failed to write response",
|
s.log.Warn("failed to write response",
|
||||||
"request_id", middleware.GetReqID(r.Context()),
|
"request_id", middleware.GetReqID(r.Context()),
|
||||||
"host", req.Source.Host,
|
"host", req.Source.Host,
|
||||||
"path", req.Source.Path,
|
"path", req.Source.Path,
|
||||||
|
|||||||
@@ -518,7 +518,7 @@ func (failingResponseWriter) Write([]byte) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TestImageRoutes_FailedWriteLoggedOnce checks that both image routes log a
|
// TestImageRoutes_FailedWriteLoggedOnce checks that both image routes log a
|
||||||
// failed write of the image once, at error level, with the request's ID, the
|
// failed write of the image once, at warn level, with the request's ID, the
|
||||||
// source host and path, the bytes served and the error. Neither may also log
|
// source host and path, the bytes served and the error. Neither may also log
|
||||||
// the image as served.
|
// the image as served.
|
||||||
func TestImageRoutes_FailedWriteLoggedOnce(t *testing.T) {
|
func TestImageRoutes_FailedWriteLoggedOnce(t *testing.T) {
|
||||||
@@ -568,7 +568,7 @@ func TestImageRoutes_FailedWriteLoggedOnce(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
want := map[string]any{
|
want := map[string]any{
|
||||||
"level": slog.LevelError.String(),
|
"level": slog.LevelWarn.String(),
|
||||||
"request_id": requestID,
|
"request_id": requestID,
|
||||||
"host": signedHost,
|
"host": signedHost,
|
||||||
"path": photoPath,
|
"path": photoPath,
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ func (s *Handlers) parseImageEncRequest(
|
|||||||
return nil, time.Time{}, false
|
return nil, time.Time{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
s.log.Debug("failed to decrypt URL", "error", err)
|
s.log.Debug("invalid encrypted URL", "error", err)
|
||||||
s.respondError(w, "invalid encrypted URL", http.StatusBadRequest)
|
s.respondError(w, "invalid encrypted URL", http.StatusBadRequest)
|
||||||
|
|
||||||
return nil, time.Time{}, false
|
return nil, time.Time{}, false
|
||||||
@@ -62,8 +62,9 @@ func (s *Handlers) parseImageEncRequest(
|
|||||||
|
|
||||||
// Apply the same dimension and fit-mode bounds as the plain image
|
// Apply the same dimension and fit-mode bounds as the plain image
|
||||||
// route: a sealed payload is trusted for its origin, not for staying
|
// route: a sealed payload is trusted for its origin, not for staying
|
||||||
// within limits, so an over-limit size or unknown fit mode is a 400
|
// within limits. This refuses an over-limit size before the source is
|
||||||
// here rather than an out-of-memory or a 500 from the processor.
|
// fetched. An unknown fit mode is a 400 here rather than a 500 from the
|
||||||
|
// processor.
|
||||||
err = imagerequest.ValidateImageRequest(req)
|
err = imagerequest.ValidateImageRequest(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Debug("encrypted URL failed validation", "error", err)
|
s.log.Debug("encrypted URL failed validation", "error", err)
|
||||||
|
|||||||
Reference in new issue
Block a user