summaryrefslogtreecommitdiffstats
path: root/src/net/net_fake_test.go
blob: 4542228fbc5827f458dac13b033e29111467ddf3 (plain)
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
104
105
106
107
// 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)
	}
}