Engine.Run starts an HTTP server without default timeouts
Description
Summary
Engine.Run starts an http.Server without read, write, idle, or header timeouts.
This leaves applications that use the default Run helper exposed to slow-client resource exhaustion unless they manually create and configure their own http.Server.
Affected Code
File: gin.go
server := &http.Server{ // #nosec G112
Addr: address,
Handler: engine.Handler(),
}
err = server.ListenAndServe()
ReadTimeout, WriteTimeout, ReadHeaderTimeout, and IdleTimeout are all left at their zero values.
Reproduction
I reproduced this with a minimal Gin server started through the default run path.
A TCP client sent only a partial HTTP request header, waited 20 seconds, and then completed the request:
GET /ping HTTP/1.1
Host: localhost
After the 20 second pause, the connection was still alive and the server returned a normal response once the request was completed:
HTTP/1.1 200 OK
Content-Type:
This shows that no ReadHeaderTimeout was applied by default.
Expected Behavior
The default server startup helper should either apply safe timeout defaults or strongly guide users toward a timeout-configured server for production use.
Actual Behavior
Engine.Run starts an HTTP server with no default timeouts.
Impact
Slow clients can keep connections open by delaying request headers. With enough concurrent slow connections, this can consume server connection resources and degrade availability.
Suggested Fix
- Consider setting a conservative default
ReadHeaderTimeoutin theRunhelper. - Alternatively, add a production-safe helper or prominent documentation warning that
Engine.Rundoes not configure timeouts. - Recommend using an explicitly configured
http.Serverfor production deployments.
Gin Version
Local gin-gonic/gin checkout at commit 34dac20
Can you reproduce the bug?
Yes
Source Code
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.String(http.StatusOK, "pong")
})
if err := r.Run(":8080"); err != nil {
panic(err)
}
}
Run the server:
go run .
In another shell, open a TCP connection and send only a partial HTTP request header:
exec 3<>/dev/tcp/localhost/8080
printf 'GET /ping HTTP/1.1\r\nHost: localhost\r\n' >&3
sleep 20
printf '\r\n' >&3
timeout 3 dd bs=1 count=80 <&3 2>/dev/null
Observed behavior:
HTTP/1.1 200 OK
Content-Type:
The connection remains open after 20 seconds and the server responds normally once the request header is completed, showing that ReadHeaderTimeout is not applied by default.
Go Version
Go 1.26.5 in Docker golang:1.26
Operating System
Linux, inside Docker golang:1.26 container
Source: gin-gonic/gin