2019-03-26 16:45:51 -05:00
|
|
|
package caddy2
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net"
|
2019-03-26 20:42:52 -05:00
|
|
|
"sync"
|
2019-03-26 16:45:51 -05:00
|
|
|
"sync/atomic"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Listen returns a listener suitable for use in a Caddy module.
|
2019-03-31 21:41:29 -05:00
|
|
|
func Listen(network, addr string) (net.Listener, error) {
|
|
|
|
lnKey := network + "/" + addr
|
2019-03-26 20:42:52 -05:00
|
|
|
|
|
|
|
listenersMu.Lock()
|
|
|
|
defer listenersMu.Unlock()
|
|
|
|
|
|
|
|
// if listener already exists, return it
|
|
|
|
if ln, ok := listeners[lnKey]; ok {
|
|
|
|
return &fakeCloseListener{Listener: ln}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// or, create new one and save it
|
2019-03-31 21:41:29 -05:00
|
|
|
ln, err := net.Listen(network, addr)
|
2019-03-26 16:45:51 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-03-26 20:42:52 -05:00
|
|
|
listeners[lnKey] = ln
|
|
|
|
|
2019-03-26 16:45:51 -05:00
|
|
|
return &fakeCloseListener{Listener: ln}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// fakeCloseListener's Close() method is a no-op. This allows
|
|
|
|
// stopping servers that are using the listener without giving
|
|
|
|
// up the socket; thus, servers become hot-swappable while the
|
|
|
|
// listener remains running. Listeners should be re-wrapped in
|
|
|
|
// a new fakeCloseListener each time the listener is reused.
|
|
|
|
type fakeCloseListener struct {
|
|
|
|
closed int32
|
|
|
|
net.Listener
|
|
|
|
}
|
|
|
|
|
|
|
|
// Accept accepts connections until Close() is called.
|
|
|
|
func (fcl *fakeCloseListener) Accept() (net.Conn, error) {
|
|
|
|
if atomic.LoadInt32(&fcl.closed) == 1 {
|
|
|
|
return nil, ErrSwappingServers
|
|
|
|
}
|
|
|
|
return fcl.Listener.Accept()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close stops accepting new connections, but does not
|
|
|
|
// actually close the underlying listener.
|
|
|
|
func (fcl *fakeCloseListener) Close() error {
|
|
|
|
atomic.StoreInt32(&fcl.closed, 1)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// CloseUnderlying actually closes the underlying listener.
|
|
|
|
func (fcl *fakeCloseListener) CloseUnderlying() error {
|
|
|
|
return fcl.Listener.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
// ErrSwappingServers is returned by fakeCloseListener when
|
|
|
|
// Close() is called, indicating that it is pretending to
|
|
|
|
// be closed so that the server using it can terminate.
|
|
|
|
var ErrSwappingServers = fmt.Errorf("listener 'closed' 😉")
|
2019-03-26 20:42:52 -05:00
|
|
|
|
|
|
|
var (
|
|
|
|
listeners = make(map[string]net.Listener)
|
|
|
|
listenersMu sync.Mutex
|
|
|
|
)
|