Skip to content

Commit c919766

Browse files
committed
add sanity test of ipv6 and ipv4 socket
1 parent 3cfab9f commit c919766

File tree

4 files changed

+98
-10
lines changed

4 files changed

+98
-10
lines changed

.github/workflows/go.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ jobs:
1212
uses: actions/setup-go@v5
1313
with:
1414
go-version: '1.24.x' # Set to your Go version
15+
- name: Run golangci-lint
16+
uses: golangci/golangci-lint-action@v4
1517
- name: Install dependencies
1618
run: go mod tidy
1719
- name: Run tests

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
README.md:multilistener/*
2-
gomarkdoc --header-file header.in --output README.md ./multilistener
2+
gomarkdoc --header-file header.in --output README.md ./multilistener
3+
4+
lint:
5+
golangci-lint run

cmd/simple-listener/main.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"net"
7+
)
8+
9+
func main() {
10+
// This listener is bound to the IPv6 loopback address and will accept
11+
// connections from both [::1] (IPv6) and 127.0.0.1 (IPv4).
12+
ln, err := net.Listen("tcp", "[::1]:8080")
13+
if err != nil {
14+
log.Fatal(err)
15+
}
16+
defer ln.Close()
17+
18+
fmt.Println("Listening on [::1]:8080. Try connecting with 'telnet 127.0.0.1 8080' and 'telnet ::1 8080'")
19+
20+
for {
21+
conn, err := ln.Accept()
22+
if err != nil {
23+
log.Println(err)
24+
continue
25+
}
26+
fmt.Printf("Accepted connection from %s\n", conn.RemoteAddr())
27+
conn.Close()
28+
}
29+
}

multilistener/multilistener_test.go

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,83 @@
11
package multilistener
22

33
import (
4+
"fmt"
45
"io"
6+
"net"
57
"net/http"
6-
"sync"
7-
"fmt"
88
"testing"
99
"time"
1010
)
1111

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+
1262
func TestMultiListener(t *testing.T) {
63+
ready := make(chan struct{})
1364
ml, err := NewLocalLoopback("8081") // Use port 0 to get a random available port
1465
if err != nil {
1566
t.Fatalf("Failed to create MultiListener: %v", err)
1667
}
1768
// Start an HTTP server on the MultiListener
18-
var wg sync.WaitGroup
19-
wg.Add(1)
2069
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+
}
2578
}()
2679

80+
<-ready
2781
// Get the addresses of the listeners
2882
addrs := ml.AllAddr().String()
2983
t.Logf("MultiListener serving on: %s", addrs)
@@ -67,7 +121,7 @@ func ExampleNewLocalLoopback() {
67121
}
68122
fmt.Printf("Serving HTTP %+v\n", dual.AllAddr())
69123
fmt.Printf("Preferred Addr: %+v\n", dual.Addr())
70-
go http.Serve(dual, nil)
124+
go http.Serve(dual, nil) //nolint:errcheck
71125
// Output:
72126
// Serving HTTP [::1]:8080,127.0.0.1:8080
73127
// Preferred Addr: [::1]:8080

0 commit comments

Comments
 (0)