2020-11-23 14:46:50 -05:00
// 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
2021-12-02 15:26:24 -05:00
ListenerWrappersRaw [ ] json . RawMessage
ReadTimeout caddy . Duration
ReadHeaderTimeout caddy . Duration
WriteTimeout caddy . Duration
IdleTimeout caddy . Duration
MaxHeaderBytes int
2022-08-15 13:01:58 -05:00
Protocols [ ] string
2021-12-02 15:26:24 -05:00
StrictSNIHost * bool
ShouldLogCredentials bool
2020-11-23 14:46:50 -05:00
}
2022-08-02 15:39:09 -05:00
func unmarshalCaddyfileServerOptions ( d * caddyfile . Dispenser ) ( any , error ) {
2020-11-23 14:46:50 -05:00
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 ) ; {
2021-01-05 16:39:30 -05:00
modID := "caddy.listeners." + d . Val ( )
unm , err := caddyfile . UnmarshalModule ( d , modID )
2020-11-23 14:46:50 -05:00
if err != nil {
return nil , err
}
listenerWrapper , ok := unm . ( caddy . ListenerWrapper )
if ! ok {
2021-01-05 16:39:30 -05:00
return nil , fmt . Errorf ( "module %s (%T) is not a listener wrapper" , modID , unm )
2020-11-23 14:46:50 -05:00
}
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 )
2021-12-02 15:26:24 -05:00
case "log_credentials" :
if d . NextArg ( ) {
return nil , d . ArgErr ( )
}
serverOpts . ShouldLogCredentials = true
2022-08-15 13:01:58 -05:00
case "protocols" :
protos := d . RemainingArgs ( )
for _ , proto := range protos {
if proto != "h1" && proto != "h2" && proto != "h2c" && proto != "h3" {
return nil , d . Errf ( "unknown protocol '%s': expected h1, h2, h2c, or h3" , proto )
}
if sliceContains ( serverOpts . Protocols , proto ) {
return nil , d . Errf ( "protocol %s specified more than once" , proto )
}
serverOpts . Protocols = append ( serverOpts . Protocols , proto )
}
if d . NextBlock ( 0 ) {
return nil , d . ArgErr ( )
}
case "strict_sni_host" :
if d . NextArg ( ) && d . Val ( ) != "insecure_off" && d . Val ( ) != "on" {
return nil , d . Errf ( "strict_sni_host only supports 'on' or 'insecure_off', got '%s'" , d . Val ( ) )
}
boolVal := true
if d . Val ( ) == "insecure_off" {
boolVal = false
}
serverOpts . StrictSNIHost = & boolVal
// TODO: DEPRECATED. (August 2022)
2020-11-23 14:46:50 -05:00
case "protocol" :
2022-08-15 13:01:58 -05:00
caddy . Log ( ) . Named ( "caddyfile" ) . Warn ( "DEPRECATED: protocol sub-option will be removed soon" )
2020-11-23 14:46:50 -05:00
for nesting := d . Nesting ( ) ; d . NextBlock ( nesting ) ; {
switch d . Val ( ) {
case "allow_h2c" :
2022-08-15 13:01:58 -05:00
caddy . Log ( ) . Named ( "caddyfile" ) . Warn ( "DEPRECATED: allow_h2c will be removed soon; use protocols option instead" )
2020-11-23 14:46:50 -05:00
if d . NextArg ( ) {
return nil , d . ArgErr ( )
}
2022-08-15 13:01:58 -05:00
if sliceContains ( serverOpts . Protocols , "h2c" ) {
return nil , d . Errf ( "protocol h2c already specified" )
}
serverOpts . Protocols = append ( serverOpts . Protocols , "h2c" )
2020-11-23 14:46:50 -05:00
case "strict_sni_host" :
2022-08-15 13:01:58 -05:00
caddy . Log ( ) . Named ( "caddyfile" ) . Warn ( "DEPRECATED: protocol > strict_sni_host in this position will be removed soon; move up to the servers block instead" )
2022-03-01 20:02:39 -05:00
if d . NextArg ( ) && d . Val ( ) != "insecure_off" && d . Val ( ) != "on" {
return nil , d . Errf ( "strict_sni_host only supports 'on' or 'insecure_off', got '%s'" , d . Val ( ) )
}
boolVal := true
if d . Val ( ) == "insecure_off" {
boolVal = false
2020-11-23 14:46:50 -05:00
}
2022-03-01 20:02:39 -05:00
serverOpts . StrictSNIHost = & boolVal
2020-11-23 14:46:50 -05:00
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 ,
2022-08-02 15:39:09 -05:00
options map [ string ] any ,
2020-11-23 14:46:50 -05:00
warnings * [ ] caddyconfig . Warning ,
) error {
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
2022-08-15 13:01:58 -05:00
server . Protocols = opts . Protocols
2020-11-23 14:46:50 -05:00
server . StrictSNIHost = opts . StrictSNIHost
2021-12-02 15:26:24 -05:00
if opts . ShouldLogCredentials {
if server . Logs == nil {
server . Logs = & caddyhttp . ServerLogConfig { }
}
server . Logs . ShouldLogCredentials = opts . ShouldLogCredentials
}
2020-11-23 14:46:50 -05:00
}
return nil
}