From 61224ce31260c86abf379206e2131ba299e5c3bb Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 13 Mar 2015 09:37:32 -0700 Subject: [PATCH] command/server: tcp listener --- command/server/listener.go | 25 ++++++++++++++++ command/server/listener_tcp.go | 20 +++++++++++++ command/server/listener_tcp_test.go | 21 ++++++++++++++ command/server/listener_test.go | 44 +++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+) create mode 100644 command/server/listener.go create mode 100644 command/server/listener_tcp.go create mode 100644 command/server/listener_tcp_test.go create mode 100644 command/server/listener_test.go diff --git a/command/server/listener.go b/command/server/listener.go new file mode 100644 index 000000000..28b063a0e --- /dev/null +++ b/command/server/listener.go @@ -0,0 +1,25 @@ +package server + +import ( + "fmt" + "net" +) + +// ListenerFactory is the factory function to create a listener. +type ListenerFactory func(map[string]string) (net.Listener, error) + +// BuiltinListeners is the list of built-in listener types. +var BuiltinListeners = map[string]ListenerFactory{ + "tcp": tcpListenerFactory, +} + +// NewListener creates a new listener of the given type with the given +// configuration. The type is looked up in the BuiltinListeners map. +func NewListener(t string, config map[string]string) (net.Listener, error) { + f, ok := BuiltinListeners[t] + if !ok { + return nil, fmt.Errorf("unknown listener type: %s", t) + } + + return f(config) +} diff --git a/command/server/listener_tcp.go b/command/server/listener_tcp.go new file mode 100644 index 000000000..cff4d4c30 --- /dev/null +++ b/command/server/listener_tcp.go @@ -0,0 +1,20 @@ +package server + +import ( + "fmt" + "net" +) + +func tcpListenerFactory(config map[string]string) (net.Listener, error) { + addr, ok := config["address"] + if !ok { + return nil, fmt.Errorf("'address' must be set") + } + + ln, err := net.Listen("tcp", addr) + if err != nil { + return nil, err + } + + return ln, nil +} diff --git a/command/server/listener_tcp_test.go b/command/server/listener_tcp_test.go new file mode 100644 index 000000000..61309a74a --- /dev/null +++ b/command/server/listener_tcp_test.go @@ -0,0 +1,21 @@ +package server + +import ( + "net" + "testing" +) + +func TestTCPListener(t *testing.T) { + ln, err := tcpListenerFactory(map[string]string{ + "address": "127.0.0.1:0", + }) + if err != nil { + t.Fatalf("err: %s", err) + } + + connFn := func(lnReal net.Listener) (net.Conn, error) { + return net.Dial("tcp", ln.Addr().String()) + } + + testListenerImpl(t, ln, connFn) +} diff --git a/command/server/listener_test.go b/command/server/listener_test.go new file mode 100644 index 000000000..d0894c510 --- /dev/null +++ b/command/server/listener_test.go @@ -0,0 +1,44 @@ +package server + +import ( + "bytes" + "io" + "net" + "testing" + "time" +) + +type testListenerConnFn func(net.Listener) (net.Conn, error) + +func testListenerImpl(t *testing.T, ln net.Listener, connFn testListenerConnFn) { + serverCh := make(chan net.Conn, 1) + go func() { + server, err := ln.Accept() + if err != nil { + t.Fatalf("err: %s", err) + } + + serverCh <- server + }() + + client, err := connFn(ln) + if err != nil { + t.Fatalf("err: %s", err) + } + server := <-serverCh + defer client.Close() + defer server.Close() + + var buf bytes.Buffer + go io.Copy(&buf, server) + + if _, err := client.Write([]byte("foo")); err != nil { + t.Fatalf("err: %s", err) + } + + time.Sleep(100 * time.Millisecond) + + if buf.String() != "foo" { + t.Fatalf("bad: %v", buf.String()) + } +}