mirror of
https://github.com/caddyserver/caddy.git
synced 2025-01-14 14:56:27 +03:00
httpcaddyfile: Configure servers via global options (#3836)
* httpcaddyfile: First pass at implementing server options * httpcaddyfile: Add listener wrapper support * httpcaddyfile: Sort sbaddrs to make adapt output more deterministic * httpcaddyfile: Add server options adapt tests * httpcaddyfile: Windows line endings lol * caddytest: More windows line endings lol (sorry Matt) * Update caddyconfig/httpcaddyfile/serveroptions.go Co-authored-by: Matt Holt <mholt@users.noreply.github.com> * httpcaddyfile: Reword listener address "matcher" * Apply suggestions from code review Co-authored-by: Matt Holt <mholt@users.noreply.github.com> * httpcaddyfile: Deprecate experimental_http3 option (moved to servers) * httpcaddyfile: Remove validation step, no longer needed Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
This commit is contained in:
parent
4a641f6c6f
commit
3cfefeb0f7
15 changed files with 1084 additions and 658 deletions
|
@ -18,6 +18,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
@ -163,6 +164,13 @@ func (st *ServerType) consolidateAddrMappings(addrToServerBlocks map[string][]se
|
||||||
|
|
||||||
sbaddrs = append(sbaddrs, a)
|
sbaddrs = append(sbaddrs, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sort them by their first address (we know there will always be at least one)
|
||||||
|
// to avoid problems with non-deterministic ordering (makes tests flaky)
|
||||||
|
sort.Slice(sbaddrs, func(i, j int) bool {
|
||||||
|
return sbaddrs[i].addresses[0] < sbaddrs[j].addresses[0]
|
||||||
|
})
|
||||||
|
|
||||||
return sbaddrs
|
return sbaddrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -218,13 +218,6 @@ func (st ServerType) Setup(inputServerBlocks []caddyfile.ServerBlock,
|
||||||
return nil, warnings, err
|
return nil, warnings, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// if experimental HTTP/3 is enabled, enable it on each server
|
|
||||||
if enableH3, ok := options["experimental_http3"].(bool); ok && enableH3 {
|
|
||||||
for _, srv := range httpApp.Servers {
|
|
||||||
srv.ExperimentalHTTP3 = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// extract any custom logs, and enforce configured levels
|
// extract any custom logs, and enforce configured levels
|
||||||
var customLogs []namedCustomLog
|
var customLogs []namedCustomLog
|
||||||
var hasDefaultLog bool
|
var hasDefaultLog bool
|
||||||
|
@ -311,23 +304,54 @@ func (ServerType) evaluateGlobalOptionsBlock(serverBlocks []serverBlock, options
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, segment := range serverBlocks[0].block.Segments {
|
for _, segment := range serverBlocks[0].block.Segments {
|
||||||
dir := segment.Directive()
|
opt := segment.Directive()
|
||||||
var val interface{}
|
var val interface{}
|
||||||
var err error
|
var err error
|
||||||
disp := caddyfile.NewDispenser(segment)
|
disp := caddyfile.NewDispenser(segment)
|
||||||
|
|
||||||
dirFunc, ok := registeredGlobalOptions[dir]
|
optFunc, ok := registeredGlobalOptions[opt]
|
||||||
if !ok {
|
if !ok {
|
||||||
tkn := segment[0]
|
tkn := segment[0]
|
||||||
return nil, fmt.Errorf("%s:%d: unrecognized global option: %s", tkn.File, tkn.Line, dir)
|
return nil, fmt.Errorf("%s:%d: unrecognized global option: %s", tkn.File, tkn.Line, opt)
|
||||||
}
|
}
|
||||||
|
|
||||||
val, err = dirFunc(disp)
|
val, err = optFunc(disp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("parsing caddyfile tokens for '%s': %v", dir, err)
|
return nil, fmt.Errorf("parsing caddyfile tokens for '%s': %v", opt, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
options[dir] = val
|
// As a special case, fold multiple "servers" options together
|
||||||
|
// in an array instead of overwriting a possible existing value
|
||||||
|
if opt == "servers" {
|
||||||
|
existingOpts, ok := options[opt].([]serverOptions)
|
||||||
|
if !ok {
|
||||||
|
existingOpts = []serverOptions{}
|
||||||
|
}
|
||||||
|
serverOpts, ok := val.(serverOptions)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unexpected type from 'servers' global options")
|
||||||
|
}
|
||||||
|
options[opt] = append(existingOpts, serverOpts)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
options[opt] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we got "servers" options, we'll sort them by their listener address
|
||||||
|
if serverOpts, ok := options["servers"].([]serverOptions); ok {
|
||||||
|
sort.Slice(serverOpts, func(i, j int) bool {
|
||||||
|
return len(serverOpts[i].ListenerAddress) > len(serverOpts[j].ListenerAddress)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Reject the config if there are duplicate listener address
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
for _, entry := range serverOpts {
|
||||||
|
if _, alreadySeen := seen[entry.ListenerAddress]; alreadySeen {
|
||||||
|
return nil, fmt.Errorf("cannot have 'servers' global options with duplicate listener addresses: %s", entry.ListenerAddress)
|
||||||
|
}
|
||||||
|
seen[entry.ListenerAddress] = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return serverBlocks[1:], nil
|
return serverBlocks[1:], nil
|
||||||
|
@ -602,6 +626,11 @@ func (st *ServerType) serversFromPairings(
|
||||||
servers[fmt.Sprintf("srv%d", i)] = srv
|
servers[fmt.Sprintf("srv%d", i)] = srv
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := applyServerOptions(servers, options, warnings)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return servers, nil
|
return servers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ func init() {
|
||||||
RegisterGlobalOption("local_certs", parseOptTrue)
|
RegisterGlobalOption("local_certs", parseOptTrue)
|
||||||
RegisterGlobalOption("key_type", parseOptSingleString)
|
RegisterGlobalOption("key_type", parseOptSingleString)
|
||||||
RegisterGlobalOption("auto_https", parseOptAutoHTTPS)
|
RegisterGlobalOption("auto_https", parseOptAutoHTTPS)
|
||||||
|
RegisterGlobalOption("servers", parseServerOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseOptTrue(d *caddyfile.Dispenser) (interface{}, error) {
|
func parseOptTrue(d *caddyfile.Dispenser) (interface{}, error) {
|
||||||
|
@ -361,3 +362,7 @@ func parseOptAutoHTTPS(d *caddyfile.Dispenser) (interface{}, error) {
|
||||||
}
|
}
|
||||||
return val, nil
|
return val, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseServerOptions(d *caddyfile.Dispenser) (interface{}, error) {
|
||||||
|
return unmarshalCaddyfileServerOptions(d)
|
||||||
|
}
|
||||||
|
|
235
caddyconfig/httpcaddyfile/serveroptions.go
Normal file
235
caddyconfig/httpcaddyfile/serveroptions.go
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
// Copyright 2015 Matthew Holt and The Caddy Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package httpcaddyfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/caddyserver/caddy/v2"
|
||||||
|
"github.com/caddyserver/caddy/v2/caddyconfig"
|
||||||
|
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||||
|
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
||||||
|
"github.com/dustin/go-humanize"
|
||||||
|
)
|
||||||
|
|
||||||
|
// serverOptions collects server config overrides parsed from Caddyfile global options
|
||||||
|
type serverOptions struct {
|
||||||
|
// If set, will only apply these options to servers that contain a
|
||||||
|
// listener address that matches exactly. If empty, will apply to all
|
||||||
|
// servers that were not already matched by another serverOptions.
|
||||||
|
ListenerAddress string
|
||||||
|
|
||||||
|
// These will all map 1:1 to the caddyhttp.Server struct
|
||||||
|
ListenerWrappersRaw []json.RawMessage
|
||||||
|
ReadTimeout caddy.Duration
|
||||||
|
ReadHeaderTimeout caddy.Duration
|
||||||
|
WriteTimeout caddy.Duration
|
||||||
|
IdleTimeout caddy.Duration
|
||||||
|
MaxHeaderBytes int
|
||||||
|
AllowH2C bool
|
||||||
|
ExperimentalHTTP3 bool
|
||||||
|
StrictSNIHost *bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (interface{}, error) {
|
||||||
|
serverOpts := serverOptions{}
|
||||||
|
for d.Next() {
|
||||||
|
if d.NextArg() {
|
||||||
|
serverOpts.ListenerAddress = d.Val()
|
||||||
|
if d.NextArg() {
|
||||||
|
return nil, d.ArgErr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for nesting := d.Nesting(); d.NextBlock(nesting); {
|
||||||
|
switch d.Val() {
|
||||||
|
case "listener_wrappers":
|
||||||
|
for nesting := d.Nesting(); d.NextBlock(nesting); {
|
||||||
|
mod, err := caddy.GetModule("caddy.listeners." + d.Val())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("finding listener module '%s': %v", d.Val(), err)
|
||||||
|
}
|
||||||
|
unm, ok := mod.New().(caddyfile.Unmarshaler)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("listener module '%s' is not a Caddyfile unmarshaler", mod)
|
||||||
|
}
|
||||||
|
err = unm.UnmarshalCaddyfile(d.NewFromNextSegment())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
listenerWrapper, ok := unm.(caddy.ListenerWrapper)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("module %s is not a listener wrapper", mod)
|
||||||
|
}
|
||||||
|
jsonListenerWrapper := caddyconfig.JSONModuleObject(
|
||||||
|
listenerWrapper,
|
||||||
|
"wrapper",
|
||||||
|
listenerWrapper.(caddy.Module).CaddyModule().ID.Name(),
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
serverOpts.ListenerWrappersRaw = append(serverOpts.ListenerWrappersRaw, jsonListenerWrapper)
|
||||||
|
}
|
||||||
|
|
||||||
|
case "timeouts":
|
||||||
|
for nesting := d.Nesting(); d.NextBlock(nesting); {
|
||||||
|
switch d.Val() {
|
||||||
|
case "read_body":
|
||||||
|
if !d.NextArg() {
|
||||||
|
return nil, d.ArgErr()
|
||||||
|
}
|
||||||
|
dur, err := caddy.ParseDuration(d.Val())
|
||||||
|
if err != nil {
|
||||||
|
return nil, d.Errf("parsing read_body timeout duration: %v", err)
|
||||||
|
}
|
||||||
|
serverOpts.ReadTimeout = caddy.Duration(dur)
|
||||||
|
|
||||||
|
case "read_header":
|
||||||
|
if !d.NextArg() {
|
||||||
|
return nil, d.ArgErr()
|
||||||
|
}
|
||||||
|
dur, err := caddy.ParseDuration(d.Val())
|
||||||
|
if err != nil {
|
||||||
|
return nil, d.Errf("parsing read_header timeout duration: %v", err)
|
||||||
|
}
|
||||||
|
serverOpts.ReadHeaderTimeout = caddy.Duration(dur)
|
||||||
|
|
||||||
|
case "write":
|
||||||
|
if !d.NextArg() {
|
||||||
|
return nil, d.ArgErr()
|
||||||
|
}
|
||||||
|
dur, err := caddy.ParseDuration(d.Val())
|
||||||
|
if err != nil {
|
||||||
|
return nil, d.Errf("parsing write timeout duration: %v", err)
|
||||||
|
}
|
||||||
|
serverOpts.WriteTimeout = caddy.Duration(dur)
|
||||||
|
|
||||||
|
case "idle":
|
||||||
|
if !d.NextArg() {
|
||||||
|
return nil, d.ArgErr()
|
||||||
|
}
|
||||||
|
dur, err := caddy.ParseDuration(d.Val())
|
||||||
|
if err != nil {
|
||||||
|
return nil, d.Errf("parsing idle timeout duration: %v", err)
|
||||||
|
}
|
||||||
|
serverOpts.IdleTimeout = caddy.Duration(dur)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, d.Errf("unrecognized timeouts option '%s'", d.Val())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case "max_header_size":
|
||||||
|
var sizeStr string
|
||||||
|
if !d.AllArgs(&sizeStr) {
|
||||||
|
return nil, d.ArgErr()
|
||||||
|
}
|
||||||
|
size, err := humanize.ParseBytes(sizeStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, d.Errf("parsing max_header_size: %v", err)
|
||||||
|
}
|
||||||
|
serverOpts.MaxHeaderBytes = int(size)
|
||||||
|
|
||||||
|
case "protocol":
|
||||||
|
for nesting := d.Nesting(); d.NextBlock(nesting); {
|
||||||
|
switch d.Val() {
|
||||||
|
case "allow_h2c":
|
||||||
|
if d.NextArg() {
|
||||||
|
return nil, d.ArgErr()
|
||||||
|
}
|
||||||
|
serverOpts.AllowH2C = true
|
||||||
|
|
||||||
|
case "experimental_http3":
|
||||||
|
if d.NextArg() {
|
||||||
|
return nil, d.ArgErr()
|
||||||
|
}
|
||||||
|
serverOpts.ExperimentalHTTP3 = true
|
||||||
|
|
||||||
|
case "strict_sni_host":
|
||||||
|
if d.NextArg() {
|
||||||
|
return nil, d.ArgErr()
|
||||||
|
}
|
||||||
|
trueBool := true
|
||||||
|
serverOpts.StrictSNIHost = &trueBool
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, d.Errf("unrecognized protocol option '%s'", d.Val())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, d.Errf("unrecognized servers option '%s'", d.Val())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return serverOpts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// applyServerOptions sets the server options on the appropriate servers
|
||||||
|
func applyServerOptions(
|
||||||
|
servers map[string]*caddyhttp.Server,
|
||||||
|
options map[string]interface{},
|
||||||
|
warnings *[]caddyconfig.Warning,
|
||||||
|
) error {
|
||||||
|
// If experimental HTTP/3 is enabled, enable it on each server.
|
||||||
|
// We already know there won't be a conflict with serverOptions because
|
||||||
|
// we validated earlier that "experimental_http3" cannot be set at the same
|
||||||
|
// time as "servers"
|
||||||
|
if enableH3, ok := options["experimental_http3"].(bool); ok && enableH3 {
|
||||||
|
*warnings = append(*warnings, caddyconfig.Warning{Message: "the 'experimental_http3' global option is deprecated, please use the 'servers > protocol > experimental_http3' option instead"})
|
||||||
|
for _, srv := range servers {
|
||||||
|
srv.ExperimentalHTTP3 = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serverOpts, ok := options["servers"].([]serverOptions)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, server := range servers {
|
||||||
|
// find the options that apply to this server
|
||||||
|
opts := func() *serverOptions {
|
||||||
|
for _, entry := range serverOpts {
|
||||||
|
if entry.ListenerAddress == "" {
|
||||||
|
return &entry
|
||||||
|
}
|
||||||
|
for _, listener := range server.Listen {
|
||||||
|
if entry.ListenerAddress == listener {
|
||||||
|
return &entry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}()
|
||||||
|
|
||||||
|
// if none apply, then move to the next server
|
||||||
|
if opts == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// set all the options
|
||||||
|
server.ListenerWrappersRaw = opts.ListenerWrappersRaw
|
||||||
|
server.ReadTimeout = opts.ReadTimeout
|
||||||
|
server.ReadHeaderTimeout = opts.ReadHeaderTimeout
|
||||||
|
server.WriteTimeout = opts.WriteTimeout
|
||||||
|
server.IdleTimeout = opts.IdleTimeout
|
||||||
|
server.MaxHeaderBytes = opts.MaxHeaderBytes
|
||||||
|
server.AllowH2C = opts.AllowH2C
|
||||||
|
server.ExperimentalHTTP3 = opts.ExperimentalHTTP3
|
||||||
|
server.StrictSNIHost = opts.StrictSNIHost
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
{
|
||||||
|
servers {
|
||||||
|
timeouts {
|
||||||
|
idle 90s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
servers :80 {
|
||||||
|
timeouts {
|
||||||
|
idle 60s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
servers :443 {
|
||||||
|
timeouts {
|
||||||
|
idle 30s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foo.com {
|
||||||
|
}
|
||||||
|
|
||||||
|
http://bar.com {
|
||||||
|
}
|
||||||
|
|
||||||
|
:8080 {
|
||||||
|
}
|
||||||
|
|
||||||
|
----------
|
||||||
|
{
|
||||||
|
"apps": {
|
||||||
|
"http": {
|
||||||
|
"servers": {
|
||||||
|
"srv0": {
|
||||||
|
"listen": [
|
||||||
|
":443"
|
||||||
|
],
|
||||||
|
"idle_timeout": 30000000000,
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"match": [
|
||||||
|
{
|
||||||
|
"host": [
|
||||||
|
"foo.com"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"terminal": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"srv1": {
|
||||||
|
"listen": [
|
||||||
|
":80"
|
||||||
|
],
|
||||||
|
"idle_timeout": 60000000000,
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"match": [
|
||||||
|
{
|
||||||
|
"host": [
|
||||||
|
"bar.com"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"terminal": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"automatic_https": {
|
||||||
|
"skip": [
|
||||||
|
"bar.com"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"srv2": {
|
||||||
|
"listen": [
|
||||||
|
":8080"
|
||||||
|
],
|
||||||
|
"idle_timeout": 90000000000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
{
|
||||||
|
servers {
|
||||||
|
listener_wrappers {
|
||||||
|
tls
|
||||||
|
}
|
||||||
|
timeouts {
|
||||||
|
read_body 30s
|
||||||
|
read_header 30s
|
||||||
|
write 30s
|
||||||
|
idle 30s
|
||||||
|
}
|
||||||
|
max_header_size 100MB
|
||||||
|
protocol {
|
||||||
|
allow_h2c
|
||||||
|
experimental_http3
|
||||||
|
strict_sni_host
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foo.com {
|
||||||
|
}
|
||||||
|
|
||||||
|
----------
|
||||||
|
{
|
||||||
|
"apps": {
|
||||||
|
"http": {
|
||||||
|
"servers": {
|
||||||
|
"srv0": {
|
||||||
|
"listen": [
|
||||||
|
":443"
|
||||||
|
],
|
||||||
|
"listener_wrappers": [
|
||||||
|
{
|
||||||
|
"wrapper": "tls"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"read_timeout": 30000000000,
|
||||||
|
"read_header_timeout": 30000000000,
|
||||||
|
"write_timeout": 30000000000,
|
||||||
|
"idle_timeout": 30000000000,
|
||||||
|
"max_header_bytes": 100000000,
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"match": [
|
||||||
|
{
|
||||||
|
"host": [
|
||||||
|
"foo.com"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"terminal": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"strict_sni_host": true,
|
||||||
|
"experimental_http3": true,
|
||||||
|
"allow_h2c": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2"
|
"github.com/caddyserver/caddy/v2"
|
||||||
|
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -231,6 +232,8 @@ func (tlsPlaceholderWrapper) CaddyModule() caddy.ModuleInfo {
|
||||||
|
|
||||||
func (tlsPlaceholderWrapper) WrapListener(ln net.Listener) net.Listener { return ln }
|
func (tlsPlaceholderWrapper) WrapListener(ln net.Listener) net.Listener { return ln }
|
||||||
|
|
||||||
|
func (tlsPlaceholderWrapper) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { return nil }
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// DefaultHTTPPort is the default port for HTTP.
|
// DefaultHTTPPort is the default port for HTTP.
|
||||||
DefaultHTTPPort = 80
|
DefaultHTTPPort = 80
|
||||||
|
@ -241,3 +244,4 @@ const (
|
||||||
|
|
||||||
// Interface guard
|
// Interface guard
|
||||||
var _ caddy.ListenerWrapper = (*tlsPlaceholderWrapper)(nil)
|
var _ caddy.ListenerWrapper = (*tlsPlaceholderWrapper)(nil)
|
||||||
|
var _ caddyfile.Unmarshaler = (*tlsPlaceholderWrapper)(nil)
|
||||||
|
|
Loading…
Reference in a new issue