Skip to content

Commit fbe3b2b

Browse files
Use chi/middleware.Throttle instead of custom conn limiter (#4402)
Use chi/middleware.Throttle to track in-flight requests and return a 429 response once the limit is reached. This is different behaviour from the TCP listener based approach where the check occurs before the TLS handshake and if the limit is reached, the connection is silently closed.
1 parent 33cbb96 commit fbe3b2b

File tree

5 files changed

+42
-127
lines changed

5 files changed

+42
-127
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Kind can be one of:
2+
# - breaking-change: a change to previously-documented behavior
3+
# - deprecation: functionality that is being removed in a later release
4+
# - bug-fix: fixes a problem in a previous version
5+
# - enhancement: extends functionality but does not break or fix existing behavior
6+
# - feature: new functionality
7+
# - known-issue: problems that we are aware of in a given version
8+
# - security: impacts on the security of a product or a user’s deployment.
9+
# - upgrade: important information for someone upgrading from a prior version
10+
# - other: does not fit into any of the other categories
11+
kind: bug-fix
12+
13+
# Change summary; a 80ish characters long description of the change.
14+
summary: Return HTTP 429 when conn limit is reached
15+
16+
# Long description; in case the summary is not enough to describe the change
17+
# this field accommodate a description without length limits.
18+
# NOTE: This field will be rendered only for breaking-change and known-issue kinds at the moment.
19+
description: |
20+
fleet-server will return a 429 instead of silently close connections when the conn limit is reached.
21+
22+
# Affected component; usually one of "elastic-agent", "fleet-server", "filebeat", "metricbeat", "auditbeat", "all", etc.
23+
component: fleet-server
24+
25+
# PR URL; optional; the PR number that added the changeset.
26+
# If not present is automatically filled by the tooling finding the PR where this changelog fragment has been added.
27+
# NOTE: the tooling supports backports, so it's able to fill the original PR number instead of the backport PR number.
28+
# Please provide it if you are adding a fragment for a different PR.
29+
#pr: https://github.com/owner/repo/1234
30+
31+
# Issue URL; optional; the GitHub issue related to this changeset (either closes or is part of).
32+
# If not present is automatically filled by the tooling with the issue linked to the PR number.
33+
issue: https://github.com/elastic/fleet-server/issues/4200

fleet-server.reference.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ fleet:
146146
# max_agents: 0
147147
# # max_header_byte_size is the request header size limit
148148
# max_header_byte_size: 8192 # 8Kib
149-
# # max_connections is the maximum number of connnections per API endpoint
149+
# # max_connections is the maximum number of in-flight HTTP requests per API listener
150150
# max_connections: 0
151151
#
152152
# # action_limit is a limiter for the action dispatcher, it is added to control how fast the checkin endpoint writes responses when an action effecting multiple agents is detected.

internal/pkg/api/router.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ import (
99
"regexp"
1010
"strings"
1111

12-
"github.com/elastic/fleet-server/v7/internal/pkg/config"
13-
"github.com/elastic/fleet-server/v7/internal/pkg/limit"
14-
"github.com/elastic/fleet-server/v7/internal/pkg/logger"
1512
"github.com/go-chi/chi/v5"
1613
"github.com/go-chi/chi/v5/middleware"
1714
"github.com/rs/zerolog"
1815
"go.elastic.co/apm/module/apmchiv5/v2"
1916
"go.elastic.co/apm/v2"
17+
18+
"github.com/elastic/fleet-server/v7/internal/pkg/config"
19+
"github.com/elastic/fleet-server/v7/internal/pkg/limit"
20+
"github.com/elastic/fleet-server/v7/internal/pkg/logger"
2021
)
2122

2223
func newRouter(cfg *config.ServerLimits, si ServerInterface, tracer *apm.Tracer) http.Handler {
@@ -26,6 +27,9 @@ func newRouter(cfg *config.ServerLimits, si ServerInterface, tracer *apm.Tracer)
2627
}
2728
r.Use(logger.Middleware) // Attach middlewares to router directly so the occur before any request parsing/validation
2829
r.Use(middleware.Recoverer)
30+
if cfg.MaxConnections > 0 {
31+
r.Use(middleware.Throttle(cfg.MaxConnections))
32+
}
2933
r.Use(Limiter(cfg).middleware)
3034
return HandlerWithOptions(si, ChiServerOptions{
3135
BaseRouter: r,

internal/pkg/api/server.go

+1-25
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515

1616
"github.com/elastic/elastic-agent-libs/transport/tlscommon"
1717
"github.com/elastic/fleet-server/v7/internal/pkg/config"
18-
"github.com/elastic/fleet-server/v7/internal/pkg/limit"
1918
"github.com/elastic/fleet-server/v7/internal/pkg/logger"
2019

2120
"github.com/rs/zerolog"
@@ -29,7 +28,7 @@ type server struct {
2928

3029
// NewServer creates a new HTTP api for the passed addr.
3130
//
32-
// The server has a listener specific conn limit and endpoint specific rate-limits.
31+
// The server has an http request limit and endpoint specific rate-limits.
3332
// The underlying API structs (such as *CheckinT) may be shared between servers.
3433
func NewServer(addr string, cfg *config.Server, opts ...APIOpt) *server {
3534
a := &apiServer{}
@@ -76,13 +75,6 @@ func (s *server) Run(ctx context.Context) error {
7675
}
7776
}()
7877

79-
// Conn Limiter must be before the TLS handshake in the stack;
80-
// The server should not eat the cost of the handshake if there
81-
// is no capacity to service the connection.
82-
// Also, it appears the HTTP2 implementation depends on the tls.Listener
83-
// being at the top of the stack.
84-
ln = wrapConnLimitter(ctx, ln, s.cfg)
85-
8678
if s.cfg.TLS != nil && s.cfg.TLS.IsEnabled() {
8779
commonTLSCfg, err := tlscommon.LoadTLSServerConfig(s.cfg.TLS)
8880
if err != nil {
@@ -151,22 +143,6 @@ func getDiagConnFunc(ctx context.Context) func(c net.Conn, s http.ConnState) {
151143
}
152144
}
153145

154-
func wrapConnLimitter(ctx context.Context, ln net.Listener, cfg *config.Server) net.Listener {
155-
hardLimit := cfg.Limits.MaxConnections
156-
157-
if hardLimit != 0 {
158-
zerolog.Ctx(ctx).Info().
159-
Int("hardConnLimit", hardLimit).
160-
Msg("server hard connection limiter installed")
161-
162-
ln = limit.Listener(ln, hardLimit, zerolog.Ctx(ctx))
163-
} else {
164-
zerolog.Ctx(ctx).Info().Msg("server hard connection limiter disabled")
165-
}
166-
167-
return ln
168-
}
169-
170146
type stubLogger struct {
171147
log zerolog.Logger
172148
}

internal/pkg/limit/listener.go

-98
This file was deleted.

0 commit comments

Comments
 (0)