Add RequestHeader to PoolConfig; default User-Agent header

This commit is contained in:
Jay
2026-06-06 20:02:38 -04:00
parent 35428a4dad
commit 08d2ff3317
2 changed files with 99 additions and 2 deletions
+20 -2
View File
@@ -5,6 +5,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"log/slog" "log/slog"
"net/http"
"git.wisehodl.dev/jay/go-honeybee/transport" "git.wisehodl.dev/jay/go-honeybee/transport"
"git.wisehodl.dev/jay/go-honeybee/types" "git.wisehodl.dev/jay/go-honeybee/types"
@@ -82,16 +83,20 @@ type PoolConfig struct {
InboxBufferSize int InboxBufferSize int
EventsBufferSize int EventsBufferSize int
WorkerConfig WorkerConfig WorkerConfig WorkerConfig
RequestHeader http.Header
} }
type PoolOption func(*PoolConfig) type PoolOption func(*PoolConfig)
func NewPoolConfig(opts ...PoolOption) (*PoolConfig, error) { func NewPoolConfig(opts ...PoolOption) (*PoolConfig, error) {
workerCfg, _ := NewWorkerConfig() workerCfg, _ := NewWorkerConfig()
defaultHeader := http.Header{}
defaultHeader.Set("User-Agent", "honeybee/0.1.0")
cfg := &PoolConfig{ cfg := &PoolConfig{
InboxBufferSize: 256, InboxBufferSize: 256,
EventsBufferSize: 10, EventsBufferSize: 10,
WorkerConfig: *workerCfg, WorkerConfig: *workerCfg,
RequestHeader: defaultHeader,
} }
for _, o := range opts { for _, o := range opts {
o(cfg) o(cfg)
@@ -120,6 +125,16 @@ func WithWorkerConfig(wc WorkerConfig) PoolOption {
} }
} }
func WithRequestHeader(h http.Header) PoolOption {
return func(c *PoolConfig) {
if h == nil {
c.RequestHeader = nil
} else {
c.RequestHeader = h.Clone()
}
}
}
func ValidatePoolConfig(c *PoolConfig) error { func ValidatePoolConfig(c *PoolConfig) error {
if c.InboxBufferSize < 1 { if c.InboxBufferSize < 1 {
return fmt.Errorf("invalid inbox buffer size: %d", c.InboxBufferSize) return fmt.Errorf("invalid inbox buffer size: %d", c.InboxBufferSize)
@@ -328,9 +343,12 @@ func (p *Pool) Connect(id string, opts ...ConnectOption) error {
opt(o) opt(o)
} }
// TODO: pass headers var hdr http.Header
if p.config.RequestHeader != nil {
hdr = p.config.RequestHeader.Clone()
}
dialFn := func(ctx context.Context) (types.Socket, error) { dialFn := func(ctx context.Context) (types.Socket, error) {
socket, _, err := p.dialer.DialContext(ctx, id, nil) socket, _, err := p.dialer.DialContext(ctx, id, hdr)
return socket, err return socket, err
} }
+79
View File
@@ -302,6 +302,85 @@ func TestPoolRetire(t *testing.T) {
}) })
} }
func TestPoolRequestHeader(t *testing.T) {
t.Run("default pool passes User-Agent header to DialContext", func(t *testing.T) {
config, _ := NewPoolConfig()
pool, _ := NewPool(context.Background(), config, nil)
var capturedHeader http.Header
pool.dialer = &honeybeetest.MockDialer{
DialContextFunc: func(
_ context.Context, _ string, h http.Header,
) (types.Socket, *http.Response, error) {
capturedHeader = h
return honeybeetest.NewMockSocket(), nil, nil
},
}
pool.Connect("wss://test")
expectEvent(t, pool.events, "wss://test", EventConnected)
assert.Equal(t, "honeybee/0.1.0", capturedHeader.Get("User-Agent"))
pool.Close()
})
t.Run("WithRequestHeader passes header to DialContext", func(t *testing.T) {
h := http.Header{}
h.Set("X-Custom", "value")
config, _ := NewPoolConfig(WithRequestHeader(h))
pool, _ := NewPool(context.Background(), config, nil)
var capturedHeader http.Header
pool.dialer = &honeybeetest.MockDialer{
DialContextFunc: func(
_ context.Context, _ string, h http.Header,
) (types.Socket, *http.Response, error) {
capturedHeader = h
return honeybeetest.NewMockSocket(), nil, nil
},
}
pool.Connect("wss://test")
expectEvent(t, pool.events, "wss://test", EventConnected)
assert.Equal(t, "value", capturedHeader.Get("X-Custom"))
pool.Close()
})
t.Run("pool stores a clone - external mutation does not affect dial calls", func(t *testing.T) {
h := http.Header{}
h.Set("X-Custom", "original")
config, _ := NewPoolConfig(WithRequestHeader(h))
// mutate original after config is built
h.Set("X-Custom", "mutated")
pool, _ := NewPool(context.Background(), config, nil)
var capturedHeader http.Header
pool.dialer = &honeybeetest.MockDialer{
DialContextFunc: func(
_ context.Context, _ string, h http.Header,
) (types.Socket, *http.Response, error) {
capturedHeader = h
return honeybeetest.NewMockSocket(), nil, nil
},
}
pool.Connect("wss://test")
expectEvent(t, pool.events, "wss://test", EventConnected)
assert.Equal(t, "original", capturedHeader.Get("X-Custom"))
pool.Close()
})
t.Run("WithRequestHeader(nil) stores nil cleanly", func(t *testing.T) {
cfg, err := NewPoolConfig(WithRequestHeader(nil))
assert.NoError(t, err)
assert.Nil(t, cfg.RequestHeader)
})
}
func TestPoolSend(t *testing.T) { func TestPoolSend(t *testing.T) {
mockSocket := honeybeetest.NewMockSocket() mockSocket := honeybeetest.NewMockSocket()
outgoingData := make(chan honeybeetest.MockOutgoingData, 10) outgoingData := make(chan honeybeetest.MockOutgoingData, 10)