package mtprotoedge import ( "bytes" "context" "errors" "io" "net" "testing" "time" ) func TestSamePortMuxListenerCloseWaitsAndReturnsBacklogAdmission(t *testing.T) { admission := newAdmissionController(4, 4, 1) listener := &samePortMuxListener{ addr: &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1)}, ch: make(chan net.Conn, 1), closed: make(chan struct{}), } backlog, backlogPeer := trackedMuxPipe(t, admission, 1001) defer backlogPeer.Close() if !listener.deliver(context.Background(), backlog) { t.Fatal("initial backlog delivery was rejected") } // Deterministically model a producer that passed the delivery gate but has not yet // completed. Close must publish closed first, then wait before draining the backlog. if !listener.beginDelivery() { t.Fatal("in-flight delivery gate unexpectedly closed") } closeDone := make(chan struct{}) go func() { _ = listener.Close() close(closeDone) }() select { case <-listener.closed: case <-time.After(time.Second): t.Fatal("Close did not publish listener closure") } select { case <-closeDone: t.Fatal("Close returned before in-flight delivery completed") default: } listener.deliveryWG.Done() select { case <-closeDone: case <-time.After(time.Second): t.Fatal("Close did not finish after delivery completed") } assertAdmissionConnections(t, admission, 0) late, latePeer := trackedMuxPipe(t, admission, 1002) defer latePeer.Close() if listener.deliver(context.Background(), late) { t.Fatal("delivery after Close unexpectedly succeeded") } _ = late.Close() // dispatch owns and closes a rejected delivery. assertAdmissionConnections(t, admission, 0) } func TestSamePortMuxPermanentAcceptErrorCancelsBlockedDeliveryBeforeWait(t *testing.T) { serverSide, clientSide := net.Pipe() defer clientSide.Close() wantErr := errors.New("same-port permanent accept failure") base := &connThenErrorListener{conn: serverSide, err: wantErr} closed := make(chan struct{}) mux := &samePortMux{ base: base, addr: base.Addr(), sniffTimeout: time.Hour, closed: closed, } // An unbuffered child listener deterministically leaves dispatch blocked in deliver: no // consumer is running, and the base listener immediately returns a permanent second error. mux.tcp = &samePortMuxListener{addr: mux.addr, ch: make(chan net.Conn), closed: make(chan struct{})} mux.http = &samePortMuxListener{addr: mux.addr, ch: make(chan net.Conn), closed: make(chan struct{})} writeDone := make(chan error, 1) go func() { _, err := clientSide.Write([]byte{0xef, 0, 0, 0}) writeDone <- err }() serveDone := make(chan error, 1) go func() { serveDone <- mux.Serve(context.Background()) }() select { case err := <-serveDone: if !errors.Is(err, wantErr) { t.Fatalf("Serve error = %v, want %v", err, wantErr) } case <-time.After(time.Second): t.Fatal("same-port Serve waited for blocked delivery before canceling it") } select { case <-writeDone: case <-time.After(time.Second): t.Fatal("sniff writer remained blocked after same-port shutdown") } } func TestSamePortMuxShutdownInterruptsSlowSniffImmediately(t *testing.T) { tests := []struct { name string shutdown func(context.CancelFunc, *samePortMux) }{ { name: "context cancel", shutdown: func(cancel context.CancelFunc, _ *samePortMux) { cancel() }, }, { name: "mux close", shutdown: func(_ context.CancelFunc, mux *samePortMux) { _ = mux.Close() }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { base, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("listen: %v", err) } mux := newSamePortMux(base, time.Minute) ctx, cancel := context.WithCancel(context.Background()) defer cancel() serveDone := make(chan error, 1) go func() { serveDone <- mux.Serve(ctx) }() peer, err := net.Dial("tcp", base.Addr().String()) if err != nil { t.Fatalf("dial: %v", err) } defer peer.Close() // No bytes are written: dispatch is blocked in the four-byte sniff with a one-minute // deadline. Shutdown must close this accepted socket instead of waiting for it. tt.shutdown(cancel, mux) select { case err := <-serveDone: if err != nil { t.Fatalf("Serve: %v", err) } case <-time.After(time.Second): t.Fatal("Serve waited for the sniff deadline after shutdown") } if err := peer.SetReadDeadline(time.Now().Add(time.Second)); err != nil { t.Fatalf("set peer deadline: %v", err) } var one [1]byte if _, err := peer.Read(one[:]); err == nil { t.Fatal("slow sniff socket remained open after mux shutdown") } }) } } func TestSamePortMuxSuccessfulHandoffReleasesSniffOwnership(t *testing.T) { base, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("listen: %v", err) } mux := newSamePortMux(base, time.Minute) ctx, cancel := context.WithCancel(context.Background()) serveDone := make(chan error, 1) go func() { serveDone <- mux.Serve(ctx) }() peer, err := net.Dial("tcp", base.Addr().String()) if err != nil { t.Fatalf("dial: %v", err) } defer peer.Close() if _, err := peer.Write([]byte{0xef, 0, 0, 0}); err != nil { t.Fatalf("write sniff prefix: %v", err) } accepted, err := mux.TCP().Accept() if err != nil { t.Fatalf("accept child: %v", err) } defer accepted.Close() // Once dispatch has delivered the Conn, canceling the mux may close listeners/backlog but // must not let the old sniff watcher close a socket now owned by the child consumer. cancel() select { case err := <-serveDone: if err != nil { t.Fatalf("Serve: %v", err) } case <-time.After(time.Second): t.Fatal("Serve did not stop after cancel") } if _, err := peer.Write([]byte{1, 2, 3, 4}); err != nil { t.Fatalf("write after handoff/shutdown: %v", err) } if err := accepted.SetReadDeadline(time.Now().Add(time.Second)); err != nil { t.Fatalf("set accepted deadline: %v", err) } got := make([]byte, 8) if _, err := io.ReadFull(accepted, got); err != nil { t.Fatalf("read handed-off connection: %v", err) } want := []byte{0xef, 0, 0, 0, 1, 2, 3, 4} if !bytes.Equal(got, want) { t.Fatalf("handed-off bytes = %x, want %x", got, want) } } func trackedMuxPipe(t *testing.T, admission *admissionController, port int) (net.Conn, net.Conn) { t.Helper() server, peer := net.Pipe() release, ok := admission.acquireConnection(&net.TCPAddr{ IP: net.ParseIP("203.0.113.20"), Port: port, }) if !ok { _ = server.Close() _ = peer.Close() t.Fatal("test connection admission rejected") } return &admittedConn{Conn: server, release: release}, peer } func assertAdmissionConnections(t *testing.T, admission *admissionController, want int) { t.Helper() admission.mu.Lock() got := admission.connections byIP := len(admission.byIP) admission.mu.Unlock() if got != want || (want == 0 && byIP != 0) { t.Fatalf("admission state = connections:%d by_ip:%d, want connections:%d", got, byIP, want) } }