Say at the receiver route where its 1 MB body cap lives (closes #173)
check / check (push) Successful in 3m22s
check / check (push) Successful in 3m22s
The receiver route /h/{uuid} has no MaxBodySize middleware, unlike the page route groups; its 1 MB cap is enforced in the handler, which owns the response senders see. Nothing at the route said so, so a reader could take the receiver for uncapped or remove the only bound on the one unauthenticated endpoint. The route registration and the handler's body-reading function now say where the cap lives and why. A routing test sends a body of exactly 1 MB and one a byte over through the production router and pins that only the second is refused, with the handler's 413 and message.
Model: opus-5-5
This commit was merged in pull request #429.
This commit is contained in:
@@ -150,7 +150,9 @@ func (h *Handlers) lookupEntrypoint(
|
|||||||
return entrypoint, true
|
return entrypoint, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// readWebhookBody reads and validates the request body size.
|
// readWebhookBody reads and validates the request body size. This is
|
||||||
|
// the receiver's only body cap: /h/{uuid} has no MaxBodySize
|
||||||
|
// middleware (see Server.setupWebhookRoutes).
|
||||||
func (h *Handlers) readWebhookBody(
|
func (h *Handlers) readWebhookBody(
|
||||||
w http.ResponseWriter,
|
w http.ResponseWriter,
|
||||||
r *http.Request,
|
r *http.Request,
|
||||||
|
|||||||
@@ -324,6 +324,12 @@ func (s *Server) setupSourceRoutes() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) setupWebhookRoutes() {
|
func (s *Server) setupWebhookRoutes() {
|
||||||
|
// No MaxBodySize here, unlike the page groups. The receiver's 1 MB
|
||||||
|
// body cap is in Handlers.readWebhookBody, because the handler
|
||||||
|
// owns the response a sender gets for an oversized body and
|
||||||
|
// MaxBodySize would change it. That cap is the only bound on this
|
||||||
|
// unauthenticated endpoint's body; TestReceiver_OversizeBodyRefused
|
||||||
|
// pins it.
|
||||||
s.router.With(s.mw.ReceiverRateLimit()).HandleFunc(
|
s.router.With(s.mw.ReceiverRateLimit()).HandleFunc(
|
||||||
"/h/{uuid}",
|
"/h/{uuid}",
|
||||||
s.h.HandleWebhook(),
|
s.h.HandleWebhook(),
|
||||||
|
|||||||
@@ -1428,6 +1428,53 @@ func TestReceiver_EntrypointURLIsRateLimited(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestReceiver_OversizeBodyRefused pins the receiver's 1 MB body
|
||||||
|
// cap, which lives in the handler rather than in a MaxBodySize
|
||||||
|
// middleware. A body exactly at the cap is accepted; one byte over is
|
||||||
|
// refused with the handler's own 413.
|
||||||
|
func TestReceiver_OversizeBodyRefused(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
const bodyCap = 1 << 20 // 1 MB
|
||||||
|
|
||||||
|
env := newTestEnvWithConfig(t, &config.Config{
|
||||||
|
DataDir: t.TempDir(),
|
||||||
|
Environment: config.EnvironmentDev,
|
||||||
|
ReceiverRateLimit: 10,
|
||||||
|
})
|
||||||
|
|
||||||
|
userID, _ := env.seedUser(t, "receiver", "somepassword")
|
||||||
|
wh := env.seedWebhook(t, userID)
|
||||||
|
require.NoError(t, env.db.DB().Omit(clause.Associations).Create(
|
||||||
|
&database.Entrypoint{
|
||||||
|
WebhookID: wh.ID,
|
||||||
|
Path: "0b7c3e5a-2d9f-4a61-8e4b-7c1d6f2a9e35",
|
||||||
|
Active: true,
|
||||||
|
},
|
||||||
|
).Error)
|
||||||
|
|
||||||
|
send := func(size int) *httptest.ResponseRecorder {
|
||||||
|
req := httptest.NewRequestWithContext(
|
||||||
|
context.Background(), http.MethodPost,
|
||||||
|
"/h/0b7c3e5a-2d9f-4a61-8e4b-7c1d6f2a9e35",
|
||||||
|
strings.NewReader(strings.Repeat("a", size)),
|
||||||
|
)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
env.router.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(
|
||||||
|
t, http.StatusOK, send(bodyCap).Code,
|
||||||
|
"a body exactly at the cap must be accepted",
|
||||||
|
)
|
||||||
|
|
||||||
|
w := send(bodyCap + 1)
|
||||||
|
assert.Equal(t, http.StatusRequestEntityTooLarge, w.Code)
|
||||||
|
assert.Equal(t, "Request body too large\n", w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
// metricsConfig is a Config differing from the routing default only
|
// metricsConfig is a Config differing from the routing default only
|
||||||
// in the two /metrics credentials.
|
// in the two /metrics credentials.
|
||||||
func metricsConfig(
|
func metricsConfig(
|
||||||
|
|||||||
Reference in New Issue
Block a user