arblant
firefox-vpn-client / core / internal / proxy / relay_test.go
relay_test.go103 lines · 2.4 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
package proxy

import (
	"errors"
	"sync/atomic"
	"testing"
	"time"
)

func TestProxyBidirectionalClosesBothSidesOnCopyError(t *testing.T) {
	t.Parallel()

	client := newControlledNetConn(errors.New("client read failed"))
	upstream := newControlledNetConn(nil)
	done := make(chan struct{})
	go func() {
		proxyBidirectional(client, upstream, 0)
		close(done)
	}()

	select {
	case <-done:
	case <-time.After(time.Second):
		t.Fatal("proxyBidirectional did not close the blocked peer after copy error")
	}
	select {
	case <-upstream.closed:
	default:
		t.Fatal("expected upstream connection to be closed")
	}
}

func TestTunnelWrappersForwardHalfCloseAndReleaseOnClose(t *testing.T) {
	t.Parallel()

	tests := []struct {
		name string
		conn func(*fakeHalfCloseConn, *atomic.Int32) halfCloseTestConn
	}{
		{
			name: "pooled",
			conn: func(underlying *fakeHalfCloseConn, releaseCount *atomic.Int32) halfCloseTestConn {
				return &pooledTunnelConn{
					Conn: underlying,
					release: func() {
						releaseCount.Add(1)
					},
				}
			},
		},
		{
			name: "managed",
			conn: func(underlying *fakeHalfCloseConn, releaseCount *atomic.Int32) halfCloseTestConn {
				return &managedTunnelConn{
					Conn: underlying,
					release: func() {
						releaseCount.Add(1)
					},
				}
			},
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			underlying := &fakeHalfCloseConn{}
			var releaseCount atomic.Int32
			conn := tc.conn(underlying, &releaseCount)

			assertHalfCloseForwarding(t, conn, underlying, &releaseCount)
		})
	}
}

func assertHalfCloseForwarding(t *testing.T, conn halfCloseTestConn, underlying *fakeHalfCloseConn, releaseCount *atomic.Int32) {
	t.Helper()

	if err := conn.CloseWrite(); err != nil {
		t.Fatalf("CloseWrite returned error: %v", err)
	}
	if got := underlying.closeWriteCount.Load(); got != 1 {
		t.Fatalf("expected underlying CloseWrite once, got %d", got)
	}

	if err := conn.CloseRead(); err != nil {
		t.Fatalf("CloseRead returned error: %v", err)
	}
	if got := underlying.closeReadCount.Load(); got != 1 {
		t.Fatalf("expected underlying CloseRead once, got %d", got)
	}

	if err := conn.Close(); err != nil {
		t.Fatalf("Close returned error: %v", err)
	}
	if err := conn.Close(); err != nil {
		t.Fatalf("second Close returned error: %v", err)
	}
	if got := releaseCount.Load(); got != 1 {
		t.Fatalf("expected pooled session release once, got %d", got)
	}
}