@marcel91200 / caddy Mirror · read-only
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, 01:00 UTC

master
go 145 lines 4.74 KB
Raw
1 // Copyright 2015 Matthew Holt and The Caddy Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package caddyfile
16
17 import (
18 "bytes"
19 "encoding/json"
20 "fmt"
21
22 "github.com/caddyserver/caddy/v2"
23 "github.com/caddyserver/caddy/v2/caddyconfig"
24 )
25
26 // Adapter adapts Caddyfile to Caddy JSON.
27 type Adapter struct {
28 ServerType ServerType
29 }
30
31 // Adapt converts the Caddyfile config in body to Caddy JSON.
32 func (a Adapter) Adapt(body []byte, options map[string]any) ([]byte, []caddyconfig.Warning, error) {
33 if a.ServerType == nil {
34 return nil, nil, fmt.Errorf("no server type")
35 }
36 if options == nil {
37 options = make(map[string]any)
38 }
39
40 filename, _ := options["filename"].(string)
41 if filename == "" {
42 filename = "Caddyfile"
43 }
44
45 serverBlocks, err := Parse(filename, body)
46 if err != nil {
47 return nil, nil, err
48 }
49
50 cfg, warnings, err := a.ServerType.Setup(serverBlocks, options)
51 if err != nil {
52 return nil, warnings, err
53 }
54
55 // lint check: see if input was properly formatted; sometimes messy files parse
56 // successfully but result in logical errors (the Caddyfile is a bad format, I'm sorry)
57 if warning, different := FormattingDifference(filename, body); different {
58 warnings = append(warnings, warning)
59 }
60
61 result, err := json.Marshal(cfg)
62
63 return result, warnings, err
64 }
65
66 // FormattingDifference returns a warning and true if the formatted version
67 // is any different from the input; empty warning and false otherwise.
68 // TODO: also perform this check on imported files
69 func FormattingDifference(filename string, body []byte) (caddyconfig.Warning, bool) {
70 // replace windows-style newlines to normalize comparison
71 normalizedBody := bytes.ReplaceAll(body, []byte("\r\n"), []byte("\n"))
72
73 formatted := Format(normalizedBody)
74 if bytes.Equal(formatted, normalizedBody) {
75 return caddyconfig.Warning{}, false
76 }
77
78 // find where the difference is
79 line := 1
80 for i, ch := range normalizedBody {
81 if i >= len(formatted) || ch != formatted[i] {
82 break
83 }
84 if ch == '\n' {
85 line++
86 }
87 }
88 return caddyconfig.Warning{
89 File: filename,
90 Line: line,
91 Message: "Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies",
92 }, true
93 }
94
95 // Unmarshaler is a type that can unmarshal Caddyfile tokens to
96 // set itself up for a JSON encoding. The goal of an unmarshaler
97 // is not to set itself up for actual use, but to set itself up for
98 // being marshaled into JSON. Caddyfile-unmarshaled values will not
99 // be used directly; they will be encoded as JSON and then used from
100 // that. Implementations _may_ be able to support multiple segments
101 // (instances of their directive or batch of tokens); typically this
102 // means wrapping parsing logic in a loop: `for d.Next() { ... }`.
103 // More commonly, only a single segment is supported, so a simple
104 // `d.Next()` at the start should be used to consume the module
105 // identifier token (directive name, etc).
106 type Unmarshaler interface {
107 UnmarshalCaddyfile(d *Dispenser) error
108 }
109
110 // ServerType is a type that can evaluate a Caddyfile and set up a caddy config.
111 type ServerType interface {
112 // Setup takes the server blocks which contain tokens,
113 // as well as options (e.g. CLI flags) and creates a
114 // Caddy config, along with any warnings or an error.
115 Setup([]ServerBlock, map[string]any) (*caddy.Config, []caddyconfig.Warning, error)
116 }
117
118 // UnmarshalModule instantiates a module with the given ID and invokes
119 // UnmarshalCaddyfile on the new value using the immediate next segment
120 // of d as input. In other words, d's next token should be the first
121 // token of the module's Caddyfile input.
122 //
123 // This function is used when the next segment of Caddyfile tokens
124 // belongs to another Caddy module. The returned value is often
125 // type-asserted to the module's associated type for practical use
126 // when setting up a config.
127 func UnmarshalModule(d *Dispenser, moduleID string) (Unmarshaler, error) {
128 mod, err := caddy.GetModule(moduleID)
129 if err != nil {
130 return nil, d.Errf("getting module named '%s': %v", moduleID, err)
131 }
132 inst := mod.New()
133 unm, ok := inst.(Unmarshaler)
134 if !ok {
135 return nil, d.Errf("module %s is not a Caddyfile unmarshaler; is %T", mod.ID, inst)
136 }
137 err = unm.UnmarshalCaddyfile(d.NewFromNextSegment())
138 if err != nil {
139 return nil, err
140 }
141 return unm, nil
142 }
143
144 // Interface guard
145 var _ caddyconfig.Adapter = (*Adapter)(nil)