Prechádzať zdrojové kódy

Merge pull request #11735 from Sirupsen/listenbuffer-promote

listenbuffer: add docs and test
Brian Goff 10 rokov pred
rodič
commit
7c2cc476da

+ 27 - 0
pkg/listenbuffer/README.md

@@ -0,0 +1,27 @@
+# listenbuffer
+
+listenbuffer uses the kernel's listening backlog functionality to queue
+connections, allowing applications to start listening immediately and handle
+connections later. This is signaled by closing the activation channel passed to
+the constructor.
+
+The maximum amount of queued connections depends on the configuration of your
+kernel (typically called SOMAXXCON) and cannot be configured in Go with the
+net package. See `src/net/sock_platform.go` in the Go tree or consult your
+kernel's manual.
+
+	activator := make(chan struct{})
+	buffer, err := NewListenBuffer("tcp", "localhost:4000", activator)
+	if err != nil {
+		panic(err)
+	}
+
+	// will block until activator has been closed or is sent an event
+	client, err := buffer.Accept()
+
+Somewhere else in your application once it's been booted:
+
+	close(activator)
+
+`buffer.Accept()` will return the first client in the kernel listening queue, or
+continue to block until a client connects or an error occurs.

+ 37 - 7
pkg/listenbuffer/buffer.go

@@ -1,13 +1,37 @@
 /*
 /*
-   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
+listenbuffer uses the kernel's listening backlog functionality to queue
+connections, allowing applications to start listening immediately and handle
+connections later. This is signaled by closing the activation channel passed to
+the constructor.
+
+The maximum amount of queued connections depends on the configuration of your
+kernel (typically called SOMAXXCON) and cannot be configured in Go with the
+net package. See `src/net/sock_platform.go` in the Go tree or consult your
+kernel's manual.
+
+	activator := make(chan struct{})
+	buffer, err := NewListenBuffer("tcp", "localhost:4000", activator)
+	if err != nil {
+		panic(err)
+	}
+
+	// will block until activator has been closed or is sent an event
+	client, err := buffer.Accept()
+
+Somewhere else in your application once it's been booted:
+
+	close(activator)
+
+`buffer.Accept()` will return the first client in the kernel listening queue, or
+continue to block until a client connects or an error occurs.
 */
 */
 package listenbuffer
 package listenbuffer
 
 
 import "net"
 import "net"
 
 
-// NewListenBuffer returns a listener listening on addr with the protocol.
+// NewListenBuffer returns a net.Listener listening on addr with the protocol
+// passed. The channel passed is used to activate the listenbuffer when the
+// caller is ready to accept connections.
 func NewListenBuffer(proto, addr string, activate chan struct{}) (net.Listener, error) {
 func NewListenBuffer(proto, addr string, activate chan struct{}) (net.Listener, error) {
 	wrapped, err := net.Listen(proto, addr)
 	wrapped, err := net.Listen(proto, addr)
 	if err != nil {
 	if err != nil {
@@ -20,20 +44,26 @@ func NewListenBuffer(proto, addr string, activate chan struct{}) (net.Listener,
 	}, nil
 	}, nil
 }
 }
 
 
+// defaultListener is the buffered wrapper around the net.Listener
 type defaultListener struct {
 type defaultListener struct {
-	wrapped  net.Listener // the real listener to wrap
-	ready    bool         // is the listner ready to start accpeting connections
-	activate chan struct{}
+	wrapped  net.Listener  // The net.Listener wrapped by listenbuffer
+	ready    bool          // Whether the listenbuffer has been activated
+	activate chan struct{} // Channel to control activation of the listenbuffer
 }
 }
 
 
+// Close closes the wrapped socket.
 func (l *defaultListener) Close() error {
 func (l *defaultListener) Close() error {
 	return l.wrapped.Close()
 	return l.wrapped.Close()
 }
 }
 
 
+// Addr returns the listening address of the wrapped socket.
 func (l *defaultListener) Addr() net.Addr {
 func (l *defaultListener) Addr() net.Addr {
 	return l.wrapped.Addr()
 	return l.wrapped.Addr()
 }
 }
 
 
+// Accept returns a client connection on the wrapped socket if the listen buffer
+// has been activated. To active the listenbuffer the activation channel passed
+// to NewListenBuffer must have been closed or sent an event.
 func (l *defaultListener) Accept() (net.Conn, error) {
 func (l *defaultListener) Accept() (net.Conn, error) {
 	// if the listen has been told it is ready then we can go ahead and
 	// if the listen has been told it is ready then we can go ahead and
 	// start returning connections
 	// start returning connections

+ 41 - 0
pkg/listenbuffer/listen_buffer_test.go

@@ -0,0 +1,41 @@
+package listenbuffer
+
+import (
+	"io/ioutil"
+	"net"
+	"testing"
+)
+
+func TestListenBufferAllowsAcceptingWhenActivated(t *testing.T) {
+	lock := make(chan struct{})
+	buffer, err := NewListenBuffer("tcp", "", lock)
+	if err != nil {
+		t.Fatal("Unable to create listen buffer: ", err)
+	}
+
+	go func() {
+		conn, err := net.Dial("tcp", buffer.Addr().String())
+		if err != nil {
+			t.Fatal("Client failed to establish connection to server: ", err)
+		}
+
+		conn.Write([]byte("ping"))
+		conn.Close()
+	}()
+
+	close(lock)
+
+	client, err := buffer.Accept()
+	if err != nil {
+		t.Fatal("Failed to accept client: ", err)
+	}
+
+	response, err := ioutil.ReadAll(client)
+	if err != nil {
+		t.Fatal("Failed to read from client: ", err)
+	}
+
+	if string(response) != "ping" {
+		t.Fatal("Expected to receive ping from client, received: ", string(response))
+	}
+}