caddyfile: Expand imports inside named routes (#7986)

pull/8020/head^2
XiaoleC05 2026-09-16 21:09:57 +08:00 committed by GitHub
parent 894442fe86
commit c1645c544d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 323 additions and 2 deletions

View File

@ -173,8 +173,15 @@ func (p *parser) begin() error {
if err != nil {
return err
}
tokens = append([]Token{nameToken}, tokens...)
p.block.Segments = []Segment{tokens}
// expand any import directives inside the named route block
expandedTokens, err := p.expandImportsInBlock(tokens)
if err != nil {
return err
}
expandedTokens = append([]Token{nameToken}, expandedTokens...)
p.block.Segments = []Segment{expandedTokens}
return nil
}
@ -583,6 +590,41 @@ func (p *parser) doImport(nesting int) error {
return nil
}
// expandImportsInBlock takes a slice of tokens (typically the contents of a
// named route block including its outer curly braces) and expands any import
// directives found at the beginning of a line. The expansion is done by
// creating a temporary parser that shares the same snippets and import graph,
// then looping through the tokens and calling doImport whenever an import
// directive is encountered. All other tokens are left untouched.
func (p *parser) expandImportsInBlock(tokens []Token) ([]Token, error) {
// Create a temporary parser that operates on the provided tokens.
// The Dispenser is initialized with the token slice; snippets and the
// import graph are shared so that imports and cycle detection work
// consistently across the whole Caddyfile.
tempParser := &parser{
Dispenser: NewDispenser(tokens),
definedSnippets: p.definedSnippets,
importGraph: p.importGraph,
}
// Loop through the tokens. We only care about import directives that
// appear at the start of a line (same logic as directives()).
for tempParser.Next() {
if tempParser.Val() == "import" && tempParser.isNewLine() {
if err := tempParser.doImport(1); err != nil {
return nil, err
}
// Roll back the cursor so the next iteration sees the first
// token of the imported content (or the next token after it).
tempParser.cursor--
}
}
// The temporary parser's token slice has been modified in place by
// doImport, so we return it directly.
return tempParser.tokens, nil
}
// doSingleImport lexes the individual file at importFile and returns
// its tokens or an error, if any.
func (p *parser) doSingleImport(importFile string) ([]Token, error) {

View File

@ -0,0 +1,62 @@
(common) {
respond "from snippet"
}
&(foo) {
import common
}
localhost:9080 {
invoke foo
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":9080"
],
"routes": [
{
"match": [
{
"host": [
"localhost"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "invoke",
"name": "foo"
}
]
}
]
}
],
"terminal": true
}
],
"named_routes": {
"foo": {
"handle": [
{
"body": "from snippet",
"handler": "static_response"
}
]
}
}
}
}
}
}
}

View File

@ -0,0 +1,62 @@
(common) {
respond "{args.0}"
}
&(foo) {
import common "hello"
}
localhost:9080 {
invoke foo
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":9080"
],
"routes": [
{
"match": [
{
"host": [
"localhost"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "invoke",
"name": "foo"
}
]
}
]
}
],
"terminal": true
}
],
"named_routes": {
"foo": {
"handle": [
{
"body": "hello",
"handler": "static_response"
}
]
}
}
}
}
}
}
}

View File

@ -0,0 +1,89 @@
(common) {
route /api/* {
respond "api"
}
}
&(foo) {
import common
}
localhost:9080 {
invoke foo
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":9080"
],
"routes": [
{
"match": [
{
"host": [
"localhost"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "invoke",
"name": "foo"
}
]
}
]
}
],
"terminal": true
}
],
"named_routes": {
"foo": {
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "api",
"handler": "static_response"
}
]
}
]
}
],
"match": [
{
"path": [
"/api/*"
]
}
]
}
]
}
]
}
}
}
}
}
}
}

View File

@ -0,0 +1,66 @@
(inner) {
respond "inner"
}
(outer) {
import inner
}
&(foo) {
import outer
}
localhost:9080 {
invoke foo
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":9080"
],
"routes": [
{
"match": [
{
"host": [
"localhost"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "invoke",
"name": "foo"
}
]
}
]
}
],
"terminal": true
}
],
"named_routes": {
"foo": {
"handle": [
{
"body": "inner",
"handler": "static_response"
}
]
}
}
}
}
}
}
}