mirror of
https://github.com/go-i2p/go-meta-listener.git
synced 2025-06-08 01:09:16 -04:00
that approach feels doomed
This commit is contained in:
28
tcp/doc.go
28
tcp/doc.go
@ -1,28 +0,0 @@
|
||||
// Package tcp provides a minimal, production-hardened TCP listener implementation
|
||||
// with secure cross-platform defaults suitable for internet-facing services.
|
||||
//
|
||||
// This package exports a single function Listen() that creates TCP listeners
|
||||
// with optimized settings for production workloads including connection reuse,
|
||||
// keep-alive monitoring, optimized buffer sizes, and minimal latency configuration.
|
||||
//
|
||||
// The implementation uses only Go standard library components to ensure
|
||||
// compatibility across Windows, Linux, macOS, and BSD systems without
|
||||
// requiring platform-specific code or external dependencies.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// listener, err := tcp.Listen("tcp", ":8080")
|
||||
// if err != nil {
|
||||
// log.Fatalf("Failed to create listener: %v", err)
|
||||
// }
|
||||
// defer listener.Close()
|
||||
//
|
||||
// for {
|
||||
// conn, err := listener.Accept()
|
||||
// if err != nil {
|
||||
// log.Printf("Accept error: %v", err)
|
||||
// continue
|
||||
// }
|
||||
// go handleConnection(conn)
|
||||
// }
|
||||
package tcp
|
140
tcp/listen.go
140
tcp/listen.go
@ -1,140 +0,0 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// Production-optimized defaults
|
||||
keepAliveInterval = 30 * time.Second
|
||||
bufferSize = 64 * 1024 // 64KB
|
||||
connectionBacklog = 128
|
||||
)
|
||||
|
||||
// Listen creates a TCP listener with production-hardened defaults suitable
|
||||
// for internet-facing services. The implementation applies secure socket
|
||||
// options including address reuse, keep-alive monitoring, optimized buffers,
|
||||
// and minimal latency configuration.
|
||||
//
|
||||
// Parameters:
|
||||
// - network: Must be "tcp", "tcp4", or "tcp6"
|
||||
// - address: Standard Go network address format (e.g., ":8080", "127.0.0.1:8080")
|
||||
//
|
||||
// Returns a net.Listener configured with production defaults, or an error
|
||||
// if the listener cannot be created or configured.
|
||||
//
|
||||
// The listener applies these optimizations:
|
||||
// - SO_REUSEADDR: Prevents "address already in use" during rapid restarts
|
||||
// - Keep-alive: 30-second interval for connection health monitoring
|
||||
// - Buffer sizes: 64KB receive/send buffers for optimal throughput
|
||||
// - TCP_NODELAY: Enabled to minimize latency
|
||||
// - Backlog: 128 pending connections in accept queue
|
||||
func Listen(network, address string) (net.Listener, error) {
|
||||
// Validate network parameter
|
||||
if !isValidNetwork(network) {
|
||||
return nil, fmt.Errorf("tcp.Listen: invalid network type %q for addr %s, must be tcp, tcp4, or tcp6", network, address)
|
||||
}
|
||||
|
||||
// Validate address format by attempting to resolve
|
||||
if _, err := net.ResolveTCPAddr(network, address); err != nil {
|
||||
return nil, fmt.Errorf("tcp.Listen: invalid address %q for network %q: %w", address, network, err)
|
||||
}
|
||||
|
||||
// Create the base listener
|
||||
listener, err := net.Listen(network, address)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("tcp.Listen: failed to create listener on %s %s: %w", network, address, err)
|
||||
}
|
||||
|
||||
// Apply production socket configurations
|
||||
tcpListener, ok := listener.(*net.TCPListener)
|
||||
if !ok {
|
||||
listener.Close()
|
||||
return nil, fmt.Errorf("tcp.Listen: unexpected listener type for %s %s", network, address)
|
||||
}
|
||||
|
||||
// Configure the underlying socket with production defaults
|
||||
if err := configureSocket(tcpListener); err != nil {
|
||||
// Ensure proper cleanup even if socket is in inconsistent state
|
||||
if closeErr := listener.Close(); closeErr != nil {
|
||||
// Log the close error but return the original configuration error
|
||||
// since that's the primary failure point
|
||||
return nil, fmt.Errorf("tcp.Listen: failed to configure socket options for %s %s (%w), and failed to close listener (%v)", network, address, err, closeErr)
|
||||
}
|
||||
return nil, fmt.Errorf("tcp.Listen: failed to configure socket options for %s %s: %w", network, address, err)
|
||||
}
|
||||
|
||||
return tcpListener, nil
|
||||
}
|
||||
|
||||
// isValidNetwork validates the network parameter
|
||||
func isValidNetwork(network string) bool {
|
||||
switch network {
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// configureSocket applies production-ready socket options to the TCP listener
|
||||
func configureSocket(listener *net.TCPListener) error {
|
||||
// Use SyscallConn to access the raw socket without duplicating the file descriptor
|
||||
rawConn, err := listener.SyscallConn()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get listener syscall connection: %w", err)
|
||||
}
|
||||
|
||||
// Apply socket options using the raw connection
|
||||
var sockErr error
|
||||
err = rawConn.Control(func(fd uintptr) {
|
||||
// Enable SO_REUSEADDR to prevent "address already in use" errors
|
||||
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
|
||||
sockErr = fmt.Errorf("failed to set SO_REUSEADDR: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Enable SO_KEEPALIVE for connection health monitoring
|
||||
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil {
|
||||
sockErr = fmt.Errorf("failed to set SO_KEEPALIVE: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Set keep-alive probe interval (Linux-specific, gracefully degrade on other platforms)
|
||||
keepAliveSeconds := int(keepAliveInterval.Seconds())
|
||||
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, keepAliveSeconds); err != nil {
|
||||
// TCP_KEEPINTVL may not be available on all platforms, continue without failing
|
||||
// The SO_KEEPALIVE setting above will still provide basic keep-alive functionality
|
||||
}
|
||||
|
||||
// Set receive buffer size for optimal throughput
|
||||
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_RCVBUF, bufferSize); err != nil {
|
||||
sockErr = fmt.Errorf("failed to set receive buffer size: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Set send buffer size for optimal throughput
|
||||
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_SNDBUF, bufferSize); err != nil {
|
||||
sockErr = fmt.Errorf("failed to set send buffer size: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Enable TCP_NODELAY to minimize latency (disable Nagle's algorithm)
|
||||
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_NODELAY, 1); err != nil {
|
||||
sockErr = fmt.Errorf("failed to set TCP_NODELAY: %w", err)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to access socket for configuration: %w", err)
|
||||
}
|
||||
if sockErr != nil {
|
||||
return sockErr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,295 +0,0 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestListen_ValidNetworks(t *testing.T) {
|
||||
testCases := []struct {
|
||||
network string
|
||||
address string
|
||||
}{
|
||||
{"tcp", ":0"},
|
||||
{"tcp4", ":0"},
|
||||
{"tcp6", ":0"},
|
||||
{"tcp", "127.0.0.1:0"},
|
||||
{"tcp4", "127.0.0.1:0"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("%s_%s", tc.network, tc.address), func(t *testing.T) {
|
||||
listener, err := Listen(tc.network, tc.address)
|
||||
if err != nil {
|
||||
t.Fatalf("Listen(%s, %s) failed: %v", tc.network, tc.address, err)
|
||||
}
|
||||
defer listener.Close()
|
||||
|
||||
// Verify we can get the actual address
|
||||
addr := listener.Addr()
|
||||
if addr == nil {
|
||||
t.Fatal("Listener address is nil")
|
||||
}
|
||||
|
||||
// Verify the address has expected network type
|
||||
if !strings.HasPrefix(addr.Network(), "tcp") {
|
||||
t.Errorf("Expected tcp network, got %s", addr.Network())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListen_InvalidNetworks(t *testing.T) {
|
||||
invalidNetworks := []string{"udp", "unix", "ip", "", "TCP", "tcp1"}
|
||||
|
||||
for _, network := range invalidNetworks {
|
||||
t.Run(network, func(t *testing.T) {
|
||||
_, err := Listen(network, ":0")
|
||||
if err == nil {
|
||||
t.Fatalf("Listen(%s, :0) should have failed", network)
|
||||
}
|
||||
|
||||
expectedMsg := "invalid network type"
|
||||
if !strings.Contains(err.Error(), expectedMsg) {
|
||||
t.Errorf("Error should contain %q, got: %v", expectedMsg, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListen_InvalidAddresses(t *testing.T) {
|
||||
invalidAddresses := []string{
|
||||
":::",
|
||||
":::8080",
|
||||
"256.256.256.256:8080",
|
||||
"invalid:address",
|
||||
"127.0.0.1:99999",
|
||||
}
|
||||
|
||||
for _, addr := range invalidAddresses {
|
||||
t.Run(addr, func(t *testing.T) {
|
||||
_, err := Listen("tcp", addr)
|
||||
if err == nil {
|
||||
t.Fatalf("Listen(tcp, %s) should have failed", addr)
|
||||
}
|
||||
|
||||
expectedMsg := "invalid address"
|
||||
if !strings.Contains(err.Error(), expectedMsg) {
|
||||
t.Errorf("Error should contain %q, got: %v", expectedMsg, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListen_AcceptConnections(t *testing.T) {
|
||||
listener, err := Listen("tcp", ":0")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create listener: %v", err)
|
||||
}
|
||||
defer listener.Close()
|
||||
|
||||
addr := listener.Addr().String()
|
||||
|
||||
// Test accepting multiple connections
|
||||
const numConnections = 5
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// Start accepting connections
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for i := 0; i < numConnections; i++ {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
t.Errorf("Accept failed: %v", err)
|
||||
return
|
||||
}
|
||||
conn.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
// Create client connections
|
||||
for i := 0; i < numConnections; i++ {
|
||||
conn, err := net.Dial("tcp", addr)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to dial listener: %v", err)
|
||||
}
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestListen_RapidRestarts(t *testing.T) {
|
||||
// Test that SO_REUSEADDR prevents "address already in use" errors
|
||||
address := ":0"
|
||||
|
||||
// Create and close listener to get a port
|
||||
initialListener, err := Listen("tcp", address)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create initial listener: %v", err)
|
||||
}
|
||||
|
||||
// Get the actual assigned port
|
||||
actualAddr := initialListener.Addr().String()
|
||||
initialListener.Close()
|
||||
|
||||
// Rapidly restart listeners on the same port
|
||||
for i := 0; i < 3; i++ {
|
||||
listener, err := Listen("tcp", actualAddr)
|
||||
if err != nil {
|
||||
t.Fatalf("Restart %d failed: %v", i, err)
|
||||
}
|
||||
listener.Close()
|
||||
|
||||
// Brief pause to simulate real restart scenario
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func TestListen_ConcurrentAccess(t *testing.T) {
|
||||
listener, err := Listen("tcp", ":0")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create listener: %v", err)
|
||||
}
|
||||
defer listener.Close()
|
||||
|
||||
addr := listener.Addr().String()
|
||||
const numGoroutines = 10
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// Start multiple goroutines that will connect simultaneously
|
||||
wg.Add(numGoroutines)
|
||||
for i := 0; i < numGoroutines; i++ {
|
||||
go func(id int) {
|
||||
defer wg.Done()
|
||||
|
||||
conn, err := net.Dial("tcp", addr)
|
||||
if err != nil {
|
||||
t.Errorf("Goroutine %d: dial failed: %v", id, err)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
// Write some data to verify connection works
|
||||
testData := fmt.Sprintf("test-%d", id)
|
||||
if _, err := conn.Write([]byte(testData)); err != nil {
|
||||
t.Errorf("Goroutine %d: write failed: %v", id, err)
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
// Accept all connections
|
||||
acceptedCount := 0
|
||||
done := make(chan bool)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
acceptedCount++
|
||||
conn.Close()
|
||||
|
||||
if acceptedCount >= numGoroutines {
|
||||
done <- true
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Wait for all connections with timeout
|
||||
select {
|
||||
case <-done:
|
||||
// Success
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatalf("Timeout waiting for connections, accepted %d/%d", acceptedCount, numGoroutines)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestListen_ErrorMessages(t *testing.T) {
|
||||
testCases := []struct {
|
||||
network string
|
||||
address string
|
||||
expectedErr string
|
||||
}{
|
||||
{"udp", ":8080", "invalid network type"},
|
||||
{"tcp", ":::", "invalid address"},
|
||||
{"", ":8080", "invalid network type"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("%s_%s", tc.network, tc.address), func(t *testing.T) {
|
||||
_, err := Listen(tc.network, tc.address)
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error for Listen(%s, %s)", tc.network, tc.address)
|
||||
}
|
||||
|
||||
if !strings.Contains(err.Error(), tc.expectedErr) {
|
||||
t.Errorf("Error should contain %q, got: %v", tc.expectedErr, err)
|
||||
}
|
||||
|
||||
// Verify error message includes the problematic values
|
||||
errMsg := err.Error()
|
||||
if tc.network != "" && !strings.Contains(errMsg, tc.network) {
|
||||
t.Errorf("Error should include network %q: %v", tc.network, err)
|
||||
}
|
||||
if tc.address != "" && !strings.Contains(errMsg, tc.address) {
|
||||
t.Errorf("Error should include address %q: %v", tc.address, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmark the listener creation overhead
|
||||
func BenchmarkListen(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
listener, err := Listen("tcp", ":0")
|
||||
if err != nil {
|
||||
b.Fatalf("Listen failed: %v", err)
|
||||
}
|
||||
listener.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigureSocket_NoResourceLeak(t *testing.T) {
|
||||
// Create a listener
|
||||
listener, err := net.Listen("tcp", ":0")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create base listener: %v", err)
|
||||
}
|
||||
defer listener.Close()
|
||||
|
||||
tcpListener := listener.(*net.TCPListener)
|
||||
|
||||
// Configure the socket - this should not invalidate the listener
|
||||
err = configureSocket(tcpListener)
|
||||
if err != nil {
|
||||
t.Fatalf("Socket configuration failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the listener is still usable by accepting a connection
|
||||
addr := listener.Addr().String()
|
||||
|
||||
// Test connection in a goroutine
|
||||
go func() {
|
||||
conn, err := net.Dial("tcp", addr)
|
||||
if err == nil {
|
||||
conn.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
// This should succeed if the listener wasn't invalidated
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
t.Fatalf("Listener became unusable after configuration: %v", err)
|
||||
}
|
||||
conn.Close()
|
||||
}
|
Reference in New Issue
Block a user