2019-06-30 17:07:58 -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.
2019-03-26 13:00:54 -05:00
package caddyhttp
import (
2019-08-09 13:05:47 -05:00
"bytes"
2019-03-26 20:42:52 -05:00
"context"
2019-04-25 14:54:48 -05:00
"crypto/tls"
2019-08-09 13:05:47 -05:00
"encoding/json"
2019-03-26 16:45:51 -05:00
"fmt"
2019-08-09 13:05:47 -05:00
"io"
2019-05-20 16:46:34 -05:00
weakrand "math/rand"
2019-03-26 16:45:51 -05:00
"net"
"net/http"
"strconv"
"strings"
"time"
2019-03-26 13:00:54 -05:00
2019-07-02 13:37:06 -05:00
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/modules/caddytls"
2019-09-10 09:03:37 -05:00
"github.com/lucas-clemente/quic-go/http3"
2019-04-25 14:54:48 -05:00
"github.com/mholt/certmagic"
2019-10-28 15:39:37 -05:00
"go.uber.org/zap"
2019-03-26 13:00:54 -05:00
)
func init ( ) {
2019-05-20 16:46:34 -05:00
weakrand . Seed ( time . Now ( ) . UnixNano ( ) )
2019-04-25 14:54:48 -05:00
2019-08-21 11:46:35 -05:00
err := caddy . RegisterModule ( App { } )
2019-03-26 13:00:54 -05:00
if err != nil {
2019-10-28 15:39:37 -05:00
caddy . Log ( ) . Fatal ( err . Error ( ) )
2019-03-26 13:00:54 -05:00
}
}
2019-05-14 15:14:05 -05:00
// App is the HTTP app for Caddy.
type App struct {
2019-05-22 13:32:36 -05:00
HTTPPort int ` json:"http_port,omitempty" `
HTTPSPort int ` json:"https_port,omitempty" `
2019-06-18 12:13:12 -05:00
GracePeriod caddy . Duration ` json:"grace_period,omitempty" `
2019-05-22 13:32:36 -05:00
Servers map [ string ] * Server ` json:"servers,omitempty" `
2019-03-26 20:42:52 -05:00
2019-09-10 09:03:37 -05:00
servers [ ] * http . Server
h3servers [ ] * http3 . Server
h3listeners [ ] net . PacketConn
2019-05-16 17:05:38 -05:00
2019-10-28 15:39:37 -05:00
ctx caddy . Context
logger * zap . Logger
2019-03-26 13:00:54 -05:00
}
2019-08-21 11:46:35 -05:00
// CaddyModule returns the Caddy module information.
func ( App ) CaddyModule ( ) caddy . ModuleInfo {
return caddy . ModuleInfo {
Name : "http" ,
New : func ( ) caddy . Module { return new ( App ) } ,
}
}
2019-05-14 15:14:05 -05:00
// Provision sets up the app.
2019-06-14 12:58:28 -05:00
func ( app * App ) Provision ( ctx caddy . Context ) error {
2019-05-16 17:05:38 -05:00
app . ctx = ctx
2019-10-28 15:39:37 -05:00
app . logger = ctx . Logger ( app )
2019-05-16 17:05:38 -05:00
2019-06-14 12:58:28 -05:00
repl := caddy . NewReplacer ( )
2019-05-20 11:59:20 -05:00
2019-10-28 15:39:37 -05:00
for srvName , srv := range app . Servers {
srv . logger = app . logger . Named ( "log" )
srv . accessLogger = app . logger . Named ( "log.access" )
srv . errorLogger = app . logger . Named ( "log.error" )
2019-06-26 11:49:32 -05:00
if srv . AutoHTTPS == nil {
// avoid nil pointer dereferences
srv . AutoHTTPS = new ( AutoHTTPSConfig )
}
2019-09-18 00:13:21 -05:00
// if not explicitly configured by the user, disallow TLS
// client auth bypass (domain fronting) which could
// otherwise be exploited by sending an unprotected SNI
// value during a TLS handshake, then putting a protected
// domain in the Host header after establishing connection;
// this is a safe default, but we allow users to override
// it for example in the case of running a proxy where
// domain fronting is desired and access is not restricted
// based on hostname
if srv . StrictSNIHost == nil && srv . hasTLSClientAuth ( ) {
trueBool := true
srv . StrictSNIHost = & trueBool
2019-09-03 10:35:36 -05:00
}
2019-05-20 11:59:20 -05:00
for i := range srv . Listen {
2019-10-28 15:39:37 -05:00
lnOut , err := repl . ReplaceOrErr ( srv . Listen [ i ] , true , true )
if err != nil {
return fmt . Errorf ( "server %s, listener %d: %v" ,
srvName , i , err )
}
srv . Listen [ i ] = lnOut
2019-05-20 11:59:20 -05:00
}
2019-06-26 11:49:32 -05:00
2019-05-22 14:13:39 -05:00
if srv . Routes != nil {
err := srv . Routes . Provision ( ctx )
if err != nil {
2019-10-28 15:39:37 -05:00
return fmt . Errorf ( "server %s: setting up server routes: %v" , srvName , err )
2019-05-22 14:13:39 -05:00
}
2019-04-11 21:42:55 -05:00
}
2019-06-26 11:49:32 -05:00
2019-05-22 14:13:39 -05:00
if srv . Errors != nil {
err := srv . Errors . Routes . Provision ( ctx )
if err != nil {
2019-10-28 15:39:37 -05:00
return fmt . Errorf ( "server %s: setting up server error handling routes: %v" , srvName , err )
2019-05-22 14:13:39 -05:00
}
2019-03-31 21:41:29 -05:00
}
2019-07-11 23:02:47 -05:00
if srv . MaxRehandles == nil {
srv . MaxRehandles = & DefaultMaxRehandles
}
2019-04-25 14:54:48 -05:00
}
2019-03-31 21:41:29 -05:00
2019-04-25 14:54:48 -05:00
return nil
}
2019-05-14 15:14:05 -05:00
// Validate ensures the app's configuration is valid.
2019-05-16 17:05:38 -05:00
func ( app * App ) Validate ( ) error {
2019-05-07 10:56:13 -05:00
// each server must use distinct listener addresses
lnAddrs := make ( map [ string ] string )
2019-05-16 17:05:38 -05:00
for srvName , srv := range app . Servers {
2019-05-07 10:56:13 -05:00
for _ , addr := range srv . Listen {
2019-09-05 14:14:39 -05:00
netw , expanded , err := caddy . ParseNetworkAddress ( addr )
2019-05-07 10:56:13 -05:00
if err != nil {
return fmt . Errorf ( "invalid listener address '%s': %v" , addr , err )
}
for _ , a := range expanded {
if sn , ok := lnAddrs [ netw + a ] ; ok {
2019-07-07 15:12:22 -05:00
return fmt . Errorf ( "server %s: listener address repeated: %s (already claimed by server '%s')" , srvName , a , sn )
2019-05-07 10:56:13 -05:00
}
lnAddrs [ netw + a ] = srvName
}
}
}
2019-05-20 11:59:20 -05:00
// each server's max rehandle value must be valid
for srvName , srv := range app . Servers {
2019-07-11 23:02:47 -05:00
if srv . MaxRehandles != nil && * srv . MaxRehandles < 0 {
return fmt . Errorf ( "%s: invalid max_rehandles value: %d" , srvName , * srv . MaxRehandles )
2019-05-20 11:59:20 -05:00
}
}
2019-05-07 10:56:13 -05:00
return nil
}
2019-05-14 15:14:05 -05:00
// Start runs the app. It sets up automatic HTTPS if enabled.
2019-05-16 17:05:38 -05:00
func ( app * App ) Start ( ) error {
err := app . automaticHTTPS ( )
2019-04-25 14:54:48 -05:00
if err != nil {
2019-05-20 18:15:38 -05:00
return fmt . Errorf ( "enabling automatic HTTPS: %v" , err )
2019-04-25 14:54:48 -05:00
}
2019-05-16 17:05:38 -05:00
for srvName , srv := range app . Servers {
2019-03-26 20:42:52 -05:00
s := & http . Server {
2019-03-26 16:45:51 -05:00
ReadTimeout : time . Duration ( srv . ReadTimeout ) ,
ReadHeaderTimeout : time . Duration ( srv . ReadHeaderTimeout ) ,
2019-05-23 14:16:34 -05:00
WriteTimeout : time . Duration ( srv . WriteTimeout ) ,
IdleTimeout : time . Duration ( srv . IdleTimeout ) ,
MaxHeaderBytes : srv . MaxHeaderBytes ,
2019-03-31 21:41:29 -05:00
Handler : srv ,
2019-03-26 16:45:51 -05:00
}
for _ , lnAddr := range srv . Listen {
2019-09-05 14:14:39 -05:00
network , addrs , err := caddy . ParseNetworkAddress ( lnAddr )
2019-03-26 16:45:51 -05:00
if err != nil {
2019-04-25 14:54:48 -05:00
return fmt . Errorf ( "%s: parsing listen address '%s': %v" , srvName , lnAddr , err )
2019-03-26 16:45:51 -05:00
}
for _ , addr := range addrs {
2019-06-14 12:58:28 -05:00
ln , err := caddy . Listen ( network , addr )
2019-03-26 16:45:51 -05:00
if err != nil {
2019-03-31 21:41:29 -05:00
return fmt . Errorf ( "%s: listening on %s: %v" , network , addr , err )
2019-03-26 16:45:51 -05:00
}
2019-04-25 14:54:48 -05:00
2019-09-03 10:35:36 -05:00
// enable HTTP/2 by default
2019-04-25 14:54:48 -05:00
for _ , pol := range srv . TLSConnPolicies {
if len ( pol . ALPN ) == 0 {
pol . ALPN = append ( pol . ALPN , defaultALPN ... )
}
}
// enable TLS
2019-05-07 10:56:13 -05:00
_ , port , _ := net . SplitHostPort ( addr )
2019-09-18 19:01:32 -05:00
if len ( srv . TLSConnPolicies ) > 0 && port != strconv . Itoa ( app . httpPort ( ) ) {
2019-05-16 17:05:38 -05:00
tlsCfg , err := srv . TLSConnPolicies . TLSConfig ( app . ctx )
2019-04-25 14:54:48 -05:00
if err != nil {
return fmt . Errorf ( "%s/%s: making TLS configuration: %v" , network , addr , err )
}
ln = tls . NewListener ( ln , tlsCfg )
2019-09-10 09:03:37 -05:00
/////////
// TODO: HTTP/3 support is experimental for now
if srv . ExperimentalHTTP3 {
2019-10-28 15:39:37 -05:00
app . logger . Info ( "enabling experimental HTTP/3 listener" ,
zap . String ( "addr" , addr ) ,
)
2019-09-10 09:03:37 -05:00
h3ln , err := caddy . ListenPacket ( "udp" , addr )
if err != nil {
return fmt . Errorf ( "getting HTTP/3 UDP listener: %v" , err )
}
h3srv := & http3 . Server {
Server : & http . Server {
Addr : addr ,
Handler : srv ,
TLSConfig : tlsCfg ,
} ,
}
go h3srv . Serve ( h3ln )
app . h3servers = append ( app . h3servers , h3srv )
app . h3listeners = append ( app . h3listeners , h3ln )
2019-09-11 19:49:21 -05:00
srv . h3server = h3srv
2019-09-10 09:03:37 -05:00
}
/////////
2019-04-25 14:54:48 -05:00
}
2019-03-26 16:45:51 -05:00
go s . Serve ( ln )
2019-05-16 17:05:38 -05:00
app . servers = append ( app . servers , s )
2019-03-26 16:45:51 -05:00
}
}
}
return nil
}
2019-05-07 10:56:13 -05:00
// Stop gracefully shuts down the HTTP server.
2019-05-16 17:05:38 -05:00
func ( app * App ) Stop ( ) error {
2019-05-07 10:56:13 -05:00
ctx := context . Background ( )
2019-05-16 17:05:38 -05:00
if app . GracePeriod > 0 {
2019-05-07 10:56:13 -05:00
var cancel context . CancelFunc
2019-05-16 17:05:38 -05:00
ctx , cancel = context . WithTimeout ( ctx , time . Duration ( app . GracePeriod ) )
2019-05-07 10:56:13 -05:00
defer cancel ( )
}
2019-05-16 17:05:38 -05:00
for _ , s := range app . servers {
2019-05-07 10:56:13 -05:00
err := s . Shutdown ( ctx )
2019-03-26 20:42:52 -05:00
if err != nil {
return err
}
}
2019-09-10 09:03:37 -05:00
// TODO: Closing the http3.Server is the right thing to do,
// however, doing so sometimes causes connections from clients
// to fail after config reloads due to a bug that is yet
// unsolved: https://github.com/caddyserver/caddy/pull/2727
// for _, s := range app.h3servers {
// // TODO: CloseGracefully, once implemented upstream
// // (see https://github.com/lucas-clemente/quic-go/issues/2103)
// err := s.Close()
// if err != nil {
// return err
// }
// }
// as of September 2019, closing the http3.Server
// instances doesn't close their underlying listeners
// so we have todo that ourselves
// (see https://github.com/lucas-clemente/quic-go/issues/2103)
for _ , pc := range app . h3listeners {
err := pc . Close ( )
if err != nil {
return err
}
}
2019-03-26 20:42:52 -05:00
return nil
}
2019-05-16 17:05:38 -05:00
func ( app * App ) automaticHTTPS ( ) error {
tlsAppIface , err := app . ctx . App ( "tls" )
2019-04-29 10:22:00 -05:00
if err != nil {
return fmt . Errorf ( "getting tls app: %v" , err )
}
tlsApp := tlsAppIface . ( * caddytls . TLS )
2019-04-25 14:54:48 -05:00
2019-09-18 19:01:32 -05:00
// this map will store associations of HTTP listener
// addresses to the routes that do HTTP->HTTPS redirects
lnAddrRedirRoutes := make ( map [ string ] Route )
2019-05-07 10:56:13 -05:00
2019-10-14 12:29:36 -05:00
repl := caddy . NewReplacer ( )
2019-05-16 17:05:38 -05:00
for srvName , srv := range app . Servers {
2019-04-25 14:54:48 -05:00
srv . tlsApp = tlsApp
2019-06-26 11:49:32 -05:00
if srv . AutoHTTPS . Disabled {
2019-04-25 14:54:48 -05:00
continue
}
2019-05-22 15:14:26 -05:00
// skip if all listeners use the HTTP port
2019-09-18 19:01:32 -05:00
if ! srv . listenersUseAnyPortOtherThan ( app . httpPort ( ) ) {
2019-10-28 15:39:37 -05:00
app . logger . Info ( "server is only listening on the HTTP port, so no automatic HTTPS will be applied to this server" ,
zap . String ( "server_name" , srvName ) ,
zap . Int ( "http_port" , app . httpPort ( ) ) ,
)
2019-05-22 15:14:26 -05:00
continue
}
2019-04-29 10:22:00 -05:00
// find all qualifying domain names, de-duplicated
2019-04-25 14:54:48 -05:00
domainSet := make ( map [ string ] struct { } )
2019-10-28 15:39:37 -05:00
for routeIdx , route := range srv . Routes {
for matcherSetIdx , matcherSet := range route . MatcherSets {
for matcherIdx , m := range matcherSet {
2019-05-22 14:13:39 -05:00
if hm , ok := m . ( * MatchHost ) ; ok {
2019-10-28 15:39:37 -05:00
for hostMatcherIdx , d := range * hm {
d , err = repl . ReplaceOrErr ( d , true , false )
if err != nil {
return fmt . Errorf ( "%s: route %d, matcher set %d, matcher %d, host matcher %d: %v" ,
srvName , routeIdx , matcherSetIdx , matcherIdx , hostMatcherIdx , err )
}
2019-06-26 11:57:18 -05:00
if certmagic . HostQualifies ( d ) &&
! srv . AutoHTTPS . Skipped ( d , srv . AutoHTTPS . Skip ) {
2019-06-21 09:08:26 -05:00
domainSet [ d ] = struct { } { }
2019-05-22 14:13:39 -05:00
}
2019-04-25 14:54:48 -05:00
}
}
}
}
}
2019-04-29 10:22:00 -05:00
if len ( domainSet ) > 0 {
// marshal the domains into a slice
2019-06-26 11:57:18 -05:00
var domains , domainsForCerts [ ] string
2019-04-29 10:22:00 -05:00
for d := range domainSet {
domains = append ( domains , d )
2019-06-26 11:57:18 -05:00
if ! srv . AutoHTTPS . Skipped ( d , srv . AutoHTTPS . SkipCerts ) {
2019-08-09 13:05:47 -05:00
// if a certificate for this name is already loaded,
// don't obtain another one for it, unless we are
// supposed to ignore loaded certificates
if ! srv . AutoHTTPS . IgnoreLoadedCerts &&
2019-09-13 12:46:58 -05:00
len ( tlsApp . AllMatchingCertificates ( d ) ) > 0 {
2019-10-28 15:39:37 -05:00
app . logger . Info ( "skipping automatic certificate management because one or more matching certificates are already loaded" ,
zap . String ( "domain" , d ) ,
zap . String ( "server_name" , srvName ) ,
)
2019-08-09 13:05:47 -05:00
continue
}
2019-06-26 11:57:18 -05:00
domainsForCerts = append ( domainsForCerts , d )
}
2019-04-29 10:22:00 -05:00
}
2019-06-04 23:43:21 -05:00
// ensure that these certificates are managed properly;
// for example, it's implied that the HTTPPort should also
// be the port the HTTP challenge is solved on, and so
// for HTTPS port and TLS-ALPN challenge also - we need
// to tell the TLS app to manage these certs by honoring
// those port configurations
acmeManager := & caddytls . ACMEManagerMaker {
2019-09-30 10:07:43 -05:00
Challenges : & caddytls . ChallengesConfig {
HTTP : & caddytls . HTTPChallengeConfig {
2019-09-18 19:01:32 -05:00
AlternatePort : app . HTTPPort , // we specifically want the user-configured port, if any
2019-06-04 23:43:21 -05:00
} ,
2019-09-30 10:07:43 -05:00
TLSALPN : & caddytls . TLSALPNChallengeConfig {
2019-09-18 19:01:32 -05:00
AlternatePort : app . HTTPSPort , // we specifically want the user-configured port, if any
2019-06-04 23:43:21 -05:00
} ,
} ,
}
2019-09-30 10:07:43 -05:00
if tlsApp . Automation == nil {
tlsApp . Automation = new ( caddytls . AutomationConfig )
}
2019-06-04 23:43:21 -05:00
tlsApp . Automation . Policies = append ( tlsApp . Automation . Policies ,
caddytls . AutomationPolicy {
2019-06-26 11:57:18 -05:00
Hosts : domainsForCerts ,
2019-06-04 23:43:21 -05:00
Management : acmeManager ,
} )
2019-04-29 10:22:00 -05:00
// manage their certificates
2019-10-28 15:39:37 -05:00
app . logger . Info ( "enabling automatic TLS certificate management" ,
zap . Strings ( "domains" , domainsForCerts ) ,
)
2019-06-26 11:57:18 -05:00
err := tlsApp . Manage ( domainsForCerts )
2019-04-25 14:54:48 -05:00
if err != nil {
return fmt . Errorf ( "%s: managing certificate for %s: %s" , srvName , domains , err )
}
2019-04-29 10:22:00 -05:00
2019-09-03 10:35:36 -05:00
// tell the server to use TLS if it is not already doing so
if srv . TLSConnPolicies == nil {
srv . TLSConnPolicies = caddytls . ConnectionPolicies {
& caddytls . ConnectionPolicy { ALPN : defaultALPN } ,
}
2019-04-25 14:54:48 -05:00
}
2019-04-29 10:22:00 -05:00
2019-06-26 11:49:32 -05:00
if srv . AutoHTTPS . DisableRedir {
2019-05-07 10:56:13 -05:00
continue
}
2019-10-28 15:39:37 -05:00
app . logger . Info ( "enabling automatic HTTP->HTTPS redirects" ,
zap . Strings ( "domains" , domains ) ,
)
2019-06-26 11:57:18 -05:00
2019-05-07 10:56:13 -05:00
// create HTTP->HTTPS redirects
for _ , addr := range srv . Listen {
2019-09-05 14:14:39 -05:00
netw , host , port , err := caddy . SplitNetworkAddress ( addr )
2019-05-07 10:56:13 -05:00
if err != nil {
return fmt . Errorf ( "%s: invalid listener address: %v" , srvName , addr )
}
2019-06-21 15:36:26 -05:00
2019-05-07 10:56:13 -05:00
if parts := strings . SplitN ( port , "-" , 2 ) ; len ( parts ) == 2 {
port = parts [ 0 ]
}
2019-05-20 18:15:38 -05:00
redirTo := "https://{http.request.host}"
2019-05-07 10:56:13 -05:00
2019-09-18 19:01:32 -05:00
if port != strconv . Itoa ( app . httpsPort ( ) ) {
2019-05-07 10:56:13 -05:00
redirTo += ":" + port
}
2019-05-20 18:15:38 -05:00
redirTo += "{http.request.uri}"
2019-05-07 10:56:13 -05:00
2019-09-18 19:01:32 -05:00
// build the plaintext HTTP variant of this address
httpRedirLnAddr := caddy . JoinNetworkAddress ( netw , host , strconv . Itoa ( app . httpPort ( ) ) )
// create the route that does the redirect and associate
// it with the listener address it will be served from
lnAddrRedirRoutes [ httpRedirLnAddr ] = Route {
2019-08-21 11:46:35 -05:00
MatcherSets : [ ] MatcherSet {
2019-05-22 14:13:39 -05:00
{
MatchProtocol ( "http" ) ,
MatchHost ( domains ) ,
} ,
2019-05-07 10:56:13 -05:00
} ,
2019-08-21 11:46:35 -05:00
Handlers : [ ] MiddlewareHandler {
2019-07-11 18:02:57 -05:00
StaticResponse {
2019-08-09 13:05:47 -05:00
StatusCode : WeakString ( strconv . Itoa ( http . StatusTemporaryRedirect ) ) , // TODO: use permanent redirect instead
2019-07-09 13:58:39 -05:00
Headers : http . Header {
"Location" : [ ] string { redirTo } ,
"Connection" : [ ] string { "close" } ,
} ,
Close : true ,
2019-05-07 10:56:13 -05:00
} ,
} ,
2019-09-18 19:01:32 -05:00
}
2019-05-07 10:56:13 -05:00
}
}
}
2019-09-18 19:01:32 -05:00
// if there are HTTP->HTTPS redirects to add, do so now
if len ( lnAddrRedirRoutes ) > 0 {
var redirServerAddrs [ ] string
var redirRoutes [ ] Route
// for each redirect listener, see if there's already a
// server configured to listen on that exact address; if
// so, simply the redirect route to the end of its route
// list; otherwise, we'll create a new server for all the
// listener addresses that are unused and serve the
// remaining redirects from it
redirRoutesLoop :
for addr , redirRoute := range lnAddrRedirRoutes {
for srvName , srv := range app . Servers {
if srv . hasListenerAddress ( addr ) {
// user has configured a server for the same address
// that the redirect runs from; simply append our
// redirect route to the existing routes, with a
// caveat that their config might override ours
2019-10-28 15:39:37 -05:00
app . logger . Warn ( "server is listening on same interface as redirects, so automatic HTTP->HTTPS redirects might be overridden by your own configuration" ,
zap . String ( "server_name" , srvName ) ,
zap . String ( "interface" , addr ) ,
)
2019-09-18 19:01:32 -05:00
srv . Routes = append ( srv . Routes , redirRoute )
continue redirRoutesLoop
2019-05-07 10:56:13 -05:00
}
}
2019-09-18 19:01:32 -05:00
// no server with this listener address exists;
// save this address and route for custom server
redirServerAddrs = append ( redirServerAddrs , addr )
redirRoutes = append ( redirRoutes , redirRoute )
2019-05-07 10:56:13 -05:00
}
2019-09-18 19:01:32 -05:00
// if there are routes remaining which do not belong
// in any existing server, make our own to serve the
// rest of the redirects
if len ( redirServerAddrs ) > 0 {
app . Servers [ "remaining_auto_https_redirects" ] = & Server {
Listen : redirServerAddrs ,
Routes : redirRoutes ,
tlsApp : tlsApp , // required to solve HTTP challenge
}
2019-04-25 14:54:48 -05:00
}
}
return nil
}
2019-09-18 19:01:32 -05:00
func ( app * App ) httpPort ( ) int {
if app . HTTPPort == 0 {
return DefaultHTTPPort
}
return app . HTTPPort
}
func ( app * App ) httpsPort ( ) int {
if app . HTTPSPort == 0 {
return DefaultHTTPSPort
2019-05-07 10:56:13 -05:00
}
2019-09-18 19:01:32 -05:00
return app . HTTPSPort
2019-05-07 10:56:13 -05:00
}
2019-04-25 14:54:48 -05:00
var defaultALPN = [ ] string { "h2" , "http/1.1" }
2019-05-20 11:59:20 -05:00
// RequestMatcher is a type that can match to a request.
2019-07-09 13:58:39 -05:00
// A route matcher MUST NOT modify the request, with the
// only exception being its context.
2019-05-20 11:59:20 -05:00
type RequestMatcher interface {
2019-03-31 21:41:29 -05:00
Match ( * http . Request ) bool
}
// Handler is like http.Handler except ServeHTTP may return an error.
//
// If any handler encounters an error, it should be returned for proper
// handling. Return values should be propagated down the middleware chain
2019-07-09 13:58:39 -05:00
// by returning it unchanged. Returned errors should not be re-wrapped
// if they are already HandlerError values.
2019-03-31 21:41:29 -05:00
type Handler interface {
ServeHTTP ( http . ResponseWriter , * http . Request ) error
}
// HandlerFunc is a convenience type like http.HandlerFunc.
type HandlerFunc func ( http . ResponseWriter , * http . Request ) error
// ServeHTTP implements the Handler interface.
func ( f HandlerFunc ) ServeHTTP ( w http . ResponseWriter , r * http . Request ) error {
return f ( w , r )
}
2019-07-09 13:58:39 -05:00
// Middleware chains one Handler to the next by being passed
// the next Handler in the chain.
type Middleware func ( HandlerFunc ) HandlerFunc
// MiddlewareHandler is like Handler except it takes as a third
// argument the next handler in the chain. The next handler will
// never be nil, but may be a no-op handler if this is the last
// handler in the chain. Handlers which act as middleware should
// call the next handler's ServeHTTP method so as to propagate
// the request down the chain properly. Handlers which act as
// responders (content origins) need not invoke the next handler,
// since the last handler in the chain should be the first to
// write the response.
type MiddlewareHandler interface {
ServeHTTP ( http . ResponseWriter , * http . Request , Handler ) error
}
// emptyHandler is used as a no-op handler.
var emptyHandler HandlerFunc = func ( http . ResponseWriter , * http . Request ) error { return nil }
2019-04-11 21:42:55 -05:00
2019-08-09 13:05:47 -05:00
// WeakString is a type that unmarshals any JSON value
// as a string literal, with the following exceptions:
2019-10-28 15:39:37 -05:00
// 1) actual string values are decoded as strings; and
// 2) null is decoded as empty string;
2019-08-09 13:05:47 -05:00
// and provides methods for getting the value as various
// primitive types. However, using this type removes any
// type safety as far as deserializing JSON is concerned.
type WeakString string
// UnmarshalJSON satisfies json.Unmarshaler according to
// this type's documentation.
func ( ws * WeakString ) UnmarshalJSON ( b [ ] byte ) error {
if len ( b ) == 0 {
return io . EOF
}
if b [ 0 ] == byte ( '"' ) && b [ len ( b ) - 1 ] == byte ( '"' ) {
var s string
err := json . Unmarshal ( b , & s )
if err != nil {
return err
}
* ws = WeakString ( s )
return nil
}
if bytes . Equal ( b , [ ] byte ( "null" ) ) {
return nil
}
* ws = WeakString ( b )
return nil
}
// MarshalJSON marshals was a boolean if true or false,
// a number if an integer, or a string otherwise.
func ( ws WeakString ) MarshalJSON ( ) ( [ ] byte , error ) {
if ws == "true" {
return [ ] byte ( "true" ) , nil
}
if ws == "false" {
return [ ] byte ( "false" ) , nil
}
if num , err := strconv . Atoi ( string ( ws ) ) ; err == nil {
return json . Marshal ( num )
}
return json . Marshal ( string ( ws ) )
}
// Int returns ws as an integer. If ws is not an
// integer, 0 is returned.
func ( ws WeakString ) Int ( ) int {
num , _ := strconv . Atoi ( string ( ws ) )
return num
}
// Float64 returns ws as a float64. If ws is not a
// float value, the zero value is returned.
func ( ws WeakString ) Float64 ( ) float64 {
num , _ := strconv . ParseFloat ( string ( ws ) , 64 )
return num
}
// Bool returns ws as a boolean. If ws is not a
// boolean, false is returned.
func ( ws WeakString ) Bool ( ) bool {
return string ( ws ) == "true"
}
// String returns ws as a string.
func ( ws WeakString ) String ( ) string {
return string ( ws )
}
2019-10-15 15:07:10 -05:00
// CopyHeader copies HTTP headers by completely
// replacing dest with src. (This allows deletions
// to be propagated, assuming src started as a
// consistent copy of dest.)
func CopyHeader ( dest , src http . Header ) {
for field := range dest {
delete ( dest , field )
}
for field , val := range src {
dest [ field ] = val
}
}
2019-09-02 23:01:02 -05:00
// StatusCodeMatches returns true if a real HTTP status code matches
// the configured status code, which may be either a real HTTP status
// code or an integer representing a class of codes (e.g. 4 for all
// 4xx statuses).
func StatusCodeMatches ( actual , configured int ) bool {
if actual == configured {
return true
}
if configured < 100 && actual >= configured * 100 && actual < ( configured + 1 ) * 100 {
return true
}
return false
}
2019-05-07 10:56:13 -05:00
const (
// DefaultHTTPPort is the default port for HTTP.
DefaultHTTPPort = 80
// DefaultHTTPSPort is the default port for HTTPS.
DefaultHTTPSPort = 443
)
2019-07-11 23:02:47 -05:00
// DefaultMaxRehandles is the maximum number of rehandles to
// allow, if not specified explicitly.
var DefaultMaxRehandles = 3
2019-07-07 15:12:22 -05:00
// Interface guards
var (
_ caddy . App = ( * App ) ( nil )
_ caddy . Provisioner = ( * App ) ( nil )
_ caddy . Validator = ( * App ) ( nil )
)