mirror of
https://github.com/caddyserver/caddy.git
synced 2025-05-23 02:29:59 +08:00
New startup and shutdown directives
This commit is contained in:
@ -14,6 +14,51 @@ const (
|
|||||||
defaultRoot = "."
|
defaultRoot = "."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// config represents a server configuration. It
|
||||||
|
// is populated by parsing a config file (via the
|
||||||
|
// Load function).
|
||||||
|
type Config struct {
|
||||||
|
// The hostname or IP to which to bind the server
|
||||||
|
Host string
|
||||||
|
|
||||||
|
// The port to listen on
|
||||||
|
Port string
|
||||||
|
|
||||||
|
// The directory from which to serve files
|
||||||
|
Root string
|
||||||
|
|
||||||
|
// HTTPS configuration
|
||||||
|
TLS TLSConfig
|
||||||
|
|
||||||
|
// Middleware stack
|
||||||
|
Middleware map[string][]middleware.Middleware
|
||||||
|
|
||||||
|
// Functions (or methods) to execute at server start; these
|
||||||
|
// are executed before any parts of the server are configured,
|
||||||
|
// and the functions are blocking
|
||||||
|
Startup []func() error
|
||||||
|
|
||||||
|
// Functions (or methods) to execute when the server quits;
|
||||||
|
// these are executed in response to SIGINT and are blocking
|
||||||
|
Shutdown []func() error
|
||||||
|
|
||||||
|
// MaxCPU is the maximum number of cores for the whole process to use
|
||||||
|
MaxCPU int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Address returns the host:port of c as a string.
|
||||||
|
func (c Config) Address() string {
|
||||||
|
return c.Host + ":" + c.Port
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLSConfig describes how TLS should be configured and used,
|
||||||
|
// if at all. A certificate and key are both required.
|
||||||
|
type TLSConfig struct {
|
||||||
|
Enabled bool
|
||||||
|
Certificate string
|
||||||
|
Key string
|
||||||
|
}
|
||||||
|
|
||||||
// Load loads a configuration file, parses it,
|
// Load loads a configuration file, parses it,
|
||||||
// and returns a slice of Config structs which
|
// and returns a slice of Config structs which
|
||||||
// can be used to create and configure server
|
// can be used to create and configure server
|
||||||
@ -54,43 +99,3 @@ func Default() []Config {
|
|||||||
}
|
}
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
// config represents a server configuration. It
|
|
||||||
// is populated by parsing a config file (via the
|
|
||||||
// Load function).
|
|
||||||
type Config struct {
|
|
||||||
// The hostname or IP to which to bind the server
|
|
||||||
Host string
|
|
||||||
|
|
||||||
// The port to listen on
|
|
||||||
Port string
|
|
||||||
|
|
||||||
// The directory from which to serve files
|
|
||||||
Root string
|
|
||||||
|
|
||||||
// HTTPS configuration
|
|
||||||
TLS TLSConfig
|
|
||||||
|
|
||||||
// Middleware stack
|
|
||||||
Middleware map[string][]middleware.Middleware
|
|
||||||
|
|
||||||
// Functions (or methods) to execute at server start; these
|
|
||||||
// are executed before any parts of the server are configured
|
|
||||||
Startup []func() error
|
|
||||||
|
|
||||||
// MaxCPU is the maximum number of cores for the whole process to use
|
|
||||||
MaxCPU int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Address returns the host:port of c as a string.
|
|
||||||
func (c Config) Address() string {
|
|
||||||
return c.Host + ":" + c.Port
|
|
||||||
}
|
|
||||||
|
|
||||||
// TLSConfig describes how TLS should be configured and used,
|
|
||||||
// if at all. At least a certificate and key are required.
|
|
||||||
type TLSConfig struct {
|
|
||||||
Enabled bool
|
|
||||||
Certificate string
|
|
||||||
Key string
|
|
||||||
}
|
|
||||||
|
@ -2,9 +2,12 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mholt/caddy/middleware"
|
||||||
)
|
)
|
||||||
|
|
||||||
// dirFunc is a type of parsing function which processes
|
// dirFunc is a type of parsing function which processes
|
||||||
@ -111,5 +114,55 @@ func init() {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
"startup": func(p *parser) error {
|
||||||
|
// TODO: This code is duplicated with the shutdown directive below
|
||||||
|
|
||||||
|
if !p.nextArg() {
|
||||||
|
return p.argErr()
|
||||||
|
}
|
||||||
|
|
||||||
|
command, args, err := middleware.SplitCommandAndArgs(p.tkn())
|
||||||
|
if err != nil {
|
||||||
|
return p.err("Parse", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
startupfn := func() error {
|
||||||
|
cmd := exec.Command(command, args...)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
p.cfg.Startup = append(p.cfg.Startup, startupfn)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
"shutdown": func(p *parser) error {
|
||||||
|
if !p.nextArg() {
|
||||||
|
return p.argErr()
|
||||||
|
}
|
||||||
|
|
||||||
|
command, args, err := middleware.SplitCommandAndArgs(p.tkn())
|
||||||
|
if err != nil {
|
||||||
|
return p.err("Parse", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
shutdownfn := func() error {
|
||||||
|
cmd := exec.Command(command, args...)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
p.cfg.Shutdown = append(p.cfg.Shutdown, shutdownfn)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
27
middleware/commands.go
Normal file
27
middleware/commands.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/flynn/go-shlex"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SplitCommandAndArgs takes a command string and parses it
|
||||||
|
// shell-style into the command and its separate arguments.
|
||||||
|
func SplitCommandAndArgs(command string) (cmd string, args []string, err error) {
|
||||||
|
parts, err := shlex.Split(command)
|
||||||
|
if err != nil {
|
||||||
|
err = errors.New("Error parsing command: " + err.Error())
|
||||||
|
return
|
||||||
|
} else if len(parts) == 0 {
|
||||||
|
err = errors.New("No command contained in '" + command + "'")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = parts[0]
|
||||||
|
if len(parts) > 1 {
|
||||||
|
args = parts[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
@ -4,10 +4,8 @@
|
|||||||
package websockets
|
package websockets
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/flynn/go-shlex"
|
|
||||||
"github.com/mholt/caddy/middleware"
|
"github.com/mholt/caddy/middleware"
|
||||||
"golang.org/x/net/websocket"
|
"golang.org/x/net/websocket"
|
||||||
)
|
)
|
||||||
@ -101,7 +99,7 @@ func New(c middleware.Controller) (middleware.Middleware, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Split command into the actual command and its arguments
|
// Split command into the actual command and its arguments
|
||||||
cmd, args, err := parseCommandAndArgs(command)
|
cmd, args, err := middleware.SplitCommandAndArgs(command)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -122,26 +120,6 @@ func New(c middleware.Controller) (middleware.Middleware, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseCommandAndArgs takes a command string and parses it
|
|
||||||
// shell-style into the command and its separate arguments.
|
|
||||||
func parseCommandAndArgs(command string) (cmd string, args []string, err error) {
|
|
||||||
parts, err := shlex.Split(command)
|
|
||||||
if err != nil {
|
|
||||||
err = errors.New("Error parsing command for websocket: " + err.Error())
|
|
||||||
return
|
|
||||||
} else if len(parts) == 0 {
|
|
||||||
err = errors.New("No command found for use by websocket")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd = parts[0]
|
|
||||||
if len(parts) > 1 {
|
|
||||||
args = parts[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// See CGI spec, 4.1.4
|
// See CGI spec, 4.1.4
|
||||||
GatewayInterface string
|
GatewayInterface string
|
||||||
|
@ -7,6 +7,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/bradfitz/http2"
|
"github.com/bradfitz/http2"
|
||||||
@ -75,6 +77,21 @@ func (s *Server) Serve() error {
|
|||||||
|
|
||||||
http2.ConfigureServer(server, nil) // TODO: This may not be necessary after HTTP/2 merged into std lib
|
http2.ConfigureServer(server, nil) // TODO: This may not be necessary after HTTP/2 merged into std lib
|
||||||
|
|
||||||
|
// Execute shutdown commands on exit
|
||||||
|
// TODO: Is graceful net/http shutdown necessary?
|
||||||
|
go func() {
|
||||||
|
interrupt := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(interrupt, os.Interrupt, os.Kill) // TODO: syscall.SIGQUIT? (Ctrl+\, Unix-only)
|
||||||
|
<-interrupt
|
||||||
|
for _, shutdownFunc := range s.config.Shutdown {
|
||||||
|
err := shutdownFunc()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
}()
|
||||||
|
|
||||||
if s.config.TLS.Enabled {
|
if s.config.TLS.Enabled {
|
||||||
return server.ListenAndServeTLS(s.config.TLS.Certificate, s.config.TLS.Key)
|
return server.ListenAndServeTLS(s.config.TLS.Certificate, s.config.TLS.Key)
|
||||||
} else {
|
} else {
|
||||||
@ -103,6 +120,7 @@ func (s *Server) Log(v ...interface{}) {
|
|||||||
func (s *Server) buildStack() error {
|
func (s *Server) buildStack() error {
|
||||||
s.fileServer = FileServer(http.Dir(s.config.Root))
|
s.fileServer = FileServer(http.Dir(s.config.Root))
|
||||||
|
|
||||||
|
// Execute startup functions
|
||||||
for _, start := range s.config.Startup {
|
for _, start := range s.config.Startup {
|
||||||
err := start()
|
err := start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Reference in New Issue
Block a user