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,109 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// mockReadWriter implements io.ReadWriter
|
||||
type mockReadWriter struct {
|
||||
data []byte
|
||||
readPos int
|
||||
readErr error
|
||||
|
||||
writeBuf []byte
|
||||
}
|
||||
|
||||
func (m *mockReadWriter) Read(p []byte) (int, error) {
|
||||
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 *mockReadWriter) Write(p []byte) (int, error) {
|
||||
m.writeBuf = append(m.writeBuf, p...)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func TestTransport_Normal(t *testing.T) {
|
||||
rw1 := &mockReadWriter{data: []byte("from rw1")}
|
||||
rw2 := &mockReadWriter{data: []byte("from rw2")}
|
||||
|
||||
done := make(chan struct{}, 2)
|
||||
|
||||
go func() {
|
||||
_ = CopyBuffer(rw1, rw2, 1024)
|
||||
done <- struct{}{}
|
||||
}()
|
||||
go func() {
|
||||
_ = CopyBuffer(rw2, rw1, 1024)
|
||||
done <- struct{}{}
|
||||
}()
|
||||
|
||||
// Wait for both goroutines to complete
|
||||
<-done
|
||||
<-done
|
||||
|
||||
// rw2 receives data from rw1
|
||||
if string(rw2.writeBuf) != "from rw1" {
|
||||
t.Errorf("rw2.writeBuf = %q, want %q", string(rw2.writeBuf), "from rw1")
|
||||
}
|
||||
// rw1 receives data from rw2
|
||||
if string(rw1.writeBuf) != "from rw2" {
|
||||
t.Errorf("rw1.writeBuf = %q, want %q", string(rw1.writeBuf), "from rw2")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransport_ReadError(t *testing.T) {
|
||||
readErr := errors.New("read failure")
|
||||
rw1 := &mockReadWriter{readErr: readErr}
|
||||
rw2 := &mockReadWriter{data: []byte("from rw2")}
|
||||
|
||||
err := Transport(rw1, rw2)
|
||||
if err == nil {
|
||||
t.Error("expected error, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyBuffer(t *testing.T) {
|
||||
src := &mockReadWriter{data: []byte("test data")}
|
||||
dst := &mockReadWriter{}
|
||||
|
||||
err := CopyBuffer(dst, src, 1024)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(dst.writeBuf) != "test data" {
|
||||
t.Errorf("dst.writeBuf = %q, want %q", string(dst.writeBuf), "test data")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyBuffer_ReadError(t *testing.T) {
|
||||
readErr := errors.New("read failure")
|
||||
src := &mockReadWriter{readErr: readErr}
|
||||
dst := &mockReadWriter{}
|
||||
|
||||
err := CopyBuffer(dst, src, 1024)
|
||||
if err == nil {
|
||||
t.Error("expected error, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransport_EOFIgnored(t *testing.T) {
|
||||
// Transport treats io.EOF as non-error.
|
||||
// Both goroutines return nil/EOF → Transport should return nil.
|
||||
rw1 := &mockReadWriter{data: []byte("data")}
|
||||
rw2 := &mockReadWriter{data: []byte("")} // empty data → immediate EOF
|
||||
|
||||
err := Transport(rw1, rw2)
|
||||
if err != nil {
|
||||
t.Errorf("expected nil (EOF is not an error), got %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user