-
-
Save DeadlySurgeon/309887e392ba69f862ad86f436991622 to your computer and use it in GitHub Desktop.
Pipe Connector in Go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package pipedialer | |
import ( | |
"context" | |
"net" | |
"sync" | |
) | |
type RotaryPhone struct { | |
connLock sync.Mutex | |
connIndex map[net.Conn]bool | |
accept chan net.Conn | |
closed bool | |
} | |
func New() *RotaryPhone { | |
return &RotaryPhone{ | |
connIndex: map[net.Conn]bool{}, | |
accept: make(chan net.Conn), | |
} | |
} | |
func (rp *RotaryPhone) Accept() (net.Conn, error) { | |
conn := <-rp.accept | |
if conn == nil { | |
return nil, net.ErrClosed | |
} | |
return conn, nil | |
} | |
func (rp *RotaryPhone) Close() error { | |
rp.connLock.Lock() | |
defer rp.connLock.Unlock() | |
if rp.closed { | |
return nil | |
} | |
rp.closed = true | |
close(rp.accept) | |
for conn := range rp.connIndex { | |
conn.Close() | |
} | |
return nil | |
} | |
func (rp *RotaryPhone) DialContext(ctx context.Context, network, address string) (net.Conn, error) { | |
client, server := net.Pipe() | |
rp.connLock.Lock() | |
defer rp.connLock.Unlock() | |
if rp.closed { | |
return nil, net.ErrClosed | |
} | |
rp.accept <- rp.bind(server) | |
return rp.bind(client), nil | |
} | |
func (rp *RotaryPhone) Network() string { return "dial" } | |
func (rp *RotaryPhone) String() string { return "rotaryphone" } | |
func (rp *RotaryPhone) Addr() net.Addr { return rp } | |
func (rp *RotaryPhone) bind(conn net.Conn) net.Conn { | |
return &boundClose{Conn: conn, close: func() { | |
rp.connLock.Lock() | |
defer rp.connLock.Unlock() | |
delete(rp.connIndex, conn) | |
}} | |
} | |
type boundClose struct { | |
net.Conn | |
close func() | |
} | |
func (bc *boundClose) Close() error { | |
bc.close() | |
return bc.Conn.Close() | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package pipedialer | |
import ( | |
"errors" | |
"io" | |
"net/http" | |
"sync" | |
"testing" | |
) | |
func TestRotaryPhone(t *testing.T) { | |
phone := New() | |
mux := http.NewServeMux() | |
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | |
_, _ = w.Write([]byte("Rotary Phone")) | |
}) | |
wg := &sync.WaitGroup{} | |
wg.Add(1) | |
s := &http.Server{Handler: mux} | |
go func() { | |
defer wg.Done() | |
if err := s.Serve(phone); err != nil && !errors.Is(err, http.ErrServerClosed) { | |
panic(err) | |
} | |
}() | |
defer wg.Wait() | |
defer phone.Close() | |
defer s.Close() | |
trans := http.DefaultTransport.(*http.Transport).Clone() | |
trans.DialContext = phone.DialContext | |
client := &http.Client{ | |
Transport: trans, | |
} | |
resp, err := client.Get("http://resource") | |
if err != nil { | |
t.Fatal(err) | |
} | |
defer resp.Body.Close() | |
b, err := io.ReadAll(resp.Body) | |
if err != nil { | |
t.Fatal(err) | |
} | |
if resp, expected := string(b), "Rotary Phone"; resp != expected { | |
t.Fatal("Expected", expected, "got", `"`+resp+`"`) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment