From fb500991ec0443dc3d4d74520c558137d10c4e3e Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Sat, 15 Feb 2014 20:02:54 -0800 Subject: [PATCH] Add socket activation for go apps Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- socketactivation/activation.go | 61 ++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 socketactivation/activation.go diff --git a/socketactivation/activation.go b/socketactivation/activation.go new file mode 100644 index 0000000..0edbcaa --- /dev/null +++ b/socketactivation/activation.go @@ -0,0 +1,61 @@ +/* + Package to allow go applications to immediately start + listening on a socket, unix, tcp, udp but hold connections + until the application has booted and is ready to accept them +*/ +package socketactivation + +import ( + "fmt" + "net" + "time" +) + +// NewActivationListener returns a listener listening on addr with the protocol. It sets the +// timeout to wait on first connection before an error is returned +func NewActivationListener(proto, addr string, activate chan struct{}, timeout time.Duration) (net.Listener, error) { + wrapped, err := net.Listen(proto, addr) + if err != nil { + return nil, err + } + + return &defaultListener{ + wrapped: wrapped, + activate: activate, + timeout: timeout, + }, nil +} + +type defaultListener struct { + wrapped net.Listener // the real listener to wrap + ready bool // is the listner ready to start accpeting connections + activate chan struct{} + timeout time.Duration // how long to wait before we consider this an error +} + +func (l *defaultListener) Close() error { + return l.wrapped.Close() +} + +func (l *defaultListener) Addr() net.Addr { + return l.wrapped.Addr() +} + +func (l *defaultListener) Accept() (net.Conn, error) { + // if the listen has been told it is ready then we can go ahead and + // start returning connections + if l.ready { + return l.wrapped.Accept() + } + + select { + case <-time.After(l.timeout): + // close the connection so any clients are disconnected + l.Close() + return nil, fmt.Errorf("timeout (%s) reached waiting for listener to become ready", l.timeout.String()) + case <-l.activate: + l.ready = true + return l.Accept() + } + panic("unreachable") +}