|
1 | 1 | package multilistener |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "fmt" |
4 | 5 | "io" |
| 6 | + "net" |
5 | 7 | "net/http" |
6 | | - "sync" |
7 | | - "fmt" |
8 | 8 | "testing" |
9 | 9 | "time" |
10 | 10 | ) |
11 | 11 |
|
| 12 | +// TestListenOnlyIPV6 sanity check that IPv6 Socket cannot receive ipv4 requests |
| 13 | +// |
| 14 | +// AI claimed IPV6 ::1 C sockets also accepted ipv4. This test confirms go sockets do not |
| 15 | +// see also cmd/simple-listener and test with telnet |
| 16 | +// I feel like I'm taking crazy pills. |
| 17 | +func TestListenOnlyIPV6(t *testing.T){ |
| 18 | + ready := make(chan struct{}) |
| 19 | + port := "8084" |
| 20 | + ml, err := net.Listen("tcp", "[::1]:"+port) // Listen only on IPv6 loopback |
| 21 | + if err != nil { |
| 22 | + t.Fatalf("Failed to create MultiListener for IPv6 only: %v", err) |
| 23 | + } |
| 24 | + |
| 25 | + go func() { |
| 26 | + close(ready) |
| 27 | + if err := http.Serve(ml, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 28 | + if _, err := io.WriteString(w, "Hello from IPv6 Listener!"); err != nil { |
| 29 | + t.Errorf("error writing string: %v", err) |
| 30 | + } |
| 31 | + })); err != nil { |
| 32 | + t.Errorf("http serve error: %v", err) |
| 33 | + } |
| 34 | + }() |
| 35 | + |
| 36 | + // server is ready |
| 37 | + <-ready |
| 38 | + |
| 39 | + // Attempt to connect to the IPv4 loopback address |
| 40 | + testAddr := "127.0.0.1:"+port |
| 41 | + t.Logf("Attempting to connect to %s (IPv4) to an IPv6-only listener", testAddr) |
| 42 | + |
| 43 | + resp, err := http.Get("http://" + testAddr) |
| 44 | + if err != nil { |
| 45 | + t.Logf("Expected error when connecting to IPv4 address on IPv6-only listener: %v", err) |
| 46 | + // This is the expected behavior, as the listener is only on IPv6 |
| 47 | + return |
| 48 | + } |
| 49 | + defer resp.Body.Close() |
| 50 | + |
| 51 | + // If we reach here, it means the IPv4 connection succeeded unexpectedly |
| 52 | + t.Errorf("Unexpected success connecting to IPv4 address %s on an IPv6-only listener. Status: %d", testAddr, resp.StatusCode) |
| 53 | + |
| 54 | + body, err := io.ReadAll(resp.Body) |
| 55 | + if err != nil { |
| 56 | + t.Errorf("Failed to read response body from %s: %v", testAddr, err) |
| 57 | + } |
| 58 | + t.Errorf("Received body: %s", string(body)) |
| 59 | +} |
| 60 | + |
| 61 | + |
12 | 62 | func TestMultiListener(t *testing.T) { |
| 63 | + ready := make(chan struct{}) |
13 | 64 | ml, err := NewLocalLoopback("8081") // Use port 0 to get a random available port |
14 | 65 | if err != nil { |
15 | 66 | t.Fatalf("Failed to create MultiListener: %v", err) |
16 | 67 | } |
17 | 68 | // Start an HTTP server on the MultiListener |
18 | | - var wg sync.WaitGroup |
19 | | - wg.Add(1) |
20 | 69 | go func() { |
21 | | - defer wg.Done() |
22 | | - http.Serve(ml, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
23 | | - io.WriteString(w, "Hello from MultiListener!") |
24 | | - })) |
| 70 | + close(ready) |
| 71 | + if err := http.Serve(ml, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 72 | + if _, err := io.WriteString(w, "Hello from MultiListener!"); err != nil{ |
| 73 | + t.Errorf("error writeString") |
| 74 | + } |
| 75 | + })); err != nil{ |
| 76 | + t.Errorf("http serve error") |
| 77 | + } |
25 | 78 | }() |
26 | 79 |
|
| 80 | + <-ready |
27 | 81 | // Get the addresses of the listeners |
28 | 82 | addrs := ml.AllAddr().String() |
29 | 83 | t.Logf("MultiListener serving on: %s", addrs) |
@@ -67,7 +121,7 @@ func ExampleNewLocalLoopback() { |
67 | 121 | } |
68 | 122 | fmt.Printf("Serving HTTP %+v\n", dual.AllAddr()) |
69 | 123 | fmt.Printf("Preferred Addr: %+v\n", dual.Addr()) |
70 | | - go http.Serve(dual, nil) |
| 124 | + go http.Serve(dual, nil) //nolint:errcheck |
71 | 125 | // Output: |
72 | 126 | // Serving HTTP [::1]:8080,127.0.0.1:8080 |
73 | 127 | // Preferred Addr: [::1]:8080 |
|
0 commit comments