1// Copyright 2023 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5//go:build js || wasip1
6
7package net
8
9// GOOS=js and GOOS=wasip1 do not have typical socket networking capabilities
10// found on other platforms. To help run test suites of the stdlib packages,
11// an in-memory "fake network" facility is implemented.
12//
13// The tests in this files are intended to validate the behavior of the fake
14// network stack on these platforms.
15
16import (
17	"errors"
18	"syscall"
19	"testing"
20)
21
22func TestFakePortExhaustion(t *testing.T) {
23	if testing.Short() {
24		t.Skipf("skipping test that opens 1<<16 connections")
25	}
26
27	ln := newLocalListener(t, "tcp")
28	done := make(chan struct{})
29	go func() {
30		var accepted []Conn
31		defer func() {
32			for _, c := range accepted {
33				c.Close()
34			}
35			close(done)
36		}()
37
38		for {
39			c, err := ln.Accept()
40			if err != nil {
41				return
42			}
43			accepted = append(accepted, c)
44		}
45	}()
46
47	var dialed []Conn
48	defer func() {
49		ln.Close()
50		for _, c := range dialed {
51			c.Close()
52		}
53		<-done
54	}()
55
56	// Since this test is not running in parallel, we expect to be able to open
57	// all 65535 valid (fake) ports. The listener is already using one, so
58	// we should be able to Dial the remaining 65534.
59	for len(dialed) < (1<<16)-2 {
60		c, err := Dial(ln.Addr().Network(), ln.Addr().String())
61		if err != nil {
62			t.Fatalf("unexpected error from Dial with %v connections: %v", len(dialed), err)
63		}
64		dialed = append(dialed, c)
65		if testing.Verbose() && len(dialed)%(1<<12) == 0 {
66			t.Logf("dialed %d connections", len(dialed))
67		}
68	}
69	t.Logf("dialed %d connections", len(dialed))
70
71	// Now that all of the ports are in use, dialing another should fail due
72	// to port exhaustion, which (for POSIX-like socket APIs) should return
73	// an EADDRINUSE error.
74	c, err := Dial(ln.Addr().Network(), ln.Addr().String())
75	if err == nil {
76		c.Close()
77	}
78	if errors.Is(err, syscall.EADDRINUSE) {
79		t.Logf("Dial returned expected error: %v", err)
80	} else {
81		t.Errorf("unexpected error from Dial: %v\nwant: %v", err, syscall.EADDRINUSE)
82	}
83
84	// Opening a Listener should fail at this point too.
85	ln2, err := Listen("tcp", "localhost:0")
86	if err == nil {
87		ln2.Close()
88	}
89	if errors.Is(err, syscall.EADDRINUSE) {
90		t.Logf("Listen returned expected error: %v", err)
91	} else {
92		t.Errorf("unexpected error from Listen: %v\nwant: %v", err, syscall.EADDRINUSE)
93	}
94
95	// When we close an arbitrary connection, we should be able to reuse its port
96	// even if the server hasn't yet seen the ECONNRESET for the connection.
97	dialed[0].Close()
98	dialed = dialed[1:]
99	t.Logf("closed one connection")
100	c, err = Dial(ln.Addr().Network(), ln.Addr().String())
101	if err == nil {
102		c.Close()
103		t.Logf("Dial succeeded")
104	} else {
105		t.Errorf("unexpected error from Dial: %v", err)
106	}
107}
108