caddyhttp: Add server options keepalive_idle and keepalive_count (#7298)

* Add Server options KeepAliveIdle (keepalive_idle) and KeepAliveCount (keepalive_count)

Signed-off-by: Joshua McBeth <joshua.mcbeth@gmail.com>

* Add Server option KeepAliveDisable (keepalive_disable)

Signed-off-by: Joshua McBeth <joshua.mcbeth@gmail.com>

* Remove Server option KeepAliveDisable (keepalive_disable), disable when interval is negative

Signed-off-by: Joshua McBeth <joshua.mcbeth@gmail.com>

* Add keepalive parameters to caddyfiletest

Signed-off-by: Joshua McBeth <joshua.mcbeth@gmail.com>

---------

Signed-off-by: Joshua McBeth <joshua.mcbeth@gmail.com>
This commit is contained in:
joshuamcbeth 2025-10-14 14:03:23 -04:00 committed by GitHub
parent 2ec28bca43
commit de6b78009b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 52 additions and 2 deletions

View File

@ -18,6 +18,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"slices" "slices"
"strconv"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
@ -42,6 +43,8 @@ type serverOptions struct {
WriteTimeout caddy.Duration WriteTimeout caddy.Duration
IdleTimeout caddy.Duration IdleTimeout caddy.Duration
KeepAliveInterval caddy.Duration KeepAliveInterval caddy.Duration
KeepAliveIdle caddy.Duration
KeepAliveCount int
MaxHeaderBytes int MaxHeaderBytes int
EnableFullDuplex bool EnableFullDuplex bool
Protocols []string Protocols []string
@ -142,6 +145,7 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
return nil, d.Errf("unrecognized timeouts option '%s'", d.Val()) return nil, d.Errf("unrecognized timeouts option '%s'", d.Val())
} }
} }
case "keepalive_interval": case "keepalive_interval":
if !d.NextArg() { if !d.NextArg() {
return nil, d.ArgErr() return nil, d.ArgErr()
@ -152,6 +156,26 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
} }
serverOpts.KeepAliveInterval = caddy.Duration(dur) serverOpts.KeepAliveInterval = caddy.Duration(dur)
case "keepalive_idle":
if !d.NextArg() {
return nil, d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return nil, d.Errf("parsing keepalive idle duration: %v", err)
}
serverOpts.KeepAliveIdle = caddy.Duration(dur)
case "keepalive_count":
if !d.NextArg() {
return nil, d.ArgErr()
}
cnt, err := strconv.ParseInt(d.Val(), 10, 32)
if err != nil {
return nil, d.Errf("parsing keepalive count int: %v", err)
}
serverOpts.KeepAliveCount = int(cnt)
case "max_header_size": case "max_header_size":
var sizeStr string var sizeStr string
if !d.AllArgs(&sizeStr) { if !d.AllArgs(&sizeStr) {
@ -309,6 +333,8 @@ func applyServerOptions(
server.WriteTimeout = opts.WriteTimeout server.WriteTimeout = opts.WriteTimeout
server.IdleTimeout = opts.IdleTimeout server.IdleTimeout = opts.IdleTimeout
server.KeepAliveInterval = opts.KeepAliveInterval server.KeepAliveInterval = opts.KeepAliveInterval
server.KeepAliveIdle = opts.KeepAliveIdle
server.KeepAliveCount = opts.KeepAliveCount
server.MaxHeaderBytes = opts.MaxHeaderBytes server.MaxHeaderBytes = opts.MaxHeaderBytes
server.EnableFullDuplex = opts.EnableFullDuplex server.EnableFullDuplex = opts.EnableFullDuplex
server.Protocols = opts.Protocols server.Protocols = opts.Protocols

View File

@ -18,6 +18,9 @@
trusted_proxies static private_ranges trusted_proxies static private_ranges
client_ip_headers Custom-Real-Client-IP X-Forwarded-For client_ip_headers Custom-Real-Client-IP X-Forwarded-For
client_ip_headers A-Third-One client_ip_headers A-Third-One
keepalive_interval 20s
keepalive_idle 20s
keepalive_count 10
} }
} }
@ -45,6 +48,9 @@ foo.com {
"read_header_timeout": 30000000000, "read_header_timeout": 30000000000,
"write_timeout": 30000000000, "write_timeout": 30000000000,
"idle_timeout": 30000000000, "idle_timeout": 30000000000,
"keepalive_interval": 20000000000,
"keepalive_idle": 20000000000,
"keepalive_count": 10,
"max_header_bytes": 100000000, "max_header_bytes": 100000000,
"enable_full_duplex": true, "enable_full_duplex": true,
"routes": [ "routes": [

View File

@ -538,6 +538,8 @@ func (app *App) Start() error {
KeepAliveConfig: net.KeepAliveConfig{ KeepAliveConfig: net.KeepAliveConfig{
Enable: srv.KeepAliveInterval >= 0, Enable: srv.KeepAliveInterval >= 0,
Interval: time.Duration(srv.KeepAliveInterval), Interval: time.Duration(srv.KeepAliveInterval),
Idle: time.Duration(srv.KeepAliveIdle),
Count: srv.KeepAliveCount,
}, },
}) })
if err != nil { if err != nil {

View File

@ -76,9 +76,25 @@ type Server struct {
// KeepAliveInterval is the interval at which TCP keepalive packets // KeepAliveInterval is the interval at which TCP keepalive packets
// are sent to keep the connection alive at the TCP layer when no other // are sent to keep the connection alive at the TCP layer when no other
// data is being transmitted. The default is 15s. // data is being transmitted.
// If zero, the default is 15s.
// If negative, keepalive packets are not sent and other keepalive parameters
// are ignored.
KeepAliveInterval caddy.Duration `json:"keepalive_interval,omitempty"` KeepAliveInterval caddy.Duration `json:"keepalive_interval,omitempty"`
// KeepAliveIdle is the time that the connection must be idle before
// the first TCP keep-alive probe is sent when no other data is being
// transmitted.
// If zero, the default is 15s.
// If negative, underlying socket value is unchanged.
KeepAliveIdle caddy.Duration `json:"keepalive_idle,omitempty"`
// KeepAliveCount is the maximum number of TCP keep-alive probes that
// should be sent before dropping a connection.
// If zero, the default is 9.
// If negative, underlying socket value is unchanged.
KeepAliveCount int `json:"keepalive_count,omitempty"`
// MaxHeaderBytes is the maximum size to parse from a client's // MaxHeaderBytes is the maximum size to parse from a client's
// HTTP request headers. // HTTP request headers.
MaxHeaderBytes int `json:"max_header_bytes,omitempty"` MaxHeaderBytes int `json:"max_header_bytes,omitempty"`