Bound connection outbound queues

This commit is contained in:
Daniar Mannanov 2026-08-10 23:00:20 +03:00
parent ca019756ec
commit 28c11e2fc2
3 changed files with 246 additions and 19 deletions

View file

@ -0,0 +1,113 @@
package network
import (
"bufio"
"bytes"
"errors"
"io"
"net"
"sync"
"testing"
"time"
"regionio/internal/protocol"
)
type blockedConn struct {
mu sync.Mutex
closed bool
release chan struct{}
once sync.Once
}
func newBlockedConn() *blockedConn { return &blockedConn{release: make(chan struct{})} }
func (c *blockedConn) Read([]byte) (int, error) { return 0, io.EOF }
func (c *blockedConn) Write(p []byte) (int, error) {
<-c.release
c.mu.Lock()
defer c.mu.Unlock()
if c.closed {
return 0, net.ErrClosed
}
return len(p), nil
}
func (c *blockedConn) Close() error {
c.mu.Lock()
c.closed = true
c.mu.Unlock()
c.once.Do(func() { close(c.release) })
return nil
}
func (c *blockedConn) LocalAddr() net.Addr { return testAddr("local") }
func (c *blockedConn) RemoteAddr() net.Addr { return testAddr("remote") }
func (c *blockedConn) SetDeadline(time.Time) error { return nil }
func (c *blockedConn) SetReadDeadline(time.Time) error { return nil }
func (c *blockedConn) SetWriteDeadline(time.Time) error { return nil }
func TestConnWriterPreservesPacketOrder(t *testing.T) {
raw := &recordingConn{}
conn := NewConn(raw)
defer conn.Close()
for id := int32(1); id <= 3; id++ {
if err := conn.Send(id, []byte{byte(id)}); err != nil {
t.Fatal(err)
}
}
raw.mu.Lock()
data := append([]byte(nil), raw.buf.Bytes()...)
raw.mu.Unlock()
reader := bufio.NewReader(bytes.NewReader(data))
for want := int32(1); want <= 3; want++ {
packet, err := protocol.ReadPacket(reader, -1)
if err != nil {
t.Fatal(err)
}
if packet.ID != want || len(packet.Data) != 1 || packet.Data[0] != byte(want) {
t.Fatalf("packet %d = id %d data %v", want, packet.ID, packet.Data)
}
}
}
func TestConnEnqueueDisconnectsAtByteBudget(t *testing.T) {
raw := newBlockedConn()
conn := NewConn(raw)
body := make([]byte, protocol.MaxPacketSize)
var err error
for i := 0; i < 8; i++ {
err = conn.Enqueue(1, body)
if err != nil {
break
}
}
if !errors.Is(err, ErrOutboundFull) {
t.Fatalf("enqueue error = %v, want ErrOutboundFull", err)
}
select {
case <-conn.done:
case <-time.After(time.Second):
t.Fatal("queue overflow did not close connection")
}
if err := conn.Close(); err != nil {
t.Fatal(err)
}
}
func TestConnCloseUnblocksWaitingSend(t *testing.T) {
raw := newBlockedConn()
conn := NewConn(raw)
result := make(chan error, 1)
go func() { result <- conn.Send(1, []byte("blocked")) }()
time.Sleep(10 * time.Millisecond)
if err := conn.Close(); err != nil {
t.Fatal(err)
}
select {
case err := <-result:
if err == nil {
t.Fatal("blocked send succeeded after close")
}
case <-time.After(time.Second):
t.Fatal("Close did not unblock Send")
}
}