| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002 |
- // Copyright 2015 Light Code Labs, LLC
- //
- // 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 caddy implements the Caddy server manager.
- //
- // To use this package:
- //
- // 1. Set the AppName and AppVersion variables.
- // 2. Call LoadCaddyfile() to get the Caddyfile.
- // Pass in the name of the server type (like "http").
- // Make sure the server type's package is imported
- // (import _ "github.com/mholt/caddy/caddyhttp").
- // 3. Call caddy.Start() to start Caddy. You get back
- // an Instance, on which you can call Restart() to
- // restart it or Stop() to stop it.
- //
- // You should call Wait() on your instance to wait for
- // all servers to quit before your process exits.
- package caddy
- import (
- "bytes"
- "encoding/gob"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "net"
- "os"
- "strconv"
- "strings"
- "sync"
- "time"
- "github.com/mholt/caddy/caddyfile"
- )
- // Configurable application parameters
- var (
- // AppName is the name of the application.
- AppName string
- // AppVersion is the version of the application.
- AppVersion string
- // Quiet mode will not show any informative output on initialization.
- Quiet bool
- // PidFile is the path to the pidfile to create.
- PidFile string
- // GracefulTimeout is the maximum duration of a graceful shutdown.
- GracefulTimeout time.Duration
- // isUpgrade will be set to true if this process
- // was started as part of an upgrade, where a parent
- // Caddy process started this one.
- isUpgrade = os.Getenv("CADDY__UPGRADE") == "1"
- // started will be set to true when the first
- // instance is started; it never gets set to
- // false after that.
- started bool
- // mu protects the variables 'isUpgrade' and 'started'.
- mu sync.Mutex
- )
- func init() {
- OnProcessExit = append(OnProcessExit, func() {
- if PidFile != "" {
- os.Remove(PidFile)
- }
- })
- }
- // Instance contains the state of servers created as a result of
- // calling Start and can be used to access or control those servers.
- // It is literally an instance of a server type. Instance values
- // should NOT be copied. Use *Instance for safety.
- type Instance struct {
- // serverType is the name of the instance's server type
- serverType string
- // caddyfileInput is the input configuration text used for this process
- caddyfileInput Input
- // wg is used to wait for all servers to shut down
- wg *sync.WaitGroup
- // context is the context created for this instance,
- // used to coordinate the setting up of the server type
- context Context
- // servers is the list of servers with their listeners
- servers []ServerListener
- // these callbacks execute when certain events occur
- onFirstStartup []func() error // starting, not as part of a restart
- onStartup []func() error // starting, even as part of a restart
- onRestart []func() error // before restart commences
- onShutdown []func() error // stopping, even as part of a restart
- onFinalShutdown []func() error // stopping, not as part of a restart
- // storing values on an instance is preferable to
- // global state because these will get garbage-
- // collected after in-process reloads when the
- // old instances are destroyed; use StorageMu
- // to access this value safely
- Storage map[interface{}]interface{}
- StorageMu sync.RWMutex
- }
- func Instances() []*Instance {
- return instances
- }
- // Servers returns the ServerListeners in i.
- func (i *Instance) Servers() []ServerListener { return i.servers }
- // Stop stops all servers contained in i. It does NOT
- // execute shutdown callbacks.
- func (i *Instance) Stop() error {
- // stop the servers
- for _, s := range i.servers {
- if gs, ok := s.server.(GracefulServer); ok {
- if err := gs.Stop(); err != nil {
- log.Printf("[ERROR] Stopping %s: %v", gs.Address(), err)
- }
- }
- }
- // splice i out of instance list, causing it to be garbage-collected
- instancesMu.Lock()
- for j, other := range instances {
- if other == i {
- instances = append(instances[:j], instances[j+1:]...)
- break
- }
- }
- instancesMu.Unlock()
- return nil
- }
- // ShutdownCallbacks executes all the shutdown callbacks of i,
- // including ones that are scheduled only for the final shutdown
- // of i. An error returned from one does not stop execution of
- // the rest. All the non-nil errors will be returned.
- func (i *Instance) ShutdownCallbacks() []error {
- var errs []error
- for _, shutdownFunc := range i.onShutdown {
- err := shutdownFunc()
- if err != nil {
- errs = append(errs, err)
- }
- }
- for _, finalShutdownFunc := range i.onFinalShutdown {
- err := finalShutdownFunc()
- if err != nil {
- errs = append(errs, err)
- }
- }
- return errs
- }
- // Restart replaces the servers in i with new servers created from
- // executing the newCaddyfile. Upon success, it returns the new
- // instance to replace i. Upon failure, i will not be replaced.
- func (i *Instance) Restart(newCaddyfile Input) (*Instance, error) {
- log.Println("[INFO] Reloading")
- i.wg.Add(1)
- defer i.wg.Done()
- // run restart callbacks
- for _, fn := range i.onRestart {
- err := fn()
- if err != nil {
- return i, err
- }
- }
- if newCaddyfile == nil {
- newCaddyfile = i.caddyfileInput
- }
- // Add file descriptors of all the sockets that are capable of it
- restartFds := make(map[string]restartTriple)
- for _, s := range i.servers {
- gs, srvOk := s.server.(GracefulServer)
- ln, lnOk := s.listener.(Listener)
- pc, pcOk := s.packet.(PacketConn)
- if srvOk {
- if lnOk && pcOk {
- restartFds[gs.Address()] = restartTriple{server: gs, listener: ln, packet: pc}
- continue
- }
- if lnOk {
- restartFds[gs.Address()] = restartTriple{server: gs, listener: ln}
- continue
- }
- if pcOk {
- restartFds[gs.Address()] = restartTriple{server: gs, packet: pc}
- continue
- }
- }
- }
- // create new instance; if the restart fails, it is simply discarded
- newInst := &Instance{serverType: newCaddyfile.ServerType(), wg: i.wg, Storage: make(map[interface{}]interface{})}
- // attempt to start new instance
- err := startWithListenerFds(newCaddyfile, newInst, restartFds)
- if err != nil {
- return i, err
- }
- // success! stop the old instance
- for _, shutdownFunc := range i.onShutdown {
- err = shutdownFunc()
- if err != nil {
- return i, err
- }
- }
- err = i.Stop()
- if err != nil {
- return i, err
- }
- // Execute instantiation events
- EmitEvent(InstanceStartupEvent, newInst)
- log.Println("[INFO] Reloading complete")
- return newInst, nil
- }
- // SaveServer adds s and its associated listener ln to the
- // internally-kept list of servers that is running. For
- // saved servers, graceful restarts will be provided.
- func (i *Instance) SaveServer(s Server, ln net.Listener) {
- i.servers = append(i.servers, ServerListener{server: s, listener: ln})
- }
- // HasListenerWithAddress returns whether this package is
- // tracking a server using a listener with the address
- // addr.
- func HasListenerWithAddress(addr string) bool {
- instancesMu.Lock()
- defer instancesMu.Unlock()
- for _, inst := range instances {
- for _, sln := range inst.servers {
- if listenerAddrEqual(sln.listener, addr) {
- return true
- }
- }
- }
- return false
- }
- // listenerAddrEqual compares a listener's address with
- // addr. Extra care is taken to match addresses with an
- // empty hostname portion, as listeners tend to report
- // [::]:80, for example, when the matching address that
- // created the listener might be simply :80.
- func listenerAddrEqual(ln net.Listener, addr string) bool {
- lnAddr := ln.Addr().String()
- hostname, port, err := net.SplitHostPort(addr)
- if err != nil {
- return lnAddr == addr
- }
- if lnAddr == net.JoinHostPort("::", port) {
- return true
- }
- if lnAddr == net.JoinHostPort("0.0.0.0", port) {
- return true
- }
- return hostname != "" && lnAddr == addr
- }
- // TCPServer is a type that can listen and serve connections.
- // A TCPServer must associate with exactly zero or one net.Listeners.
- type TCPServer interface {
- // Listen starts listening by creating a new listener
- // and returning it. It does not start accepting
- // connections. For UDP-only servers, this method
- // can be a no-op that returns (nil, nil).
- Listen() (net.Listener, error)
- // Serve starts serving using the provided listener.
- // Serve must start the server loop nearly immediately,
- // or at least not return any errors before the server
- // loop begins. Serve blocks indefinitely, or in other
- // words, until the server is stopped. For UDP-only
- // servers, this method can be a no-op that returns nil.
- Serve(net.Listener) error
- }
- // UDPServer is a type that can listen and serve packets.
- // A UDPServer must associate with exactly zero or one net.PacketConns.
- type UDPServer interface {
- // ListenPacket starts listening by creating a new packetconn
- // and returning it. It does not start accepting connections.
- // TCP-only servers may leave this method blank and return
- // (nil, nil).
- ListenPacket() (net.PacketConn, error)
- // ServePacket starts serving using the provided packetconn.
- // ServePacket must start the server loop nearly immediately,
- // or at least not return any errors before the server
- // loop begins. ServePacket blocks indefinitely, or in other
- // words, until the server is stopped. For TCP-only servers,
- // this method can be a no-op that returns nil.
- ServePacket(net.PacketConn) error
- }
- // Server is a type that can listen and serve. It supports both
- // TCP and UDP, although the UDPServer interface can be used
- // for more than just UDP.
- //
- // If the server uses TCP, it should implement TCPServer completely.
- // If it uses UDP or some other protocol, it should implement
- // UDPServer completely. If it uses both, both interfaces should be
- // fully implemented. Any unimplemented methods should be made as
- // no-ops that simply return nil values.
- type Server interface {
- TCPServer
- UDPServer
- }
- // Stopper is a type that can stop serving. The stop
- // does not necessarily have to be graceful.
- type Stopper interface {
- // Stop stops the server. It blocks until the
- // server is completely stopped.
- Stop() error
- }
- // GracefulServer is a Server and Stopper, the stopping
- // of which is graceful (whatever that means for the kind
- // of server being implemented). It must be able to return
- // the address it is configured to listen on so that its
- // listener can be paired with it upon graceful restarts.
- // The net.Listener that a GracefulServer creates must
- // implement the Listener interface for restarts to be
- // graceful (assuming the listener is for TCP).
- type GracefulServer interface {
- Server
- Stopper
- // Address returns the address the server should
- // listen on; it is used to pair the server to
- // its listener during a graceful/zero-downtime
- // restart. Thus when implementing this method,
- // you must not access a listener to get the
- // address; you must store the address the
- // server is to serve on some other way.
- Address() string
- }
- // Listener is a net.Listener with an underlying file descriptor.
- // A server's listener should implement this interface if it is
- // to support zero-downtime reloads.
- type Listener interface {
- net.Listener
- File() (*os.File, error)
- }
- // PacketConn is a net.PacketConn with an underlying file descriptor.
- // A server's packetconn should implement this interface if it is
- // to support zero-downtime reloads (in sofar this holds true for datagram
- // connections).
- type PacketConn interface {
- net.PacketConn
- File() (*os.File, error)
- }
- // AfterStartup is an interface that can be implemented
- // by a server type that wants to run some code after all
- // servers for the same Instance have started.
- type AfterStartup interface {
- OnStartupComplete()
- }
- // LoadCaddyfile loads a Caddyfile by calling the plugged in
- // Caddyfile loader methods. An error is returned if more than
- // one loader returns a non-nil Caddyfile input. If no loaders
- // load a Caddyfile, the default loader is used. If no default
- // loader is registered or it returns nil, the server type's
- // default Caddyfile is loaded. If the server type does not
- // specify any default Caddyfile value, then an empty Caddyfile
- // is returned. Consequently, this function never returns a nil
- // value as long as there are no errors.
- func LoadCaddyfile(serverType string) (Input, error) {
- // If we are finishing an upgrade, we must obtain the Caddyfile
- // from our parent process, regardless of configured loaders.
- if IsUpgrade() {
- err := gob.NewDecoder(os.Stdin).Decode(&loadedGob)
- if err != nil {
- return nil, err
- }
- return loadedGob.Caddyfile, nil
- }
- // Ask plugged-in loaders for a Caddyfile
- cdyfile, err := loadCaddyfileInput(serverType)
- if err != nil {
- return nil, err
- }
- // Otherwise revert to default
- if cdyfile == nil {
- cdyfile = DefaultInput(serverType)
- }
- // Still nil? Geez.
- if cdyfile == nil {
- cdyfile = CaddyfileInput{ServerTypeName: serverType}
- }
- return cdyfile, nil
- }
- // Wait blocks until all of i's servers have stopped.
- func (i *Instance) Wait() {
- i.wg.Wait()
- }
- // CaddyfileFromPipe loads the Caddyfile input from f if f is
- // not interactive input. f is assumed to be a pipe or stream,
- // such as os.Stdin. If f is not a pipe, no error is returned
- // but the Input value will be nil. An error is only returned
- // if there was an error reading the pipe, even if the length
- // of what was read is 0.
- func CaddyfileFromPipe(f *os.File, serverType string) (Input, error) {
- fi, err := f.Stat()
- if err == nil && fi.Mode()&os.ModeCharDevice == 0 {
- // Note that a non-nil error is not a problem. Windows
- // will not create a stdin if there is no pipe, which
- // produces an error when calling Stat(). But Unix will
- // make one either way, which is why we also check that
- // bitmask.
- // NOTE: Reading from stdin after this fails (e.g. for the let's encrypt email address) (OS X)
- confBody, err := ioutil.ReadAll(f)
- if err != nil {
- return nil, err
- }
- return CaddyfileInput{
- Contents: confBody,
- Filepath: f.Name(),
- ServerTypeName: serverType,
- }, nil
- }
- // not having input from the pipe is not itself an error,
- // just means no input to return.
- return nil, nil
- }
- // Caddyfile returns the Caddyfile used to create i.
- func (i *Instance) Caddyfile() Input {
- return i.caddyfileInput
- }
- // Start starts Caddy with the given Caddyfile.
- //
- // This function blocks until all the servers are listening.
- func Start(cdyfile Input) (*Instance, error) {
- inst := &Instance{serverType: cdyfile.ServerType(), wg: new(sync.WaitGroup), Storage: make(map[interface{}]interface{})}
- err := startWithListenerFds(cdyfile, inst, nil)
- if err != nil {
- return inst, err
- }
- signalSuccessToParent()
- if pidErr := writePidFile(); pidErr != nil {
- log.Printf("[ERROR] Could not write pidfile: %v", pidErr)
- }
- return inst, nil
- }
- func startWithListenerFds(cdyfile Input, inst *Instance, restartFds map[string]restartTriple) error {
- // save this instance in the list now so that
- // plugins can access it if need be, for example
- // the caddytls package, so it can perform cert
- // renewals while starting up; we just have to
- // remove the instance from the list later if
- // it fails
- instancesMu.Lock()
- instances = append(instances, inst)
- instancesMu.Unlock()
- var err error
- defer func() {
- if err != nil {
- instancesMu.Lock()
- for i, otherInst := range instances {
- if otherInst == inst {
- instances = append(instances[:i], instances[i+1:]...)
- break
- }
- }
- instancesMu.Unlock()
- }
- }()
- if cdyfile == nil {
- cdyfile = CaddyfileInput{}
- }
- err = ValidateAndExecuteDirectives(cdyfile, inst, false)
- if err != nil {
- return err
- }
- slist, err := inst.context.MakeServers()
- if err != nil {
- return err
- }
- // run startup callbacks
- if !IsUpgrade() && restartFds == nil {
- // first startup means not a restart or upgrade
- for _, firstStartupFunc := range inst.onFirstStartup {
- err = firstStartupFunc()
- if err != nil {
- return err
- }
- }
- }
- for _, startupFunc := range inst.onStartup {
- err = startupFunc()
- if err != nil {
- return err
- }
- }
- err = startServers(slist, inst, restartFds)
- if err != nil {
- return err
- }
- // run any AfterStartup callbacks if this is not
- // part of a restart; then show file descriptor notice
- if restartFds == nil {
- for _, srvln := range inst.servers {
- if srv, ok := srvln.server.(AfterStartup); ok {
- srv.OnStartupComplete()
- }
- }
- if !Quiet {
- for _, srvln := range inst.servers {
- // only show FD notice if the listener is not nil.
- // This can happen when only serving UDP or TCP
- if srvln.listener == nil {
- continue
- }
- if !IsLoopback(srvln.listener.Addr().String()) {
- checkFdlimit()
- break
- }
- }
- }
- }
- mu.Lock()
- started = true
- mu.Unlock()
- return nil
- }
- // ValidateAndExecuteDirectives will load the server blocks from cdyfile
- // by parsing it, then execute the directives configured by it and store
- // the resulting server blocks into inst. If justValidate is true, parse
- // callbacks will not be executed between directives, since the purpose
- // is only to check the input for valid syntax.
- func ValidateAndExecuteDirectives(cdyfile Input, inst *Instance, justValidate bool) error {
- // If parsing only inst will be nil, create an instance for this function call only.
- if justValidate {
- inst = &Instance{serverType: cdyfile.ServerType(), wg: new(sync.WaitGroup), Storage: make(map[interface{}]interface{})}
- }
- stypeName := cdyfile.ServerType()
- stype, err := getServerType(stypeName)
- if err != nil {
- return err
- }
- inst.caddyfileInput = cdyfile
- sblocks, err := loadServerBlocks(stypeName, cdyfile.Path(), bytes.NewReader(cdyfile.Body()))
- if err != nil {
- return err
- }
- inst.context = stype.NewContext(inst)
- if inst.context == nil {
- return fmt.Errorf("server type %s produced a nil Context", stypeName)
- }
- sblocks, err = inst.context.InspectServerBlocks(cdyfile.Path(), sblocks)
- if err != nil {
- return fmt.Errorf("error inspecting server blocks: %v", err)
- }
- return executeDirectives(inst, cdyfile.Path(), stype.Directives(), sblocks, justValidate)
- }
- func executeDirectives(inst *Instance, filename string,
- directives []string, sblocks []caddyfile.ServerBlock, justValidate bool) error {
- // map of server block ID to map of directive name to whatever.
- storages := make(map[int]map[string]interface{})
- // It is crucial that directives are executed in the proper order.
- // We loop with the directives on the outer loop so we execute
- // a directive for all server blocks before going to the next directive.
- // This is important mainly due to the parsing callbacks (below).
- for _, dir := range directives {
- for i, sb := range sblocks {
- var once sync.Once
- if _, ok := storages[i]; !ok {
- storages[i] = make(map[string]interface{})
- }
- for j, key := range sb.Keys {
- // Execute directive if it is in the server block
- if tokens, ok := sb.Tokens[dir]; ok {
- controller := &Controller{
- instance: inst,
- Key: key,
- Dispenser: caddyfile.NewDispenserTokens(filename, tokens),
- OncePerServerBlock: func(f func() error) error {
- var err error
- once.Do(func() {
- err = f()
- })
- return err
- },
- ServerBlockIndex: i,
- ServerBlockKeyIndex: j,
- ServerBlockKeys: sb.Keys,
- ServerBlockStorage: storages[i][dir],
- }
- setup, err := DirectiveAction(inst.serverType, dir)
- if err != nil {
- return err
- }
- err = setup(controller)
- if err != nil {
- return err
- }
- storages[i][dir] = controller.ServerBlockStorage // persist for this server block
- }
- }
- }
- if !justValidate {
- // See if there are any callbacks to execute after this directive
- if allCallbacks, ok := parsingCallbacks[inst.serverType]; ok {
- callbacks := allCallbacks[dir]
- for _, callback := range callbacks {
- if err := callback(inst.context); err != nil {
- return err
- }
- }
- }
- }
- }
- return nil
- }
- func startServers(serverList []Server, inst *Instance, restartFds map[string]restartTriple) error {
- errChan := make(chan error, len(serverList))
- for _, s := range serverList {
- var (
- ln net.Listener
- pc net.PacketConn
- err error
- )
- // if performing an upgrade, obtain listener file descriptors
- // from parent process
- if IsUpgrade() {
- if gs, ok := s.(GracefulServer); ok {
- addr := gs.Address()
- if fdIndex, ok := loadedGob.ListenerFds["tcp"+addr]; ok {
- file := os.NewFile(fdIndex, "")
- ln, err = net.FileListener(file)
- if err != nil {
- return err
- }
- err = file.Close()
- if err != nil {
- return err
- }
- }
- if fdIndex, ok := loadedGob.ListenerFds["udp"+addr]; ok {
- file := os.NewFile(fdIndex, "")
- pc, err = net.FilePacketConn(file)
- if err != nil {
- return err
- }
- err = file.Close()
- if err != nil {
- return err
- }
- }
- }
- }
- // If this is a reload and s is a GracefulServer,
- // reuse the listener for a graceful restart.
- if gs, ok := s.(GracefulServer); ok && restartFds != nil {
- addr := gs.Address()
- if old, ok := restartFds[addr]; ok {
- // listener
- if old.listener != nil {
- file, err := old.listener.File()
- if err != nil {
- return err
- }
- ln, err = net.FileListener(file)
- if err != nil {
- return err
- }
- err = file.Close()
- if err != nil {
- return err
- }
- }
- // packetconn
- if old.packet != nil {
- file, err := old.packet.File()
- if err != nil {
- return err
- }
- pc, err = net.FilePacketConn(file)
- if err != nil {
- return err
- }
- err = file.Close()
- if err != nil {
- return err
- }
- }
- }
- }
- if ln == nil {
- ln, err = s.Listen()
- if err != nil {
- return err
- }
- }
- if pc == nil {
- pc, err = s.ListenPacket()
- if err != nil {
- return err
- }
- }
- inst.wg.Add(2)
- go func(s Server, ln net.Listener, pc net.PacketConn, inst *Instance) {
- defer inst.wg.Done()
- go func() {
- errChan <- s.Serve(ln)
- defer inst.wg.Done()
- }()
- errChan <- s.ServePacket(pc)
- }(s, ln, pc, inst)
- inst.servers = append(inst.servers, ServerListener{server: s, listener: ln, packet: pc})
- }
- // Log errors that may be returned from Serve() calls,
- // these errors should only be occurring in the server loop.
- go func() {
- for err := range errChan {
- if err == nil {
- continue
- }
- if strings.Contains(err.Error(), "use of closed network connection") {
- // this error is normal when closing the listener
- continue
- }
- log.Println(err)
- }
- }()
- return nil
- }
- func getServerType(serverType string) (ServerType, error) {
- stype, ok := serverTypes[serverType]
- if ok {
- return stype, nil
- }
- if len(serverTypes) == 0 {
- return ServerType{}, fmt.Errorf("no server types plugged in")
- }
- if serverType == "" {
- if len(serverTypes) == 1 {
- for _, stype := range serverTypes {
- return stype, nil
- }
- }
- return ServerType{}, fmt.Errorf("multiple server types available; must choose one")
- }
- return ServerType{}, fmt.Errorf("unknown server type '%s'", serverType)
- }
- func loadServerBlocks(serverType, filename string, input io.Reader) ([]caddyfile.ServerBlock, error) {
- validDirectives := ValidDirectives(serverType)
- serverBlocks, err := caddyfile.Parse(filename, input, validDirectives)
- if err != nil {
- return nil, err
- }
- if len(serverBlocks) == 0 && serverTypes[serverType].DefaultInput != nil {
- newInput := serverTypes[serverType].DefaultInput()
- serverBlocks, err = caddyfile.Parse(newInput.Path(),
- bytes.NewReader(newInput.Body()), validDirectives)
- if err != nil {
- return nil, err
- }
- }
- return serverBlocks, nil
- }
- // Stop stops ALL servers. It blocks until they are all stopped.
- // It does NOT execute shutdown callbacks, and it deletes all
- // instances after stopping is completed. Do not re-use any
- // references to old instances after calling Stop.
- func Stop() error {
- // This awkward for loop is to avoid a deadlock since
- // inst.Stop() also acquires the instancesMu lock.
- for {
- instancesMu.Lock()
- if len(instances) == 0 {
- break
- }
- inst := instances[0]
- instancesMu.Unlock()
- if err := inst.Stop(); err != nil {
- log.Printf("[ERROR] Stopping %s: %v", inst.serverType, err)
- }
- }
- return nil
- }
- // IsLoopback returns true if the hostname of addr looks
- // explicitly like a common local hostname. addr must only
- // be a host or a host:port combination.
- func IsLoopback(addr string) bool {
- host, _, err := net.SplitHostPort(addr)
- if err != nil {
- host = addr // happens if the addr is just a hostname
- }
- return host == "localhost" ||
- strings.Trim(host, "[]") == "::1" ||
- strings.HasPrefix(host, "127.")
- }
- // IsInternal returns true if the IP of addr
- // belongs to a private network IP range. addr must only
- // be an IP or an IP:port combination.
- // Loopback addresses are considered false.
- func IsInternal(addr string) bool {
- privateNetworks := []string{
- "10.0.0.0/8",
- "172.16.0.0/12",
- "192.168.0.0/16",
- "fc00::/7",
- }
- host, _, err := net.SplitHostPort(addr)
- if err != nil {
- host = addr // happens if the addr is just a hostname, missing port
- // if we encounter an error, the brackets need to be stripped
- // because SplitHostPort didn't do it for us
- host = strings.Trim(host, "[]")
- }
- ip := net.ParseIP(host)
- if ip == nil {
- return false
- }
- for _, privateNetwork := range privateNetworks {
- _, ipnet, _ := net.ParseCIDR(privateNetwork)
- if ipnet.Contains(ip) {
- return true
- }
- }
- return false
- }
- // Started returns true if at least one instance has been
- // started by this package. It never gets reset to false
- // once it is set to true.
- func Started() bool {
- mu.Lock()
- defer mu.Unlock()
- return started
- }
- // CaddyfileInput represents a Caddyfile as input
- // and is simply a convenient way to implement
- // the Input interface.
- type CaddyfileInput struct {
- Filepath string
- Contents []byte
- ServerTypeName string
- }
- // Body returns c.Contents.
- func (c CaddyfileInput) Body() []byte { return c.Contents }
- // Path returns c.Filepath.
- func (c CaddyfileInput) Path() string { return c.Filepath }
- // ServerType returns c.ServerType.
- func (c CaddyfileInput) ServerType() string { return c.ServerTypeName }
- // Input represents a Caddyfile; its contents and file path
- // (which should include the file name at the end of the path).
- // If path does not apply (e.g. piped input) you may use
- // any understandable value. The path is mainly used for logging,
- // error messages, and debugging.
- type Input interface {
- // Gets the Caddyfile contents
- Body() []byte
- // Gets the path to the origin file
- Path() string
- // The type of server this input is intended for
- ServerType() string
- }
- // DefaultInput returns the default Caddyfile input
- // to use when it is otherwise empty or missing.
- // It uses the default host and port (depends on
- // host, e.g. localhost is 2015, otherwise 443) and
- // root.
- func DefaultInput(serverType string) Input {
- if _, ok := serverTypes[serverType]; !ok {
- return nil
- }
- if serverTypes[serverType].DefaultInput == nil {
- return nil
- }
- return serverTypes[serverType].DefaultInput()
- }
- // writePidFile writes the process ID to the file at PidFile.
- // It does nothing if PidFile is not set.
- func writePidFile() error {
- if PidFile == "" {
- return nil
- }
- pid := []byte(strconv.Itoa(os.Getpid()) + "\n")
- return ioutil.WriteFile(PidFile, pid, 0644)
- }
- type restartTriple struct {
- server GracefulServer
- listener Listener
- packet PacketConn
- }
- var (
- // instances is the list of running Instances.
- instances []*Instance
- // instancesMu protects instances.
- instancesMu sync.Mutex
- )
- var (
- // DefaultConfigFile is the name of the configuration file that is loaded
- // by default if no other file is specified.
- DefaultConfigFile = "Caddyfile"
- )
- // CtxKey is a value type for use with context.WithValue.
- type CtxKey string
|