Skip to content

Commit 310f7df

Browse files
authored
Merge branch 'master' into timberjack-rotate-at
2 parents f228474 + 65e0ddc commit 310f7df

34 files changed

+497
-103
lines changed

admin.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,6 +1029,13 @@ func handleConfig(w http.ResponseWriter, r *http.Request) error {
10291029
return err
10301030
}
10311031

1032+
// If this request changed the config, clear the last
1033+
// config info we have stored, if it is different from
1034+
// the original source.
1035+
ClearLastConfigIfDifferent(
1036+
r.Header.Get("Caddy-Config-Source-File"),
1037+
r.Header.Get("Caddy-Config-Source-Adapter"))
1038+
10321039
default:
10331040
return APIError{
10341041
HTTPStatus: http.StatusMethodNotAllowed,

admin_test.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,9 @@ func TestLoadConcurrent(t *testing.T) {
149149
var wg sync.WaitGroup
150150

151151
for i := 0; i < 100; i++ {
152-
wg.Add(1)
153-
go func() {
152+
wg.Go(func() {
154153
_ = Load(testCfg, true)
155-
wg.Done()
156-
}()
154+
})
157155
}
158156
wg.Wait()
159157
}

caddy.go

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -975,11 +975,11 @@ func Version() (simple, full string) {
975975
if CustomVersion != "" {
976976
full = CustomVersion
977977
simple = CustomVersion
978-
return
978+
return simple, full
979979
}
980980
full = "unknown"
981981
simple = "unknown"
982-
return
982+
return simple, full
983983
}
984984
// find the Caddy module in the dependency list
985985
for _, dep := range bi.Deps {
@@ -1059,7 +1059,7 @@ func Version() (simple, full string) {
10591059
}
10601060
}
10611061

1062-
return
1062+
return simple, full
10631063
}
10641064

10651065
// Event represents something that has happened or is happening.
@@ -1197,6 +1197,91 @@ var (
11971197
rawCfgMu sync.RWMutex
11981198
)
11991199

1200+
// lastConfigFile and lastConfigAdapter remember the source config
1201+
// file and adapter used when Caddy was started via the CLI "run" command.
1202+
// These are consulted by the SIGUSR1 handler to attempt reloading from
1203+
// the same source. They are intentionally not set for other entrypoints
1204+
// such as "caddy start" or subcommands like file-server.
1205+
var (
1206+
lastConfigMu sync.RWMutex
1207+
lastConfigFile string
1208+
lastConfigAdapter string
1209+
)
1210+
1211+
// reloadFromSourceFunc is the type of stored callback
1212+
// which is called when we receive a SIGUSR1 signal.
1213+
type reloadFromSourceFunc func(file, adapter string) error
1214+
1215+
// reloadFromSourceCallback is the stored callback
1216+
// which is called when we receive a SIGUSR1 signal.
1217+
var reloadFromSourceCallback reloadFromSourceFunc
1218+
1219+
// errReloadFromSourceUnavailable is returned when no reload-from-source callback is set.
1220+
var errReloadFromSourceUnavailable = errors.New("reload from source unavailable in this process") //nolint:unused
1221+
1222+
// SetLastConfig records the given source file and adapter as the
1223+
// last-known external configuration source. Intended to be called
1224+
// only when starting via "caddy run --config <file> --adapter <adapter>".
1225+
func SetLastConfig(file, adapter string, fn reloadFromSourceFunc) {
1226+
lastConfigMu.Lock()
1227+
lastConfigFile = file
1228+
lastConfigAdapter = adapter
1229+
reloadFromSourceCallback = fn
1230+
lastConfigMu.Unlock()
1231+
}
1232+
1233+
// ClearLastConfigIfDifferent clears the recorded last-config if the provided
1234+
// source file/adapter do not match the recorded last-config. If both srcFile
1235+
// and srcAdapter are empty, the last-config is cleared.
1236+
func ClearLastConfigIfDifferent(srcFile, srcAdapter string) {
1237+
if (srcFile != "" || srcAdapter != "") && lastConfigMatches(srcFile, srcAdapter) {
1238+
return
1239+
}
1240+
SetLastConfig("", "", nil)
1241+
}
1242+
1243+
// getLastConfig returns the last-known config file and adapter.
1244+
func getLastConfig() (file, adapter string, fn reloadFromSourceFunc) {
1245+
lastConfigMu.RLock()
1246+
f, a, cb := lastConfigFile, lastConfigAdapter, reloadFromSourceCallback
1247+
lastConfigMu.RUnlock()
1248+
return f, a, cb
1249+
}
1250+
1251+
// lastConfigMatches returns true if the provided source file and/or adapter
1252+
// matches the recorded last-config. Matching rules (in priority order):
1253+
// 1. If srcAdapter is provided and differs from the recorded adapter, no match.
1254+
// 2. If srcFile exactly equals the recorded file, match.
1255+
// 3. If both sides can be made absolute and equal, match.
1256+
// 4. If basenames are equal, match.
1257+
func lastConfigMatches(srcFile, srcAdapter string) bool {
1258+
lf, la, _ := getLastConfig()
1259+
1260+
// If adapter is provided, it must match.
1261+
if srcAdapter != "" && srcAdapter != la {
1262+
return false
1263+
}
1264+
1265+
// Quick equality check.
1266+
if srcFile == lf {
1267+
return true
1268+
}
1269+
1270+
// Try absolute path comparison.
1271+
sAbs, sErr := filepath.Abs(srcFile)
1272+
lAbs, lErr := filepath.Abs(lf)
1273+
if sErr == nil && lErr == nil && sAbs == lAbs {
1274+
return true
1275+
}
1276+
1277+
// Final fallback: basename equality.
1278+
if filepath.Base(srcFile) == filepath.Base(lf) {
1279+
return true
1280+
}
1281+
1282+
return false
1283+
}
1284+
12001285
// errSameConfig is returned if the new config is the same
12011286
// as the old one. This isn't usually an actual, actionable
12021287
// error; it's mostly a sentinel value.

