213 lines
6.8 KiB
Go
213 lines
6.8 KiB
Go
|
|
package notify
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"errors"
|
||
|
|
"math"
|
||
|
|
"net"
|
||
|
|
"testing"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"b612.me/stario"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestClientConnCurrentTransportConnStreamSnapshot(t *testing.T) {
|
||
|
|
server := NewServer().(*ServerCommon)
|
||
|
|
left, right := net.Pipe()
|
||
|
|
defer left.Close()
|
||
|
|
defer right.Close()
|
||
|
|
|
||
|
|
logical := server.bootstrapAcceptedLogical("transport-stream", nil, left)
|
||
|
|
if logical == nil {
|
||
|
|
t.Fatal("bootstrapAcceptedLogical should return logical")
|
||
|
|
}
|
||
|
|
|
||
|
|
transport := logical.CurrentTransportConn()
|
||
|
|
if transport == nil {
|
||
|
|
t.Fatal("CurrentTransportConn should expose active stream transport")
|
||
|
|
}
|
||
|
|
if transport.LogicalConn() != logical {
|
||
|
|
t.Fatal("LogicalConn mismatch")
|
||
|
|
}
|
||
|
|
if !transport.Attached() {
|
||
|
|
t.Fatal("Attached mismatch: got false want true")
|
||
|
|
}
|
||
|
|
if !transport.HasRuntimeConn() {
|
||
|
|
t.Fatal("HasRuntimeConn mismatch: got false want true")
|
||
|
|
}
|
||
|
|
if !transport.UsesStreamTransport() {
|
||
|
|
t.Fatal("UsesStreamTransport mismatch: got false want true")
|
||
|
|
}
|
||
|
|
if !transport.IsCurrent() {
|
||
|
|
t.Fatal("IsCurrent mismatch: got false want true")
|
||
|
|
}
|
||
|
|
snapshot, err := GetTransportConnRuntimeSnapshot(transport)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("GetTransportConnRuntimeSnapshot failed: %v", err)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.ClientID, logical.ClientID; got != want {
|
||
|
|
t.Fatalf("ClientID mismatch: got %q want %q", got, want)
|
||
|
|
}
|
||
|
|
if !snapshot.Attached || !snapshot.HasRuntimeConn || !snapshot.Current {
|
||
|
|
t.Fatalf("unexpected transport snapshot: %+v", snapshot)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.BindingOwner, "server-transport"; got != want {
|
||
|
|
t.Fatalf("BindingOwner mismatch: got %q want %q", got, want)
|
||
|
|
}
|
||
|
|
if !snapshot.BindingCurrent || !snapshot.LogicalAlive {
|
||
|
|
t.Fatalf("binding state mismatch: %+v", snapshot)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestClientConnCurrentTransportConnPacketSnapshot(t *testing.T) {
|
||
|
|
server := NewServer().(*ServerCommon)
|
||
|
|
stopCtx, stopFn := context.WithCancel(context.Background())
|
||
|
|
defer stopFn()
|
||
|
|
udpListener, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 0})
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("ListenUDP failed: %v", err)
|
||
|
|
}
|
||
|
|
defer udpListener.Close()
|
||
|
|
server.setServerSessionRuntime(&serverSessionRuntime{
|
||
|
|
stopCtx: stopCtx,
|
||
|
|
stopFn: stopFn,
|
||
|
|
udpListener: udpListener,
|
||
|
|
})
|
||
|
|
|
||
|
|
addr, err := net.ResolveUDPAddr("udp", "127.0.0.1:34567")
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("ResolveUDPAddr failed: %v", err)
|
||
|
|
}
|
||
|
|
logical := server.bootstrapAcceptedLogical("transport-packet", addr, nil)
|
||
|
|
if logical == nil {
|
||
|
|
t.Fatal("bootstrapAcceptedLogical should return packet logical")
|
||
|
|
}
|
||
|
|
|
||
|
|
transport := logical.CurrentTransportConn()
|
||
|
|
if transport == nil {
|
||
|
|
t.Fatal("CurrentTransportConn should expose packet transport route")
|
||
|
|
}
|
||
|
|
if got, want := transport.RemoteAddr().String(), addr.String(); got != want {
|
||
|
|
t.Fatalf("RemoteAddr mismatch: got %q want %q", got, want)
|
||
|
|
}
|
||
|
|
if !transport.Attached() {
|
||
|
|
t.Fatal("Attached mismatch: got false want true")
|
||
|
|
}
|
||
|
|
if transport.HasRuntimeConn() {
|
||
|
|
t.Fatal("packet transport should not expose runtime conn")
|
||
|
|
}
|
||
|
|
if transport.TransportGeneration() != 0 {
|
||
|
|
t.Fatalf("packet transport generation mismatch: got %d want 0", transport.TransportGeneration())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestTransportConnSendRejectsStaleGenerationAfterReattach(t *testing.T) {
|
||
|
|
server := NewServer().(*ServerCommon)
|
||
|
|
UseLegacySecurityServer(server)
|
||
|
|
|
||
|
|
runtimeCtx, runtimeCancel := context.WithCancel(context.Background())
|
||
|
|
defer runtimeCancel()
|
||
|
|
queue := stario.NewQueueCtx(runtimeCtx, 4, math.MaxUint32)
|
||
|
|
server.setServerSessionRuntime(&serverSessionRuntime{
|
||
|
|
stopCtx: runtimeCtx,
|
||
|
|
stopFn: runtimeCancel,
|
||
|
|
queue: queue,
|
||
|
|
})
|
||
|
|
server.markSessionStarted()
|
||
|
|
defer server.markSessionStopped("test done", nil)
|
||
|
|
|
||
|
|
firstLeft, firstRight := net.Pipe()
|
||
|
|
defer firstRight.Close()
|
||
|
|
logical, _, _ := newRegisteredServerLogicalForTest(t, server, "transport-send-stale", firstLeft, runtimeCtx, runtimeCancel)
|
||
|
|
logical.applyClientConnAttachmentProfile(0, 100*time.Millisecond, server.defaultMsgEn, server.defaultMsgDe, server.handshakeRsaKey, server.SecretKey)
|
||
|
|
firstTransport := logical.CurrentTransportConn()
|
||
|
|
if firstTransport == nil {
|
||
|
|
t.Fatal("first transport snapshot should exist")
|
||
|
|
}
|
||
|
|
|
||
|
|
secondLeft, secondRight := net.Pipe()
|
||
|
|
defer secondRight.Close()
|
||
|
|
if err := logical.attachClientConnSessionTransport(secondLeft); err != nil {
|
||
|
|
t.Fatalf("attachClientConnSessionTransport failed: %v", err)
|
||
|
|
}
|
||
|
|
secondTransport := logical.CurrentTransportConn()
|
||
|
|
if secondTransport == nil {
|
||
|
|
t.Fatal("second transport snapshot should exist after reattach")
|
||
|
|
}
|
||
|
|
if firstTransport.IsCurrent() {
|
||
|
|
t.Fatal("first transport should become stale after reattach")
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := firstTransport.Send("stale", MsgVal("payload")); !errors.Is(err, errTransportDetached) {
|
||
|
|
t.Fatalf("stale transport send error = %v, want errors.Is(..., %v)", err, errTransportDetached)
|
||
|
|
}
|
||
|
|
|
||
|
|
recvCh := make(chan []byte, 1)
|
||
|
|
errCh := make(chan error, 1)
|
||
|
|
go func() {
|
||
|
|
_ = secondRight.SetReadDeadline(time.Now().Add(time.Second))
|
||
|
|
reader := stario.NewFrameReader(secondRight, nil)
|
||
|
|
payload, err := reader.Next()
|
||
|
|
if err != nil {
|
||
|
|
errCh <- err
|
||
|
|
return
|
||
|
|
}
|
||
|
|
recvCh <- payload
|
||
|
|
}()
|
||
|
|
|
||
|
|
if err := secondTransport.Send("fresh", MsgVal("payload")); err != nil {
|
||
|
|
t.Fatalf("fresh transport send failed: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
select {
|
||
|
|
case err := <-errCh:
|
||
|
|
t.Fatalf("fresh transport read failed: %v", err)
|
||
|
|
case got := <-recvCh:
|
||
|
|
if len(got) == 0 {
|
||
|
|
t.Fatal("fresh transport should produce framed payload")
|
||
|
|
}
|
||
|
|
case <-time.After(time.Second):
|
||
|
|
t.Fatal("fresh transport send timed out")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestTransportConnRuntimeSnapshotIncludesDetachDiagnostics(t *testing.T) {
|
||
|
|
server := NewServer().(*ServerCommon)
|
||
|
|
left, right := net.Pipe()
|
||
|
|
defer right.Close()
|
||
|
|
|
||
|
|
logical := server.bootstrapAcceptedLogical("transport-detach", nil, left)
|
||
|
|
if logical == nil {
|
||
|
|
t.Fatal("bootstrapAcceptedLogical should return logical")
|
||
|
|
}
|
||
|
|
transport := logical.CurrentTransportConn()
|
||
|
|
if transport == nil {
|
||
|
|
t.Fatal("CurrentTransportConn should return active transport")
|
||
|
|
}
|
||
|
|
|
||
|
|
server.detachLogicalSessionTransport(logical, "read error", errors.New("boom"))
|
||
|
|
|
||
|
|
snapshot, err := GetTransportConnRuntimeSnapshot(transport)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("GetTransportConnRuntimeSnapshot failed: %v", err)
|
||
|
|
}
|
||
|
|
if snapshot.Current {
|
||
|
|
t.Fatalf("snapshot Current should be false after detach: %+v", snapshot)
|
||
|
|
}
|
||
|
|
if snapshot.BindingCurrent {
|
||
|
|
t.Fatalf("snapshot BindingCurrent should be false after detach: %+v", snapshot)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.LogicalReason, ""; got != want {
|
||
|
|
t.Fatalf("snapshot LogicalReason = %q, want %q", got, want)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.TransportDetachReason, "read error"; got != want {
|
||
|
|
t.Fatalf("snapshot TransportDetachReason = %q, want %q", got, want)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.TransportDetachKind, clientConnTransportDetachKindReadError; got != want {
|
||
|
|
t.Fatalf("snapshot TransportDetachKind = %q, want %q", got, want)
|
||
|
|
}
|
||
|
|
if got, want := snapshot.TransportDetachError, "boom"; got != want {
|
||
|
|
t.Fatalf("snapshot TransportDetachError = %q, want %q", got, want)
|
||
|
|
}
|
||
|
|
}
|