pkg/socketactivation/activation.go
Michael Crosby fb500991ec Add socket activation for go apps
Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)
2014-02-15 20:18:47 -08:00

61 lines
1.6 KiB
Go

/*
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")
}