| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | // Package caddy implements the Caddy web server as a service
 | 
					
						
							|  |  |  | // in your own Go programs.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // To use this package, follow a few simple steps:
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //   1. Set the AppName and AppVersion variables.
 | 
					
						
							|  |  |  | //   2. Call LoadCaddyfile() to get the Caddyfile (it
 | 
					
						
							|  |  |  | //      might have been piped in as part of a restart).
 | 
					
						
							|  |  |  | //      You should pass in your own Caddyfile loader.
 | 
					
						
							|  |  |  | //   3. Call caddy.Start() to start Caddy, caddy.Stop()
 | 
					
						
							|  |  |  | //      to stop it, or caddy.Restart() to restart it.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // You should use caddy.Wait() to wait for all Caddy servers
 | 
					
						
							|  |  |  | // to quit before your process exits.
 | 
					
						
							|  |  |  | package core
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import (
 | 
					
						
							|  |  |  | 	"bytes"
 | 
					
						
							|  |  |  | 	"encoding/gob"
 | 
					
						
							|  |  |  | 	"errors"
 | 
					
						
							|  |  |  | 	"fmt"
 | 
					
						
							|  |  |  | 	"io/ioutil"
 | 
					
						
							|  |  |  | 	"log"
 | 
					
						
							|  |  |  | 	"os"
 | 
					
						
							|  |  |  | 	"path"
 | 
					
						
							|  |  |  | 	"strings"
 | 
					
						
							|  |  |  | 	"sync"
 | 
					
						
							|  |  |  | 	"sync/atomic"
 | 
					
						
							| 
									
										
										
										
											2016-04-10 18:50:11 +01:00
										 |  |  | 	"testing"
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 	"time"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/miekg/coredns/core/https"
 | 
					
						
							|  |  |  | 	"github.com/miekg/coredns/server"
 | 
					
						
							|  |  |  | )
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Configurable application parameters
 | 
					
						
							|  |  |  | var (
 | 
					
						
							|  |  |  | 	// AppName is the name of the application.
 | 
					
						
							|  |  |  | 	AppName string
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// AppVersion is the version of the application.
 | 
					
						
							|  |  |  | 	AppVersion string
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Quiet when set to true, 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
 | 
					
						
							|  |  |  | )
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var (
 | 
					
						
							|  |  |  | 	// caddyfile is the input configuration text used for this process
 | 
					
						
							|  |  |  | 	caddyfile Input
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// caddyfileMu protects caddyfile during changes
 | 
					
						
							|  |  |  | 	caddyfileMu sync.Mutex
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// errIncompleteRestart occurs if this process is a fork
 | 
					
						
							|  |  |  | 	// of the parent but no Caddyfile was piped in
 | 
					
						
							|  |  |  | 	errIncompleteRestart = errors.New("incomplete restart")
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// servers is a list of all the currently-listening servers
 | 
					
						
							|  |  |  | 	servers []*server.Server
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// serversMu protects the servers slice during changes
 | 
					
						
							|  |  |  | 	serversMu sync.Mutex
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// wg is used to wait for all servers to shut down
 | 
					
						
							|  |  |  | 	wg sync.WaitGroup
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// loadedGob is used if this is a child process as part of
 | 
					
						
							|  |  |  | 	// a graceful restart; it is used to map listeners to their
 | 
					
						
							|  |  |  | 	// index in the list of inherited file descriptors. This
 | 
					
						
							|  |  |  | 	// variable is not safe for concurrent access.
 | 
					
						
							|  |  |  | 	loadedGob caddyfileGob
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// startedBefore should be set to true if caddy has been started
 | 
					
						
							|  |  |  | 	// at least once (does not indicate whether currently running).
 | 
					
						
							|  |  |  | 	startedBefore bool
 | 
					
						
							|  |  |  | )
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const (
 | 
					
						
							|  |  |  | 	// DefaultHost is the default host.
 | 
					
						
							|  |  |  | 	DefaultHost = ""
 | 
					
						
							|  |  |  | 	// DefaultPort is the default port.
 | 
					
						
							|  |  |  | 	DefaultPort = "53"
 | 
					
						
							|  |  |  | 	// DefaultRoot is the default root folder.
 | 
					
						
							|  |  |  | 	DefaultRoot = "."
 | 
					
						
							|  |  |  | )
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Start starts Caddy with the given Caddyfile. If cdyfile
 | 
					
						
							|  |  |  | // is nil, the LoadCaddyfile function will be called to get
 | 
					
						
							|  |  |  | // one.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This function blocks until all the servers are listening.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Note (POSIX): If Start is called in the child process of a
 | 
					
						
							|  |  |  | // restart more than once within the duration of the graceful
 | 
					
						
							|  |  |  | // cutoff (i.e. the child process called Start a first time,
 | 
					
						
							|  |  |  | // then called Stop, then Start again within the first 5 seconds
 | 
					
						
							|  |  |  | // or however long GracefulTimeout is) and the Caddyfiles have
 | 
					
						
							|  |  |  | // at least one listener address in common, the second Start
 | 
					
						
							|  |  |  | // may fail with "address already in use" as there's no
 | 
					
						
							|  |  |  | // guarantee that the parent process has relinquished the
 | 
					
						
							|  |  |  | // address before the grace period ends.
 | 
					
						
							|  |  |  | func Start(cdyfile Input) (err error) {
 | 
					
						
							|  |  |  | 	// If we return with no errors, we must do two things: tell the
 | 
					
						
							|  |  |  | 	// parent that we succeeded and write to the pidfile.
 | 
					
						
							|  |  |  | 	defer func() {
 | 
					
						
							|  |  |  | 		if err == nil {
 | 
					
						
							|  |  |  | 			signalSuccessToParent() // TODO: Is doing this more than once per process a bad idea? Start could get called more than once in other apps.
 | 
					
						
							|  |  |  | 			if PidFile != "" {
 | 
					
						
							|  |  |  | 				err := writePidFile()
 | 
					
						
							|  |  |  | 				if err != nil {
 | 
					
						
							|  |  |  | 					log.Printf("[ERROR] Could not write pidfile: %v", err)
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}()
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Input must never be nil; try to load something
 | 
					
						
							|  |  |  | 	if cdyfile == nil {
 | 
					
						
							|  |  |  | 		cdyfile, err = LoadCaddyfile(nil)
 | 
					
						
							|  |  |  | 		if err != nil {
 | 
					
						
							|  |  |  | 			return err
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	caddyfileMu.Lock()
 | 
					
						
							|  |  |  | 	caddyfile = cdyfile
 | 
					
						
							|  |  |  | 	caddyfileMu.Unlock()
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// load the server configs (activates Let's Encrypt)
 | 
					
						
							|  |  |  | 	configs, err := loadConfigs(path.Base(cdyfile.Path()), bytes.NewReader(cdyfile.Body()))
 | 
					
						
							|  |  |  | 	if err != nil {
 | 
					
						
							|  |  |  | 		return err
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// group zones by address
 | 
					
						
							|  |  |  | 	groupings, err := arrangeBindings(configs)
 | 
					
						
							|  |  |  | 	if err != nil {
 | 
					
						
							|  |  |  | 		return err
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Start each server with its one or more configurations
 | 
					
						
							|  |  |  | 	err = startServers(groupings)
 | 
					
						
							|  |  |  | 	if err != nil {
 | 
					
						
							|  |  |  | 		return err
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	startedBefore = true
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Show initialization output
 | 
					
						
							|  |  |  | 	if !Quiet && !IsRestart() {
 | 
					
						
							|  |  |  | 		var checkedFdLimit bool
 | 
					
						
							|  |  |  | 		for _, group := range groupings {
 | 
					
						
							|  |  |  | 			for _, conf := range group.Configs {
 | 
					
						
							|  |  |  | 				// Print address of site
 | 
					
						
							|  |  |  | 				fmt.Println(conf.Address())
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// Note if non-localhost site resolves to loopback interface
 | 
					
						
							|  |  |  | 				if group.BindAddr.IP.IsLoopback() && !isLocalhost(conf.Host) {
 | 
					
						
							|  |  |  | 					fmt.Printf("Notice: %s is only accessible on this machine (%s)\n",
 | 
					
						
							|  |  |  | 						conf.Host, group.BindAddr.IP.String())
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 				if !checkedFdLimit && !group.BindAddr.IP.IsLoopback() && !isLocalhost(conf.Host) {
 | 
					
						
							|  |  |  | 					checkFdlimit()
 | 
					
						
							|  |  |  | 					checkedFdLimit = true
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // startServers starts all the servers in groupings,
 | 
					
						
							|  |  |  | // taking into account whether or not this process is
 | 
					
						
							|  |  |  | // a child from a graceful restart or not. It blocks
 | 
					
						
							|  |  |  | // until the servers are listening.
 | 
					
						
							|  |  |  | func startServers(groupings bindingGroup) error {
 | 
					
						
							|  |  |  | 	var startupWg sync.WaitGroup
 | 
					
						
							|  |  |  | 	errChan := make(chan error, len(groupings)) // must be buffered to allow Serve functions below to return if stopped later
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, group := range groupings {
 | 
					
						
							|  |  |  | 		s, err := server.New(group.BindAddr.String(), group.Configs, GracefulTimeout)
 | 
					
						
							|  |  |  | 		if err != nil {
 | 
					
						
							|  |  |  | 			return err
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		// TODO(miek): does not work, because this callback uses http instead of dns
 | 
					
						
							|  |  |  | 		//		s.ReqCallback = https.RequestCallback // ensures we can solve ACME challenges while running
 | 
					
						
							|  |  |  | 		if s.OnDemandTLS {
 | 
					
						
							|  |  |  | 			s.TLSConfig.GetCertificate = https.GetOrObtainCertificate // TLS on demand -- awesome!
 | 
					
						
							|  |  |  | 		} else {
 | 
					
						
							|  |  |  | 			s.TLSConfig.GetCertificate = https.GetCertificate
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		var ln server.ListenerFile
 | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 			if IsRestart() {
 | 
					
						
							|  |  |  | 				// Look up this server's listener in the map of inherited file descriptors;
 | 
					
						
							|  |  |  | 				// if we don't have one, we must make a new one (later).
 | 
					
						
							|  |  |  | 				if fdIndex, ok := loadedGob.ListenerFds[s.Addr]; ok {
 | 
					
						
							|  |  |  | 					file := os.NewFile(fdIndex, "")
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					fln, err := net.FileListener(file)
 | 
					
						
							|  |  |  | 					if err != nil {
 | 
					
						
							|  |  |  | 						return err
 | 
					
						
							|  |  |  | 					}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					ln, ok = fln.(server.ListenerFile)
 | 
					
						
							|  |  |  | 					if !ok {
 | 
					
						
							|  |  |  | 						return errors.New("listener for " + s.Addr + " was not a ListenerFile")
 | 
					
						
							|  |  |  | 					}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					file.Close()
 | 
					
						
							|  |  |  | 					delete(loadedGob.ListenerFds, s.Addr)
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 		*/
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		wg.Add(1)
 | 
					
						
							|  |  |  | 		go func(s *server.Server, ln server.ListenerFile) {
 | 
					
						
							|  |  |  | 			defer wg.Done()
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// run startup functions that should only execute when
 | 
					
						
							|  |  |  | 			// the original parent process is starting.
 | 
					
						
							|  |  |  | 			if !IsRestart() && !startedBefore {
 | 
					
						
							|  |  |  | 				err := s.RunFirstStartupFuncs()
 | 
					
						
							|  |  |  | 				if err != nil {
 | 
					
						
							|  |  |  | 					errChan <- err
 | 
					
						
							|  |  |  | 					return
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// start the server
 | 
					
						
							|  |  |  | 			// TODO(miek): for now will always be nil, so we will run ListenAndServe()
 | 
					
						
							| 
									
										
										
										
											2016-04-10 18:50:11 +01:00
										 |  |  | 			// TODO(miek): this is also why graceful restarts don't work.
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 			if ln != nil {
 | 
					
						
							|  |  |  | 				//errChan <- s.Serve(ln)
 | 
					
						
							|  |  |  | 			} else {
 | 
					
						
							|  |  |  | 				errChan <- s.ListenAndServe()
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 		}(s, ln)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		startupWg.Add(1)
 | 
					
						
							|  |  |  | 		go func(s *server.Server) {
 | 
					
						
							|  |  |  | 			defer startupWg.Done()
 | 
					
						
							|  |  |  | 			s.WaitUntilStarted()
 | 
					
						
							|  |  |  | 		}(s)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		serversMu.Lock()
 | 
					
						
							|  |  |  | 		servers = append(servers, s)
 | 
					
						
							|  |  |  | 		serversMu.Unlock()
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Close the remaining (unused) file descriptors to free up resources
 | 
					
						
							|  |  |  | 	if IsRestart() {
 | 
					
						
							|  |  |  | 		for key, fdIndex := range loadedGob.ListenerFds {
 | 
					
						
							|  |  |  | 			os.NewFile(fdIndex, "").Close()
 | 
					
						
							|  |  |  | 			delete(loadedGob.ListenerFds, key)
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Wait for all servers to finish starting
 | 
					
						
							|  |  |  | 	startupWg.Wait()
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Return the first error, if any
 | 
					
						
							|  |  |  | 	select {
 | 
					
						
							|  |  |  | 	case err := <-errChan:
 | 
					
						
							|  |  |  | 		// "use of closed network connection" is normal if it was a graceful shutdown
 | 
					
						
							|  |  |  | 		if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
 | 
					
						
							|  |  |  | 			return err
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	default:
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Stop stops all servers. It blocks until they are all stopped.
 | 
					
						
							|  |  |  | // It does NOT execute shutdown callbacks that may have been
 | 
					
						
							|  |  |  | // configured by middleware (they must be executed separately).
 | 
					
						
							|  |  |  | func Stop() error {
 | 
					
						
							|  |  |  | 	https.Deactivate()
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	serversMu.Lock()
 | 
					
						
							|  |  |  | 	for _, s := range servers {
 | 
					
						
							|  |  |  | 		if err := s.Stop(); err != nil {
 | 
					
						
							|  |  |  | 			log.Printf("[ERROR] Stopping %s: %v", s.Addr, err)
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	servers = []*server.Server{} // don't reuse servers
 | 
					
						
							|  |  |  | 	serversMu.Unlock()
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Wait blocks until all servers are stopped.
 | 
					
						
							|  |  |  | func Wait() {
 | 
					
						
							|  |  |  | 	wg.Wait()
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // LoadCaddyfile loads a Caddyfile, prioritizing a Caddyfile
 | 
					
						
							|  |  |  | // piped from stdin as part of a restart (only happens on first call
 | 
					
						
							|  |  |  | // to LoadCaddyfile). If it is not a restart, this function tries
 | 
					
						
							|  |  |  | // calling the user's loader function, and if that returns nil, then
 | 
					
						
							|  |  |  | // this function resorts to the default configuration. Thus, if there
 | 
					
						
							|  |  |  | // are no other errors, this function always returns at least the
 | 
					
						
							|  |  |  | // default Caddyfile.
 | 
					
						
							|  |  |  | func LoadCaddyfile(loader func() (Input, error)) (cdyfile Input, err error) {
 | 
					
						
							|  |  |  | 	// If we are a fork, finishing the restart is highest priority;
 | 
					
						
							|  |  |  | 	// piped input is required in this case.
 | 
					
						
							|  |  |  | 	if IsRestart() {
 | 
					
						
							|  |  |  | 		err := gob.NewDecoder(os.Stdin).Decode(&loadedGob)
 | 
					
						
							|  |  |  | 		if err != nil {
 | 
					
						
							|  |  |  | 			return nil, err
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		cdyfile = loadedGob.Caddyfile
 | 
					
						
							|  |  |  | 		atomic.StoreInt32(https.OnDemandIssuedCount, loadedGob.OnDemandTLSCertsIssued)
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Try user's loader
 | 
					
						
							|  |  |  | 	if cdyfile == nil && loader != nil {
 | 
					
						
							|  |  |  | 		cdyfile, err = loader()
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Otherwise revert to default
 | 
					
						
							|  |  |  | 	if cdyfile == nil {
 | 
					
						
							|  |  |  | 		cdyfile = DefaultInput()
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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) (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.
 | 
					
						
							|  |  |  | 		// BUG: 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(),
 | 
					
						
							|  |  |  | 		}, nil
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// not having input from the pipe is not itself an error,
 | 
					
						
							|  |  |  | 	// just means no input to return.
 | 
					
						
							|  |  |  | 	return nil, nil
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Caddyfile returns the current Caddyfile
 | 
					
						
							|  |  |  | func Caddyfile() Input {
 | 
					
						
							|  |  |  | 	caddyfileMu.Lock()
 | 
					
						
							|  |  |  | 	defer caddyfileMu.Unlock()
 | 
					
						
							|  |  |  | 	return caddyfile
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// IsFile returns true if the original input was a file on the file system
 | 
					
						
							|  |  |  | 	// that could be loaded again later if requested.
 | 
					
						
							|  |  |  | 	IsFile() bool
 | 
					
						
							|  |  |  | }
 | 
					
						
							| 
									
										
										
										
											2016-04-10 18:50:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | // TestServer returns a test server.
 | 
					
						
							| 
									
										
										
										
											2016-04-13 23:23:35 +01:00
										 |  |  | // The ports can be retreived with server.LocalAddr(). The testserver itself can be stopped
 | 
					
						
							|  |  |  | // with Stop(). It just takes a normal Corefile as input.
 | 
					
						
							| 
									
										
										
										
											2016-04-10 18:50:11 +01:00
										 |  |  | func TestServer(t *testing.T, corefile string) (*server.Server, error) {
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cdyfile := CaddyfileInput{Contents: []byte(corefile)}
 | 
					
						
							|  |  |  | 	configs, err := loadConfigs(path.Base(cdyfile.Path()), bytes.NewReader(cdyfile.Body()))
 | 
					
						
							|  |  |  | 	if err != nil {
 | 
					
						
							|  |  |  | 		return nil, err
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	groupings, err := arrangeBindings(configs)
 | 
					
						
							|  |  |  | 	if err != nil {
 | 
					
						
							|  |  |  | 		return nil, err
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	t.Logf("Starting %d servers", len(groupings))
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	group := groupings[0]
 | 
					
						
							|  |  |  | 	s, err := server.New(group.BindAddr.String(), group.Configs, time.Second)
 | 
					
						
							|  |  |  | 	return s, err
 | 
					
						
							|  |  |  | }
 |