Browse Source

Add socket activation for go apps
Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)

Michael Crosby 11 years ago
parent
commit
cfb7711a74
1 changed files with 61 additions and 0 deletions
  1. 61 0
      pkg/socketactivation/activation.go

+ 61 - 0
pkg/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")
+}