caddyconfig/caddyfile/parse.go

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -533,29 +533,24 @@ func (p *parser) doImport(nesting int) error {
533533
}
534534
// if it is {block}, we substitute with all tokens in the block
535535
// if it is {blocks.*}, we substitute with the tokens in the mapping for the *
536-
var skip bool
537536
var tokensToAdd []Token
537+
foundBlockDirective := false
538538
switch {
539539
case token.Text == "{block}":
540+
foundBlockDirective = true
540541
tokensToAdd = blockTokens
541542
case strings.HasPrefix(token.Text, "{blocks.") && strings.HasSuffix(token.Text, "}"):
543+
foundBlockDirective = true
542544
// {blocks.foo.bar} will be extracted to key `foo.bar`
543545
blockKey := strings.TrimPrefix(strings.TrimSuffix(token.Text, "}"), "{blocks.")
544546
val, ok := blockMapping[blockKey]
545547
if ok {
546548
tokensToAdd = val
547549
}
548-
default:
549-
skip = true
550550
}
551-
if !skip {
552-
if len(tokensToAdd) == 0 {
553-
// if there is no content in the snippet block, don't do any replacement
554-
// this allows snippets which contained {block}/{block.*} before this change to continue functioning as normal
555-
tokensCopy = append(tokensCopy, token)
556-
} else {
557-
tokensCopy = append(tokensCopy, tokensToAdd...)
558-
}
551+
552+
if foundBlockDirective {
553+
tokensCopy = append(tokensCopy, tokensToAdd...)
559554
continue
560555
}
561556

caddyconfig/load.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,13 @@ func (adminLoad) handleLoad(w http.ResponseWriter, r *http.Request) error {
121121
}
122122
}
123123

124+
// If this request changed the config, clear the last
125+
// config info we have stored, if it is different from
126+
// the original source.
127+
caddy.ClearLastConfigIfDifferent(
128+
r.Header.Get("Caddy-Config-Source-File"),
129+
r.Header.Get("Caddy-Config-Source-Adapter"))
130+
124131
caddy.Log().Named("admin.api").Info("load complete")
125132

126133
return nil
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
(snippet) {
2+
header {
3+
reverse_proxy localhost:3000
4+
{block}
5+
}
6+
}
7+
8+
example.com {
9+
import snippet
10+
}
11+
----------
12+
{
13+
"apps": {
14+
"http": {
15+
"servers": {
16+
"srv0": {
17+
"listen": [
18+
":443"
19+
],
20+
"routes": [
21+
{
22+
"match": [
23+
{
24+
"host": [
25+
"example.com"
26+
]
27+
}
28+
],
29+
"handle": [
30+
{
31+
"handler": "subroute",
32+
"routes": [
33+
{
34+
"handle": [
35+
{
36+
"handler": "headers",
37+
"response": {
38+
"set": {
39+
"Reverse_proxy": [
40+
"localhost:3000"
41+
]
42+
}
43+
}
44+
}
45+
]
46+
}
47+
]
48+
}
49+
],
50+
"terminal": true
51+
}
52+
]
53+
}
54+
}
55+
}
56+
}
57+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
(snippet) {
2+
header {
3+
reverse_proxy localhost:3000
4+
{blocks.content_type}
5+
}
6+
}
7+
8+
example.com {
9+
import snippet
10+
}
11+
----------
12+
{
13+
"apps": {
14+
"http": {
15+
"servers": {
16+
"srv0": {
17+
"listen": [
18+
":443"
19+
],
20+
"routes": [
21+
{
22+
"match": [
23+
{
24+
"host": [
25+
"example.com"
26+
]
27+
}
28+
],
29+
"handle": [
30+
{
31+
"handler": "subroute",
32+
"routes": [
33+
{
34+
"handle": [
35+
{
36+
"handler": "headers",
37+
"response": {
38+
"set": {
39+
"Reverse_proxy": [
40+
"localhost:3000"
41+
]
42+
}
43+
}
44+
}
45+
]
46+
}
47+
]
48+
}
49+
],
50+
"terminal": true
51+
}
52+
]
53+
}
54+
}
55+
}
56+
}
57+
}

0 commit comments

Comments
 (0)