| // Copyright 2018 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package fakenet |
| |
| import ( |
| "io" |
| "net" |
| "sync" |
| "time" |
| ) |
| |
| // NewConn returns a net.Conn built on top of the supplied reader and writer. |
| // It decouples the read and write on the conn from the underlying stream |
| // to enable Close to abort ones that are in progress. |
| // It's primary use is to fake a network connection from stdin and stdout. |
| func NewConn(name string, in io.ReadCloser, out io.WriteCloser) net.Conn { |
| c := &fakeConn{ |
| name: name, |
| reader: newFeeder(in.Read), |
| writer: newFeeder(out.Write), |
| in: in, |
| out: out, |
| } |
| go c.reader.run() |
| go c.writer.run() |
| return c |
| } |
| |
| type fakeConn struct { |
| name string |
| reader *connFeeder |
| writer *connFeeder |
| in io.ReadCloser |
| out io.WriteCloser |
| } |
| |
| type fakeAddr string |
| |
| // connFeeder serializes calls to the source function (io.Reader.Read or |
| // io.Writer.Write) by delegating them to a channel. This also allows calls to |
| // be intercepted when the connection is closed, and cancelled early if the |
| // connection is closed while the calls are still outstanding. |
| type connFeeder struct { |
| source func([]byte) (int, error) |
| input chan []byte |
| result chan feedResult |
| mu sync.Mutex |
| closed bool |
| done chan struct{} |
| } |
| |
| type feedResult struct { |
| n int |
| err error |
| } |
| |
| func (c *fakeConn) Close() error { |
| c.reader.close() |
| c.writer.close() |
| c.in.Close() |
| c.out.Close() |
| return nil |
| } |
| |
| func (c *fakeConn) Read(b []byte) (n int, err error) { return c.reader.do(b) } |
| func (c *fakeConn) Write(b []byte) (n int, err error) { return c.writer.do(b) } |
| func (c *fakeConn) LocalAddr() net.Addr { return fakeAddr(c.name) } |
| func (c *fakeConn) RemoteAddr() net.Addr { return fakeAddr(c.name) } |
| func (c *fakeConn) SetDeadline(t time.Time) error { return nil } |
| func (c *fakeConn) SetReadDeadline(t time.Time) error { return nil } |
| func (c *fakeConn) SetWriteDeadline(t time.Time) error { return nil } |
| func (a fakeAddr) Network() string { return "fake" } |
| func (a fakeAddr) String() string { return string(a) } |
| |
| func newFeeder(source func([]byte) (int, error)) *connFeeder { |
| return &connFeeder{ |
| source: source, |
| input: make(chan []byte), |
| result: make(chan feedResult), |
| done: make(chan struct{}), |
| } |
| } |
| |
| func (f *connFeeder) close() { |
| f.mu.Lock() |
| if !f.closed { |
| f.closed = true |
| close(f.done) |
| } |
| f.mu.Unlock() |
| } |
| |
| func (f *connFeeder) do(b []byte) (n int, err error) { |
| // send the request to the worker |
| select { |
| case f.input <- b: |
| case <-f.done: |
| return 0, io.EOF |
| } |
| // get the result from the worker |
| select { |
| case r := <-f.result: |
| return r.n, r.err |
| case <-f.done: |
| return 0, io.EOF |
| } |
| } |
| |
| func (f *connFeeder) run() { |
| var b []byte |
| for { |
| // wait for an input request |
| select { |
| case b = <-f.input: |
| case <-f.done: |
| return |
| } |
| // invoke the underlying method |
| n, err := f.source(b) |
| // send the result back to the requester |
| select { |
| case f.result <- feedResult{n: n, err: err}: |
| case <-f.done: |
| return |
| } |
| } |
| } |