Skip to content

Commit 5b19e5e

Browse files
committed
fix: use independent pipes in vpn tunnel tests
1 parent 6116776 commit 5b19e5e

File tree

1 file changed

+54
-3
lines changed

1 file changed

+54
-3
lines changed

vpn/tunnel_internal_test.go

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package vpn
22

33
import (
44
"context"
5+
"io"
56
"net"
67
"net/netip"
78
"net/url"
@@ -485,9 +486,7 @@ func TestTunnel_sendAgentUpdate(t *testing.T) {
485486

486487
//nolint:revive // t takes precedence
487488
func setupTunnel(t *testing.T, ctx context.Context, client *fakeClient, mClock quartz.Clock) (*Tunnel, *speaker[*ManagerMessage, *TunnelMessage, TunnelMessage]) {
488-
mp, tp := net.Pipe()
489-
t.Cleanup(func() { _ = mp.Close() })
490-
t.Cleanup(func() { _ = tp.Close() })
489+
mp, tp := newBidiPipe(t)
491490
logger := testutil.Logger(t)
492491

493492
var tun *Tunnel
@@ -510,3 +509,55 @@ func setupTunnel(t *testing.T, ctx context.Context, client *fakeClient, mClock q
510509
mgr.start()
511510
return tun, mgr
512511
}
512+
513+
// In practice, the underlying connection is a pair of dup'd pipes. The tunnel
514+
// closing the connection (the dup'd FD) should not cause the manager to read EOF.
515+
// By replicating the behavior of these FDs, we avoid a race where the manager
516+
// reads EOF before it reads the stop response from the tunnel.
517+
type bidiPipe struct {
518+
rw io.ReadWriteCloser
519+
*refcnt
520+
}
521+
522+
type refcnt struct {
523+
count int
524+
mu sync.Mutex
525+
closeFunc func() error
526+
}
527+
528+
// Not idempotent, but it's only called once per bidiPipe.
529+
func (b *bidiPipe) Close() error {
530+
b.refcnt.mu.Lock()
531+
defer b.refcnt.mu.Unlock()
532+
533+
b.refcnt.count--
534+
if b.refcnt.count == 0 {
535+
_ = b.refcnt.closeFunc()
536+
}
537+
return nil
538+
}
539+
540+
func (b *bidiPipe) Read(p []byte) (n int, err error) {
541+
return b.rw.Read(p)
542+
}
543+
544+
func (b *bidiPipe) Write(p []byte) (n int, err error) {
545+
return b.rw.Write(p)
546+
}
547+
548+
func newBidiPipe(t *testing.T) (io.ReadWriteCloser, io.ReadWriteCloser) {
549+
mp, tp := net.Pipe()
550+
t.Cleanup(func() { _ = mp.Close() })
551+
t.Cleanup(func() { _ = tp.Close() })
552+
553+
refcnt := &refcnt{
554+
count: 2,
555+
closeFunc: func() error {
556+
_ = tp.Close()
557+
_ = mp.Close()
558+
return nil
559+
},
560+
}
561+
562+
return &bidiPipe{rw: tp, refcnt: refcnt}, &bidiPipe{rw: mp, refcnt: refcnt}
563+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy