Commit 9315738d authored by Miek Gieben's avatar Miek Gieben Committed by Matt Holt

Allow for UDP servers (#935)

* Allow for UDP servers

Extend the Server interface with ServePacket and ListenPacket - this is
in the same vein as the net package.

Plumb the packetconn through the start and restart phases.

Rename RestartPair to RestartTriple as it now also contains a Packet.
Not that these can now be nil, so we need to check for that when
restarting.

* Update the documentation
parent 502a8979
...@@ -148,12 +148,24 @@ func (i *Instance) Restart(newCaddyfile Input) (*Instance, error) { ...@@ -148,12 +148,24 @@ func (i *Instance) Restart(newCaddyfile Input) (*Instance, error) {
} }
// Add file descriptors of all the sockets that are capable of it // Add file descriptors of all the sockets that are capable of it
restartFds := make(map[string]restartPair) restartFds := make(map[string]restartTriple)
for _, s := range i.servers { for _, s := range i.servers {
gs, srvOk := s.server.(GracefulServer) gs, srvOk := s.server.(GracefulServer)
ln, lnOk := s.listener.(Listener) ln, lnOk := s.listener.(Listener)
if srvOk && lnOk { pc, pcOk := s.packet.(PacketConn)
restartFds[gs.Address()] = restartPair{server: gs, listener: ln} 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
}
} }
} }
...@@ -223,42 +235,38 @@ func listenerAddrEqual(ln net.Listener, addr string) bool { ...@@ -223,42 +235,38 @@ func listenerAddrEqual(ln net.Listener, addr string) bool {
return false return false
} }
/*
// TODO: We should be able to support UDP servers... I'm considering this pattern.
type UDPListener struct {
*net.UDPConn
}
func (u UDPListener) Accept() (net.Conn, error) {
return u.UDPConn, nil
}
func (u UDPListener) Close() error {
return u.UDPConn.Close()
}
func (u UDPListener) Addr() net.Addr {
return u.UDPConn.LocalAddr()
}
var _ net.Listener = UDPListener{}
*/
// Server is a type that can listen and serve. A Server // Server is a type that can listen and serve. A Server
// must associate with exactly zero or one listeners. // must associate with exactly zero or one listeners and packetconns.
// The listed method "pair up", Listen() and Serve() are needed for a
// TCP server and ListenPacket() and ServePacket() are needed for a
// UDP server. Do both TCP and UDP means all four are needed.
type Server interface { type Server interface {
// Listen starts listening by creating a new listener // Listen starts listening by creating a new listener
// and returning it. It does not start accepting // and returning it. It does not start accepting
// connections. // connections.
Listen() (net.Listener, error) Listen() (net.Listener, error)
// ListenPacket starts listening by creating a new packetconn
// and returning it. It does not start accepting connections.
// For a TCP only server this method can be a noop and just
// return (nil, nil).
ListenPacket() (net.PacketConn, error)
// Serve starts serving using the provided listener. // Serve starts serving using the provided listener.
// Serve must start the server loop nearly immediately, // Serve must start the server loop nearly immediately,
// or at least not return any errors before the server // or at least not return any errors before the server
// loop begins. Serve blocks indefinitely, or in other // loop begins. Serve blocks indefinitely, or in other
// words, until the server is stopped. // words, until the server is stopped.
Serve(net.Listener) error Serve(net.Listener) 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 a TCP only server this method can be a noop and just
// return nil.
ServePacket(net.PacketConn) error
} }
// Stopper is a type that can stop serving. The stop // Stopper is a type that can stop serving. The stop
...@@ -299,6 +307,15 @@ type Listener interface { ...@@ -299,6 +307,15 @@ type Listener interface {
File() (*os.File, error) 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 // AfterStartup is an interface that can be implemented
// by a server type that wants to run some code after all // by a server type that wants to run some code after all
// servers for the same Instance have started. // servers for the same Instance have started.
...@@ -384,7 +401,7 @@ func Start(cdyfile Input) (*Instance, error) { ...@@ -384,7 +401,7 @@ func Start(cdyfile Input) (*Instance, error) {
return inst, startWithListenerFds(cdyfile, inst, nil) return inst, startWithListenerFds(cdyfile, inst, nil)
} }
func startWithListenerFds(cdyfile Input, inst *Instance, restartFds map[string]restartPair) error { func startWithListenerFds(cdyfile Input, inst *Instance, restartFds map[string]restartTriple) error {
if cdyfile == nil { if cdyfile == nil {
cdyfile = CaddyfileInput{} cdyfile = CaddyfileInput{}
} }
...@@ -535,18 +552,23 @@ func executeDirectives(inst *Instance, filename string, ...@@ -535,18 +552,23 @@ func executeDirectives(inst *Instance, filename string,
return nil return nil
} }
func startServers(serverList []Server, inst *Instance, restartFds map[string]restartPair) error { func startServers(serverList []Server, inst *Instance, restartFds map[string]restartTriple) error {
errChan := make(chan error, len(serverList)) errChan := make(chan error, len(serverList))
for _, s := range serverList { for _, s := range serverList {
var ln net.Listener var (
var err error ln net.Listener
pc net.PacketConn
err error
)
// If this is a reload and s is a GracefulServer, // If this is a reload and s is a GracefulServer,
// reuse the listener for a graceful restart. // reuse the listener for a graceful restart.
if gs, ok := s.(GracefulServer); ok && restartFds != nil { if gs, ok := s.(GracefulServer); ok && restartFds != nil {
addr := gs.Address() addr := gs.Address()
if old, ok := restartFds[addr]; ok { if old, ok := restartFds[addr]; ok {
// listener
if old.listener != nil {
file, err := old.listener.File() file, err := old.listener.File()
if err != nil { if err != nil {
return err return err
...@@ -557,6 +579,19 @@ func startServers(serverList []Server, inst *Instance, restartFds map[string]res ...@@ -557,6 +579,19 @@ func startServers(serverList []Server, inst *Instance, restartFds map[string]res
} }
file.Close() file.Close()
} }
// 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
}
file.Close()
}
}
} }
if ln == nil { if ln == nil {
...@@ -565,14 +600,25 @@ func startServers(serverList []Server, inst *Instance, restartFds map[string]res ...@@ -565,14 +600,25 @@ func startServers(serverList []Server, inst *Instance, restartFds map[string]res
return err return err
} }
} }
if pc == nil {
pc, err = s.ListenPacket()
if err != nil {
return err
}
}
inst.wg.Add(1) inst.wg.Add(2)
go func(s Server, ln net.Listener, inst *Instance) { go func(s Server, ln net.Listener, pc net.PacketConn, inst *Instance) {
defer inst.wg.Done() defer inst.wg.Done()
go func() {
errChan <- s.Serve(ln) errChan <- s.Serve(ln)
}(s, ln, inst) defer inst.wg.Done()
}()
errChan <- s.ServePacket(pc)
}(s, ln, pc, inst)
inst.servers = append(inst.servers, serverListener{server: s, listener: ln}) inst.servers = append(inst.servers, serverListener{server: s, listener: ln, packet: pc})
} }
// Log errors that may be returned from Serve() calls, // Log errors that may be returned from Serve() calls,
...@@ -752,9 +798,10 @@ func writePidFile() error { ...@@ -752,9 +798,10 @@ func writePidFile() error {
return ioutil.WriteFile(PidFile, pid, 0644) return ioutil.WriteFile(PidFile, pid, 0644)
} }
type restartPair struct { type restartTriple struct {
server GracefulServer server GracefulServer
listener Listener listener Listener
packet PacketConn
} }
var ( var (
......
...@@ -146,6 +146,9 @@ func (s *Server) Listen() (net.Listener, error) { ...@@ -146,6 +146,9 @@ func (s *Server) Listen() (net.Listener, error) {
return ln.(*net.TCPListener), nil return ln.(*net.TCPListener), nil
} }
// ListenPacket is a noop to implement the Server interface.
func (s *Server) ListenPacket() (net.PacketConn, error) { return nil, nil }
// Serve serves requests on ln. It blocks until ln is closed. // Serve serves requests on ln. It blocks until ln is closed.
func (s *Server) Serve(ln net.Listener) error { func (s *Server) Serve(ln net.Listener) error {
if tcpLn, ok := ln.(*net.TCPListener); ok { if tcpLn, ok := ln.(*net.TCPListener); ok {
...@@ -186,6 +189,9 @@ func (s *Server) Serve(ln net.Listener) error { ...@@ -186,6 +189,9 @@ func (s *Server) Serve(ln net.Listener) error {
return err return err
} }
// ServePacket is a noop to implement the Server interface.
func (s *Server) ServePacket(pc net.PacketConn) error { return nil }
// ServeHTTP is the entry point of all HTTP requests. // ServeHTTP is the entry point of all HTTP requests.
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
defer func() { defer func() {
......
...@@ -82,10 +82,11 @@ func ValidDirectives(serverType string) []string { ...@@ -82,10 +82,11 @@ func ValidDirectives(serverType string) []string {
return stype.Directives return stype.Directives
} }
// serverListener pairs a server to its listener. // serverListener pairs a server to its listener and/or packetconn.
type serverListener struct { type serverListener struct {
server Server server Server
listener net.Listener listener net.Listener
packet net.PacketConn
} }
// Context is a type which carries a server type through // Context is a type which carries a server type through
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment