Return and pass on request IDs, and give /v1/e/ ETag, 304 and HEAD (closes #84)
check / check (push) Waiting to run
check / check (push) Waiting to run
Every response carries X-Request-Id, the upstream fetch sends it, and the "upstream fetched", "image converted" and "image served" lines log it as request_id. pixa's own RequestID middleware keeps a request's own ID only when it is at most 64 letters, digits, '-', '_' or '.', and otherwise makes a random one with crypto/rand, so nothing a client chooses freely and nothing about the host reaches upstream. /v1/e/ now sets ETag, answers a matching If-None-Match with 304 and is routed for HEAD, through notModified, which both image handlers call. No Vary is added: go-chi/cors already sends Vary: Origin. Model: opus-5-5
This commit was merged in pull request #179.
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// requestIDHeader is the header that carries a request's ID.
|
||||
const requestIDHeader = "X-Request-Id"
|
||||
|
||||
// TestResponsesCarryRequestID verifies that every response, whatever its route
|
||||
// and status, carries the request's ID as X-Request-Id, so a client can quote
|
||||
// it when reporting a problem: one pixa made up when the request brought none,
|
||||
// and the request's own X-Request-Id when it brought one.
|
||||
func TestResponsesCarryRequestID(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const clientRequestID = "client-request-id"
|
||||
|
||||
s := newTestServer(t)
|
||||
|
||||
paths := []string{
|
||||
"/robots.txt",
|
||||
"/no-such-path",
|
||||
unsignedImagePath,
|
||||
encryptedImagePath,
|
||||
}
|
||||
|
||||
for _, path := range paths {
|
||||
t.Run(path, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
s.ServeHTTP(rec, httptest.NewRequestWithContext(
|
||||
t.Context(), http.MethodGet, path, nil))
|
||||
t.Logf("status %d, %s %q",
|
||||
rec.Code, requestIDHeader, rec.Header().Get(requestIDHeader))
|
||||
|
||||
if rec.Header().Get(requestIDHeader) == "" {
|
||||
t.Errorf("response has no %s", requestIDHeader)
|
||||
}
|
||||
|
||||
req := httptest.NewRequestWithContext(
|
||||
t.Context(), http.MethodGet, path, nil)
|
||||
req.Header.Set(requestIDHeader, clientRequestID)
|
||||
|
||||
rec = httptest.NewRecorder()
|
||||
s.ServeHTTP(rec, req)
|
||||
|
||||
got := rec.Header().Get(requestIDHeader)
|
||||
if got != clientRequestID {
|
||||
t.Errorf("%s = %q, want the request's own %q",
|
||||
requestIDHeader, got, clientRequestID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ func (s *Server) SetupRoutes() {
|
||||
s.router = chi.NewRouter()
|
||||
|
||||
s.router.Use(middleware.Recoverer)
|
||||
s.router.Use(middleware.RequestID)
|
||||
s.router.Use(s.mw.RequestID())
|
||||
s.router.Use(s.mw.ClientIP())
|
||||
s.router.Use(s.mw.SecurityHeaders())
|
||||
s.router.Use(s.mw.Logging())
|
||||
@@ -97,6 +97,7 @@ func (s *Server) SetupRoutes() {
|
||||
// The trailing filename (e.g., /img.jpg) is ignored but helps
|
||||
// browsers with content type
|
||||
r.Get("/e/{token}/*", s.h.HandleImageEnc())
|
||||
r.Head("/e/{token}/*", s.h.HandleImageEnc())
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestEncryptedImageRouteAnswersHEAD verifies that HEAD on the encrypted image
|
||||
// route reaches its handler, as GET does, instead of being answered 405 Method
|
||||
// Not Allowed. The handler refuses a token it cannot decrypt with 400, so that
|
||||
// status shows the request got through.
|
||||
func TestEncryptedImageRouteAnswersHEAD(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s := newTestServer(t)
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
s.ServeHTTP(rec, httptest.NewRequestWithContext(
|
||||
t.Context(), http.MethodHead, encryptedImagePath, nil))
|
||||
t.Logf("status %d", rec.Code)
|
||||
|
||||
if rec.Code != http.StatusBadRequest {
|
||||
t.Errorf("status = %d, want %d from the encrypted image handler",
|
||||
rec.Code, http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user