Bound connection outbound queues
This commit is contained in:
parent
ca019756ec
commit
28c11e2fc2
3 changed files with 246 additions and 19 deletions
113
internal/network/conn_queue_test.go
Normal file
113
internal/network/conn_queue_test.go
Normal 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")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue