Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 14, 05:15 UTC
admin: Sync server variables (fix #4260) (#4274)
* Synchronize server assignment/references to avoid data race * only hold lock during var reassignment
Steven Angles committed
Aug 16, 2021 at 17:04 UTC
a10910f3981908424493c043d26dfcb4e5f8dc25
2 files changed
+49
-20
admin.go
+13
-2
@@ -335,6 +335,7 @@ func replaceLocalAdminServer(cfg *Config) error {
335
return err
336
}
337
338
+ serverMu.Lock()
339
localAdminServer = &http.Server{
340
Addr: addr.String(), // for logging purposes only
341
Handler: handler,
@@ -343,10 +344,14 @@ func replaceLocalAdminServer(cfg *Config) error {
344
IdleTimeout: 60 * time.Second,
345
MaxHeaderBytes: 1024 * 64,
346
}
347
+ serverMu.Unlock()
348
349
adminLogger := Log().Named("admin")
350
go func() {
349
- if err := localAdminServer.Serve(ln); !errors.Is(err, http.ErrServerClosed) {
351
+ serverMu.Lock()
352
+ server := localAdminServer
353
+ serverMu.Unlock()
354
+ if err := server.Serve(ln); !errors.Is(err, http.ErrServerClosed) {
355
adminLogger.Error("admin server shutdown for unknown reason", zap.Error(err))
356
}
357
}()
@@ -474,6 +479,7 @@ func replaceRemoteAdminServer(ctx Context, cfg *Config) error {
479
return err
480
}
481
482
+ serverMu.Lock()
483
// create secure HTTP server
484
remoteAdminServer = &http.Server{
485
Addr: addr.String(), // for logging purposes only
@@ -485,6 +491,7 @@ func replaceRemoteAdminServer(ctx Context, cfg *Config) error {
491
MaxHeaderBytes: 1024 * 64,
492
ErrorLog: serverLogger,
493
}
494
+ serverMu.Unlock()
495
496
// start listener
497
ln, err := Listen(addr.Network, addr.JoinHostPort(0))
@@ -494,7 +501,10 @@ func replaceRemoteAdminServer(ctx Context, cfg *Config) error {
501
ln = tls.NewListener(ln, tlsConfig)
502
503
go func() {
497
- if err := remoteAdminServer.Serve(ln); !errors.Is(err, http.ErrServerClosed) {
504
+ serverMu.Lock()
505
+ server := remoteAdminServer
506
+ serverMu.Unlock()
507
+ if err := server.Serve(ln); !errors.Is(err, http.ErrServerClosed) {
508
remoteLogger.Error("admin remote server shutdown for unknown reason", zap.Error(err))
509
}
510
}()
@@ -1229,6 +1239,7 @@ var bufPool = sync.Pool{
1239
1240
// keep a reference to admin endpoint singletons while they're active
1241
var (
1242
+ serverMu sync.Mutex
1243
localAdminServer, remoteAdminServer *http.Server
1244
identityCertCache *certmagic.Cache
1245
)
admin_test.go
+36
-18
@@ -17,9 +17,28 @@ package caddy
17
import (
18
"encoding/json"
19
"reflect"
20
+ "sync"
21
"testing"
22
)
23
24
+var testCfg = []byte(`{
25
+ "apps": {
26
+ "http": {
27
+ "servers": {
28
+ "myserver": {
29
+ "listen": ["tcp/localhost:8080-8084"],
30
+ "read_timeout": "30s"
31
+ },
32
+ "yourserver": {
33
+ "listen": ["127.0.0.1:5000"],
34
+ "read_header_timeout": "15s"
35
+ }
36
+ }
37
+ }
38
+ }
39
+ }
40
+ `)
41
+
42
func TestUnsyncedConfigAccess(t *testing.T) {
43
// each test is performed in sequence, so
44
// each change builds on the previous ones;
@@ -108,25 +127,24 @@ func TestUnsyncedConfigAccess(t *testing.T) {
127
}
128
}
129
130
+// TestLoadConcurrent exercises Load under concurrent conditions
131
+// and is most useful under test with `-race` enabled.
132
+func TestLoadConcurrent(t *testing.T) {
133
+ var wg sync.WaitGroup
134
+
135
+ for i := 0; i < 100; i++ {
136
+ wg.Add(1)
137
+ go func() {
138
+ _ = Load(testCfg, true)
139
+ wg.Done()
140
+ }()
141
+ }
142
+
143
+ wg.Wait()
144
+}
145
+
146
func BenchmarkLoad(b *testing.B) {
147
for i := 0; i < b.N; i++ {
113
- cfg := []byte(`{
114
- "apps": {
115
- "http": {
116
- "servers": {
117
- "myserver": {
118
- "listen": ["tcp/localhost:8080-8084"],
119
- "read_timeout": "30s"
120
- },
121
- "yourserver": {
122
- "listen": ["127.0.0.1:5000"],
123
- "read_header_timeout": "15s"
124
- }
125
- }
126
- }
127
- }
128
- }
129
- `)
130
- Load(cfg, true)
148
+ Load(testCfg, true)
149
}
150
}