diff options
| -rw-r--r-- | go.mod | 5 | ||||
| -rw-r--r-- | go.sum | 11 | ||||
| -rw-r--r-- | pipeln.go | 78 | ||||
| -rw-r--r-- | pipeln_test.go | 30 |
4 files changed, 124 insertions, 0 deletions
@@ -0,0 +1,5 @@ +module go.awhk.org/pipeln + +go 1.15 + +require github.com/stretchr/testify v1.7.0 @@ -0,0 +1,11 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pipeln.go b/pipeln.go new file mode 100644 index 0000000..8ff6a62 --- /dev/null +++ b/pipeln.go @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: CC0-1.0 + +package pipeln + +import ( + "context" + "errors" + "net" +) + +var ( + ErrBadAddress = errors.New("bad address") + ErrClosed = errors.New("closed listener") +) + +type addr struct { + ln *PipeListenerDialer +} + +var _ net.Addr = addr{} + +func (addr) Network() string { + return "pipe" +} + +func (a addr) String() string { + return "pipe:" + a.ln.addr +} + +// PipeListener can be used to simulate client-server interaction within +// the same process. Useful for testing. Somehow the Go standard library +// provides net.Pipe but no net.PipeListener. +type PipeListenerDialer struct { + addr string + conns chan net.Conn + done chan struct{} +} + +var _ net.Listener = &PipeListenerDialer{} + +func (ln *PipeListenerDialer) Accept() (net.Conn, error) { + select { + case conn := <-ln.conns: + return conn, nil + case <-ln.done: + return nil, ErrClosed + } +} + +func (ln *PipeListenerDialer) Addr() net.Addr { + return addr{ln} +} + +func (ln *PipeListenerDialer) Close() error { + close(ln.done) + return nil +} + +func (ln *PipeListenerDialer) Dial(_, addr string) (net.Conn, error) { + if addr != ln.addr { + return nil, ErrBadAddress + } + s, c := net.Pipe() + select { + case <-ln.done: + return nil, ErrClosed + case ln.conns <- s: + return c, nil + } +} + +func (ln *PipeListenerDialer) DialContext(_ context.Context, network, addr string) (net.Conn, error) { + return ln.Dial(network, addr) +} + +func New(addr string) *PipeListenerDialer { + return &PipeListenerDialer{addr, make(chan net.Conn), make(chan struct{})} +} diff --git a/pipeln_test.go b/pipeln_test.go new file mode 100644 index 0000000..e5de8d0 --- /dev/null +++ b/pipeln_test.go @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: CC0-1.0 + +package pipeln + +import ( + "context" + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test(t *testing.T) { + ln := New("test:80") + + mux := http.NewServeMux() + mux.HandleFunc("/endpoint", func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + }) + srv := http.Server{Handler: mux} + go srv.Serve(ln) + + client := http.Client{Transport: &http.Transport{Dial: ln.Dial}} + resp, err := client.Get("http://test/endpoint") + require.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + + srv.Shutdown(context.Background()) +} |
