Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@
"opensource",
"opentype",
"Pacman",
"perr",
"picus",
"Pinia",
"pkce",
Expand Down Expand Up @@ -230,6 +231,7 @@
"unplugin",
"unsanitize",
"Upsert",
"upstack",
"urfave",
"usecase",
"useragent",
Expand All @@ -250,6 +252,7 @@
"woodpeckerci",
"WORKDIR",
"Wrapf",
"writedeadline",
"x-enum-varnames",
"xlink",
"xlog",
Expand Down
29 changes: 29 additions & 0 deletions cmd/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,33 @@ import (

const (
shutdownTimeout = time.Second * 5

readHeaderTimeout = 10 * time.Second
writeTimeout = 60 * time.Second
idleTimeout = 120 * time.Second
)

// setupServerTimeouts applies conservative timeouts to an http.Server so a
// response write blocked on a zero-window peer, or an abandoned keepalive
// connection, is reclaimed instead of orphaning the connection forever.
//
// ReadTimeout is intentionally left zero: request bodies here are small
// (webhooks, API JSON — agent log upload rides gRPC on :9000, not this
// server), so an unbounded body read is low exposure. The slow-loris variants
// are still bounded: a slow HEADER is capped by ReadHeaderTimeout, and a slow
// BODY is capped by WriteTimeout — net/http arms the write deadline once the
// headers are read, so a handler that has begun responding cannot be held open
// indefinitely by a trickled request body. A future body-streaming handler
// that re-arms its own write deadline (as the SSE handlers do) would escape
// that WriteTimeout bound and must add its own body/read deadline. SSE
// handlers override WriteTimeout per-response via http.ResponseController (see
// server/api/stream.go).
func setupServerTimeouts(s *http.Server) {
s.ReadHeaderTimeout = readHeaderTimeout
s.WriteTimeout = writeTimeout
s.IdleTimeout = idleTimeout
}

var (
stopServerFunc context.CancelCauseFunc = func(error) {}
shutdownCancelFunc context.CancelFunc = func() {}
Expand Down Expand Up @@ -192,6 +217,7 @@ func run(ctx context.Context, c *cli.Command) error {
NextProtos: []string{"h2", "http/1.1"},
},
}
setupServerTimeouts(tlsServer)

go func() {
<-ctx.Done()
Expand Down Expand Up @@ -231,6 +257,7 @@ func run(ctx context.Context, c *cli.Command) error {
Addr: server.Config.Server.Port,
Handler: http.HandlerFunc(redirect),
}
setupServerTimeouts(redirectServer)
go func() {
<-ctx.Done()
log.Info().Msg("shutdown redirect server ...")
Expand Down Expand Up @@ -278,6 +305,7 @@ func run(ctx context.Context, c *cli.Command) error {
httpServer := &http.Server{
Handler: handler,
}
setupServerTimeouts(httpServer)

go func() {
<-ctx.Done()
Expand Down Expand Up @@ -307,6 +335,7 @@ func run(ctx context.Context, c *cli.Command) error {
Addr: metricsServerAddr,
Handler: metricsRouter,
}
setupServerTimeouts(metricsServer)

go func() {
<-ctx.Done()
Expand Down
Loading
Loading