// Copyright 2023 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build js || wasip1 package net // GOOS=js and GOOS=wasip1 do not have typical socket networking capabilities // found on other platforms. To help run test suites of the stdlib packages, // an in-memory "fake network" facility is implemented. // // The tests in this files are intended to validate the behavior of the fake // network stack on these platforms. import ( "errors" "syscall" "testing" ) func TestFakePortExhaustion(t *testing.T) { if testing.Short() { t.Skipf("skipping test that opens 1<<16 connections") } ln := newLocalListener(t, "tcp") done := make(chan struct{}) go func() { var accepted []Conn defer func() { for _, c := range accepted { c.Close() } close(done) }() for { c, err := ln.Accept() if err != nil { return } accepted = append(accepted, c) } }() var dialed []Conn defer func() { ln.Close() for _, c := range dialed { c.Close() } <-done }() // Since this test is not running in parallel, we expect to be able to open // all 65535 valid (fake) ports. The listener is already using one, so // we should be able to Dial the remaining 65534. for len(dialed) < (1<<16)-2 { c, err := Dial(ln.Addr().Network(), ln.Addr().String()) if err != nil { t.Fatalf("unexpected error from Dial with %v connections: %v", len(dialed), err) } dialed = append(dialed, c) if testing.Verbose() && len(dialed)%(1<<12) == 0 { t.Logf("dialed %d connections", len(dialed)) } } t.Logf("dialed %d connections", len(dialed)) // Now that all of the ports are in use, dialing another should fail due // to port exhaustion, which (for POSIX-like socket APIs) should return // an EADDRINUSE error. c, err := Dial(ln.Addr().Network(), ln.Addr().String()) if err == nil { c.Close() } if errors.Is(err, syscall.EADDRINUSE) { t.Logf("Dial returned expected error: %v", err) } else { t.Errorf("unexpected error from Dial: %v\nwant: %v", err, syscall.EADDRINUSE) } // Opening a Listener should fail at this point too. ln2, err := Listen("tcp", "localhost:0") if err == nil { ln2.Close() } if errors.Is(err, syscall.EADDRINUSE) { t.Logf("Listen returned expected error: %v", err) } else { t.Errorf("unexpected error from Listen: %v\nwant: %v", err, syscall.EADDRINUSE) } // When we close an arbitrary connection, we should be able to reuse its port // even if the server hasn't yet seen the ECONNRESET for the connection. dialed[0].Close() dialed = dialed[1:] t.Logf("closed one connection") c, err = Dial(ln.Addr().Network(), ln.Addr().String()) if err == nil { c.Close() t.Logf("Dial succeeded") } else { t.Errorf("unexpected error from Dial: %v", err) } }