fix(dialer): correct inverted setMark on FreeBSD/OpenBSD
Fix setMark early-return conditions that were inverted relative to Linux: on FreeBSD (SO_USER_COOKIE) and OpenBSD (SO_RTABLE), a non-zero mark was short-circuited instead of applied, disabling socket marking entirely. Also fix GetClientIP to trim leading whitespace from X-Forwarded-For entries (per RFC 7239), and fix Body.Read to subtract len(b) instead of n from recordSize when a single read exceeds the remaining quota. Add unit tests for internal/net/ (addr, dialer, http, ip, net, pipe, transport, resolve, proxyproto, udp).
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
xio "github.com/go-gost/x/internal/io"
|
||||
)
|
||||
|
||||
func TestIsIPv4(t *testing.T) {
|
||||
tests := []struct {
|
||||
address string
|
||||
want bool
|
||||
}{
|
||||
{"192.168.1.1", true},
|
||||
{"10.0.0.1", true},
|
||||
{"127.0.0.1:8080", true},
|
||||
{"::1", false},
|
||||
{"[::1]:8080", false},
|
||||
{"", false},
|
||||
{":8080", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.address, func(t *testing.T) {
|
||||
if got := IsIPv4(tt.address); got != tt.want {
|
||||
t.Errorf("IsIPv4(%q) = %v, want %v", tt.address, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// mockConn implements net.Conn for testing
|
||||
type mockConn struct {
|
||||
net.Conn
|
||||
readErr error
|
||||
writeErr error
|
||||
}
|
||||
|
||||
func (m *mockConn) Read(p []byte) (int, error) {
|
||||
if m.readErr != nil {
|
||||
return 0, m.readErr
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (m *mockConn) Write(p []byte) (int, error) {
|
||||
if m.writeErr != nil {
|
||||
return 0, m.writeErr
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (m *mockConn) Close() error { return nil }
|
||||
|
||||
type mockAddr struct{}
|
||||
|
||||
func (m mockAddr) Network() string { return "tcp" }
|
||||
func (m mockAddr) String() string { return "127.0.0.1:0" }
|
||||
|
||||
// mockReadWriteCloser implements io.ReadWriteCloser + CloseRead + CloseWrite
|
||||
type mockReadWriteCloser struct {
|
||||
data []byte
|
||||
readPos int
|
||||
writeBuf []byte
|
||||
closed bool
|
||||
readClosed bool
|
||||
writeClosed bool
|
||||
readErr error
|
||||
writeErr error
|
||||
writeReadErr error
|
||||
deadlineFunc func() error
|
||||
}
|
||||
|
||||
func (m *mockReadWriteCloser) Read(p []byte) (int, error) {
|
||||
if m.readClosed {
|
||||
return 0, net.ErrClosed
|
||||
}
|
||||
if m.readErr != nil {
|
||||
return 0, m.readErr
|
||||
}
|
||||
if m.readPos >= len(m.data) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
n := copy(p, m.data[m.readPos:])
|
||||
m.readPos += n
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (m *mockReadWriteCloser) Write(p []byte) (int, error) {
|
||||
if m.writeClosed {
|
||||
return 0, net.ErrClosed
|
||||
}
|
||||
if m.writeErr != nil {
|
||||
return 0, m.writeErr
|
||||
}
|
||||
m.writeBuf = append(m.writeBuf, p...)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (m *mockReadWriteCloser) Close() error {
|
||||
m.closed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockReadWriteCloser) CloseRead() error {
|
||||
m.readClosed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockReadWriteCloser) CloseWrite() error {
|
||||
m.writeClosed = true
|
||||
return xio.ErrUnsupported
|
||||
}
|
||||
|
||||
func (m *mockReadWriteCloser) SetReadDeadline(t interface{}) error {
|
||||
if m.deadlineFunc != nil {
|
||||
return m.deadlineFunc()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestNewReadWriteConn(t *testing.T) {
|
||||
r := &mockReadWriteCloser{data: []byte("read data")}
|
||||
w := &mockReadWriteCloser{}
|
||||
c := &mockConn{}
|
||||
|
||||
rwc := NewReadWriteConn(r, w, c)
|
||||
|
||||
// Test Read
|
||||
buf := make([]byte, 9)
|
||||
n, err := rwc.Read(buf)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if n != 9 || string(buf[:n]) != "read data" {
|
||||
t.Errorf("expected 'read data', got %q", string(buf[:n]))
|
||||
}
|
||||
|
||||
// Test Write
|
||||
n, err = rwc.Write([]byte("write data"))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if n != 10 {
|
||||
t.Errorf("expected 10 bytes written, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_readWriteConn_CloseRead_CloseWrite(t *testing.T) {
|
||||
r := &mockReadWriteCloser{}
|
||||
w := &mockReadWriteCloser{}
|
||||
c := &mockConn{}
|
||||
|
||||
conn := NewReadWriteConn(r, w, c)
|
||||
|
||||
if cr, ok := conn.(xio.CloseRead); ok {
|
||||
err := cr.CloseRead()
|
||||
if err == nil {
|
||||
t.Error("expected error when CloseRead on conn without CloseRead")
|
||||
}
|
||||
} else {
|
||||
t.Error("should implement CloseRead")
|
||||
}
|
||||
|
||||
if cw, ok := conn.(xio.CloseWrite); ok {
|
||||
err := cw.CloseWrite()
|
||||
if err == nil {
|
||||
t.Error("expected error when CloseWrite on conn without CloseWrite")
|
||||
}
|
||||
} else {
|
||||
t.Error("should implement CloseWrite")
|
||||
}
|
||||
}
|
||||
|
||||
// closeReadWriteConn implements both CloseRead and CloseWrite
|
||||
type closeReadWriteConn struct {
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func (c *closeReadWriteConn) CloseRead() error { return nil }
|
||||
func (c *closeReadWriteConn) CloseWrite() error { return nil }
|
||||
func (c *closeReadWriteConn) Close() error { return nil }
|
||||
|
||||
func Test_readWriteConn_CloseRead_CloseWrite_supported(t *testing.T) {
|
||||
r := &mockReadWriteCloser{}
|
||||
w := &mockReadWriteCloser{}
|
||||
c := &closeReadWriteConn{}
|
||||
|
||||
conn := NewReadWriteConn(r, w, c)
|
||||
|
||||
if cr, ok := conn.(xio.CloseRead); ok {
|
||||
err := cr.CloseRead()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
} else {
|
||||
t.Error("should implement CloseRead")
|
||||
}
|
||||
|
||||
if cw, ok := conn.(xio.CloseWrite); ok {
|
||||
err := cw.CloseWrite()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
} else {
|
||||
t.Error("should implement CloseWrite")
|
||||
}
|
||||
}
|
||||
|
||||
// errReadWriteCloser returns errors on read/write, for error path testing
|
||||
type errReadWriteCloser struct {
|
||||
*mockReadWriteCloser
|
||||
}
|
||||
|
||||
func (m *errReadWriteCloser) Read(p []byte) (int, error) {
|
||||
return 0, errors.New("read error")
|
||||
}
|
||||
|
||||
func (m *errReadWriteCloser) Write(p []byte) (int, error) {
|
||||
return 0, errors.New("write error")
|
||||
}
|
||||
Reference in New Issue
Block a user