@marcel91200 / caddy Mirror · read-only / commits / b72bf7a1
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 04:45 UTC

caddyfile: keep an opening brace that ends the input (#8047)

* caddyfile: Keep an opening brace that ends the input Format writes an opening brace lazily: the '{' case records the pending brace and continues, and the brace is only emitted once a following regular character is processed. When the input ends while a brace is still pending, the read loop exits and nothing flushes it, so the brace is dropped. 'caddy fmt' on a Caddyfile whose last line has just opened a block therefore deletes the brace the user typed, and with --overwrite that result is written back over the file. Flush a pending brace after the loop ends. This also restores idempotence for inputs such as '{ {', which formatted to '{' and then to nothing. * caddyfile: Flush a pending brace before another one replaces it The '{' case overwrote the pending-brace state without emitting the brace already waiting, so adjacent braces lost one: Format("{{") gave just "{". Only the space-separated path flushed, and it did so with an inline block. Move that block into a flushOpenBrace closure and call it from the three places a pending brace has to be emitted: the existing space-separated path, the '{' case before it replaces the state, and the end of input. Using it at the end of input also indents a trailing brace the same way the other paths do.

Roland committed Oct 3, 2026 at 09:16 UTC b72bf7a1efa0cfbf3c97b66db457912dc0d889fd
2 files changed +58 -25
caddyconfig/caddyfile/formatter.go
+36 -25
@@ -106,6 +106,34 @@ func Format(input []byte) []byte {
106 beginningOfLine = true
107 }
108
109 + flushOpenBrace := func() {
110 + if nesting == 0 && last == '}' {
111 + nextLine()
112 + nextLine()
113 + }
114 +
115 + openBrace = false
116 + if openBraceOwnLine && previousLineWasTopLevelImport {
117 + if last != '\n' {
118 + nextLine()
119 + }
120 + indent()
121 + } else if beginningOfLine {
122 + indent()
123 + } else if !openBraceSpace || !unicode.IsSpace(last) {
124 + write(' ')
125 + }
126 + write('{')
127 + openBraceWritten = true
128 + openBraceOwnLine = false
129 + nextLine()
130 + newLines = 0
131 + // prevent infinite nesting from ridiculous inputs (issue #4169)
132 + if nesting < 10 {
133 + nesting++
134 + }
135 + }
136 +
137 for {
138 ch, _, err := rdr.ReadRune()
139 if err != nil {
@@ -270,36 +298,15 @@ func Format(input []byte) []byte {
298 }
299
300 if openBrace && spacePrior && !openBraceWritten {
273 - if nesting == 0 && last == '}' {
274 - nextLine()
275 - nextLine()
276 - }
277 -
278 - openBrace = false
279 - if openBraceOwnLine && previousLineWasTopLevelImport {
280 - if last != '\n' {
281 - nextLine()
282 - }
283 - indent()
284 - } else if beginningOfLine {
285 - indent()
286 - } else if !openBraceSpace || !unicode.IsSpace(last) {
287 - write(' ')
288 - }
289 - write('{')
290 - openBraceWritten = true
291 - openBraceOwnLine = false
292 - nextLine()
293 - newLines = 0
294 - // prevent infinite nesting from ridiculous inputs (issue #4169)
295 - if nesting < 10 {
296 - nesting++
297 - }
301 + flushOpenBrace()
302 }
303
304 switch {
305 case ch == '{':
306 finishToken()
307 + if openBrace && !openBraceWritten {
308 + flushOpenBrace()
309 + }
310 openBrace = true
311 openBraceSpace = spacePrior && !beginningOfLine
312 openBraceOwnLine = newLines > 0
@@ -367,6 +374,10 @@ func Format(input []byte) []byte {
374 beginningOfLine = false
375 }
376
377 + if openBrace && !openBraceWritten {
378 + flushOpenBrace()
379 + }
380 +
381 // the Caddyfile does not need any leading or trailing spaces, but...
382 trimmedResult := bytes.TrimSpace(out.Bytes())
383
caddyconfig/caddyfile/formatter_test.go
+22
@@ -495,6 +495,28 @@ import ./conf.d/matcher_not_my_subnet.caddy
495 order appsec after crowdsec
496 }`,
497 },
498 + {
499 + description: "keep an opening brace that ends the input",
500 + input: `localhost {`,
501 + expect: `localhost {`,
502 + },
503 + {
504 + description: "keep a standalone opening brace that ends the input",
505 + input: `{`,
506 + expect: `{`,
507 + },
508 + {
509 + description: "keep adjacent opening braces that end the input",
510 + input: `{{`,
511 + expect: `{
512 + {`,
513 + },
514 + {
515 + description: "keep spaced opening braces that end the input",
516 + input: `{ {`,
517 + expect: `{
518 + {`,
519 + },
520 } {
521 // the formatter should output a trailing newline,
522 // even if the tests aren't written to expect that