Skip to content

Instantly share code, notes, and snippets.

@DeadlySurgeon
Last active November 29, 2024 23:37
Show Gist options
  • Save DeadlySurgeon/309887e392ba69f862ad86f436991622 to your computer and use it in GitHub Desktop.
Save DeadlySurgeon/309887e392ba69f862ad86f436991622 to your computer and use it in GitHub Desktop.
Pipe Connector in Go
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()
}
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