New
This commit is contained in:
@@ -0,0 +1,301 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"dragontcp/internal/cover"
|
||||
"dragontcp/internal/wire"
|
||||
)
|
||||
|
||||
func writeBHTTPTestRequest(w io.Writer, mode byte, sid wire.SessionID, seq uint64, payload []byte, downloadHint uint32) error {
|
||||
n := uint32(len(payload))
|
||||
if mode == bhttpModeDownload {
|
||||
n = downloadHint
|
||||
payload = nil
|
||||
}
|
||||
packet := make([]byte, bhttpRequestHeaderSize+len(payload))
|
||||
packet[0] = mode
|
||||
copy(packet[1:17], sid[:])
|
||||
binary.BigEndian.PutUint64(packet[17:25], seq)
|
||||
binary.BigEndian.PutUint32(packet[25:29], n)
|
||||
copy(packet[29:], payload)
|
||||
wire.MaskInPlace(packet[29:], sid, mode, seq, false)
|
||||
_, err := w.Write(packet)
|
||||
return err
|
||||
}
|
||||
|
||||
func readBHTTPTestResponse(r io.Reader, sid wire.SessionID, mode byte, seq uint64) (byte, []byte, error) {
|
||||
status, body, err := wire.ReadResponse(r)
|
||||
if err == nil && status != wire.StatusError {
|
||||
wire.MaskInPlace(body, sid, mode, seq, true)
|
||||
}
|
||||
return status, body, err
|
||||
}
|
||||
|
||||
func startBHTTPTestServer(t *testing.T, sessions *bhttpSessionManager) (net.Conn, <-chan struct{}) {
|
||||
t.Helper()
|
||||
server, client := net.Pipe()
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer close(done)
|
||||
defer server.Close()
|
||||
handleBinary(
|
||||
server,
|
||||
0,
|
||||
false,
|
||||
"",
|
||||
false,
|
||||
newDNSCache(time.Minute, 16),
|
||||
0,
|
||||
newStreamManager(time.Minute, nil),
|
||||
sessions,
|
||||
1024*1024,
|
||||
1024*1024,
|
||||
10*time.Millisecond,
|
||||
nil,
|
||||
)
|
||||
}()
|
||||
return client, done
|
||||
}
|
||||
|
||||
func TestBHTTPReferenceSessionStack(t *testing.T) {
|
||||
sessions := newBHTTPSessionManager(time.Minute, 32)
|
||||
client, done := startBHTTPTestServer(t, sessions)
|
||||
defer func() {
|
||||
client.Close()
|
||||
<-done
|
||||
}()
|
||||
_ = client.SetDeadline(time.Now().Add(2 * time.Second))
|
||||
|
||||
var sid wire.SessionID
|
||||
for i := range sid {
|
||||
sid[i] = byte(i + 1)
|
||||
}
|
||||
|
||||
if err := writeBHTTPTestRequest(client, bhttpModeUpload, sid, 0, nil, 0); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if status, _, err := readBHTTPTestResponse(client, sid, bhttpModeUpload, 0); err != nil || status != wire.StatusOK {
|
||||
t.Fatalf("registration status=%d err=%v", status, err)
|
||||
}
|
||||
|
||||
if err := writeBHTTPTestRequest(client, bhttpModeUpload, sid, 1, []byte("Hello BHTTP"), 0); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if status, _, err := readBHTTPTestResponse(client, sid, bhttpModeUpload, 1); err != nil || status != wire.StatusOK {
|
||||
t.Fatalf("upload status=%d err=%v", status, err)
|
||||
}
|
||||
|
||||
// The size is in the header but no 1,350-byte body follows. This is the
|
||||
// framing difference that made the native Dragon parser wait forever.
|
||||
if err := writeBHTTPTestRequest(client, bhttpModeDownload, sid, 0, nil, 1350); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if status, _, err := readBHTTPTestResponse(client, sid, bhttpModeDownload, 0); err != nil || status != wire.StatusOK {
|
||||
t.Fatalf("download status=%d err=%v", status, err)
|
||||
}
|
||||
|
||||
batch := make([]byte, 6)
|
||||
binary.BigEndian.PutUint32(batch[:4], 1350)
|
||||
binary.BigEndian.PutUint16(batch[4:], 2)
|
||||
if err := writeBHTTPTestRequest(client, bhttpModeBatchDownload, sid, 0, batch, 0); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i := 0; i < 2; i++ {
|
||||
if status, _, err := readBHTTPTestResponse(client, sid, bhttpModeBatchDownload, 0); err != nil || status != wire.StatusOK {
|
||||
t.Fatalf("batch response %d status=%d err=%v", i, status, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := writeBHTTPTestRequest(client, bhttpModeACK, sid, 5, nil, 0); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if status, _, err := readBHTTPTestResponse(client, sid, bhttpModeACK, 5); err != nil || status != wire.StatusOK {
|
||||
t.Fatalf("ack status=%d err=%v", status, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBPExplicitCloseRemovesSession(t *testing.T) {
|
||||
sessions := newBHTTPSessionManager(time.Minute, 32)
|
||||
client, done := startBHTTPTestServer(t, sessions)
|
||||
defer func() {
|
||||
client.Close()
|
||||
<-done
|
||||
}()
|
||||
_ = client.SetDeadline(time.Now().Add(2 * time.Second))
|
||||
|
||||
var sid wire.SessionID
|
||||
copy(sid[:], []byte("close-session-01"))
|
||||
if err := writeBHTTPTestRequest(client, bhttpModeUpload, sid, 0, nil, 0); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if status, _, err := readBHTTPTestResponse(client, sid, bhttpModeUpload, 0); err != nil || status != wire.StatusOK {
|
||||
t.Fatalf("registration status=%d err=%v", status, err)
|
||||
}
|
||||
if sessions.get(sid) == nil {
|
||||
t.Fatal("registered session is missing")
|
||||
}
|
||||
|
||||
if err := writeBHTTPTestRequest(client, bhttpModeACK, sid, 0, bpCloseMagic[:], 0); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if status, _, err := readBHTTPTestResponse(client, sid, bhttpModeACK, 0); err != nil || status != wire.StatusOK {
|
||||
t.Fatalf("close status=%d err=%v", status, err)
|
||||
}
|
||||
if sessions.get(sid) != nil {
|
||||
t.Fatal("explicit close retained the session")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBHTTPReferenceProbeAndBatchEcho(t *testing.T) {
|
||||
sessions := newBHTTPSessionManager(time.Minute, 32)
|
||||
client, done := startBHTTPTestServer(t, sessions)
|
||||
defer func() {
|
||||
client.Close()
|
||||
<-done
|
||||
}()
|
||||
_ = client.SetDeadline(time.Now().Add(2 * time.Second))
|
||||
|
||||
var sid wire.SessionID
|
||||
copy(sid[:], []byte("probe-session-01"))
|
||||
payload := make([]byte, 10)
|
||||
copy(payload[:4], []byte("BHP1"))
|
||||
payload[4] = 1
|
||||
payload[5] = bhttpModeDownload
|
||||
binary.BigEndian.PutUint32(payload[6:], 512)
|
||||
if err := writeBHTTPTestRequest(client, bhttpModeProbe, sid, 0, payload, 0); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
status, body, err := readBHTTPTestResponse(client, sid, bhttpModeProbe, 0)
|
||||
if err != nil || status != wire.StatusOK || len(body) != 512 || !bytes.Equal(body[:10], payload) {
|
||||
t.Fatalf("download probe status=%d len=%d err=%v", status, len(body), err)
|
||||
}
|
||||
for i := 10; i < len(body); i++ {
|
||||
if body[i] != byte(i*31) {
|
||||
t.Fatalf("probe pattern byte %d=%02x", i, body[i])
|
||||
}
|
||||
}
|
||||
|
||||
payload[5] = bhttpModeACK
|
||||
binary.BigEndian.PutUint32(payload[6:], 3)
|
||||
if err := writeBHTTPTestRequest(client, bhttpModeProbe, sid, 0, payload, 0); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i := 0; i < 3; i++ {
|
||||
status, body, err := readBHTTPTestResponse(client, sid, bhttpModeProbe, 0)
|
||||
if err != nil || status != wire.StatusOK || !bytes.Equal(body, payload) {
|
||||
t.Fatalf("batch probe %d status=%d body=%x err=%v", i, status, body, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBHTTPUnknownSessionDownloadHasNoBody(t *testing.T) {
|
||||
sessions := newBHTTPSessionManager(time.Minute, 32)
|
||||
client, done := startBHTTPTestServer(t, sessions)
|
||||
defer func() {
|
||||
client.Close()
|
||||
<-done
|
||||
}()
|
||||
_ = client.SetDeadline(time.Now().Add(2 * time.Second))
|
||||
|
||||
var sid wire.SessionID
|
||||
copy(sid[:], []byte("unknown-session!"))
|
||||
if err := writeBHTTPTestRequest(client, bhttpModeDownload, sid, 0, nil, 1350); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
status, _, err := wire.ReadResponse(client)
|
||||
if err != nil || status == wire.StatusOK || status == wire.StatusData {
|
||||
t.Fatalf("unknown session status=%d err=%v", status, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBinaryAutoDetectionKeepsNativeDragonProbe(t *testing.T) {
|
||||
sessions := newBHTTPSessionManager(time.Minute, 32)
|
||||
client, done := startBHTTPTestServer(t, sessions)
|
||||
defer func() {
|
||||
client.Close()
|
||||
<-done
|
||||
}()
|
||||
_ = client.SetDeadline(time.Now().Add(2 * time.Second))
|
||||
|
||||
var sid wire.SessionID
|
||||
payload := make([]byte, 11)
|
||||
copy(payload[:4], wire.ProbeMagic[:])
|
||||
payload[4] = wire.ProbeKeepalive
|
||||
if err := wire.WriteRequest(client, wire.ModeProbe, sid, 1, payload); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
status, _, err := wire.ReadResponse(client)
|
||||
if err != nil || status != wire.StatusOK {
|
||||
t.Fatalf("native probe status=%d err=%v", status, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClearCoveredBinaryAndBPProfiles(t *testing.T) {
|
||||
for _, bp := range []bool{false, true} {
|
||||
t.Run(map[bool]string{false: "B", true: "BP"}[bp], func(t *testing.T) {
|
||||
server, client := net.Pipe()
|
||||
profile := cover.Profile{Enabled: true, ID: 0x8173, Padding: 32, HeaderMask: 0x9b, Clear: true}
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer close(done)
|
||||
defer server.Close()
|
||||
profiled, isXOR, mask, err := sniffWire(server)
|
||||
if err != nil || isXOR {
|
||||
return
|
||||
}
|
||||
handleBinary(
|
||||
profiled, mask, true, "", false,
|
||||
newDNSCache(time.Minute, 16), 0,
|
||||
newStreamManager(time.Minute, nil),
|
||||
newBHTTPSessionManager(time.Minute, 32),
|
||||
1024*1024, 1024*1024, 10*time.Millisecond, nil,
|
||||
)
|
||||
}()
|
||||
defer func() {
|
||||
client.Close()
|
||||
<-done
|
||||
}()
|
||||
_ = client.SetDeadline(time.Now().Add(2 * time.Second))
|
||||
if err := cover.WritePreface(client, profile); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var sid wire.SessionID
|
||||
copy(sid[:], []byte("clear-profile-01"))
|
||||
if bp {
|
||||
payload := makeBHTTPProbe(bhttpModeDownload, 256)[:10]
|
||||
packet := make([]byte, bhttpRequestHeaderSize+len(payload))
|
||||
packet[0] = bhttpModeProbe ^ profile.HeaderMask
|
||||
copy(packet[1:17], sid[:])
|
||||
binary.BigEndian.PutUint32(packet[25:29], uint32(len(payload)))
|
||||
copy(packet[29:], payload)
|
||||
if _, err := client.Write(packet); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
status, body, err := wire.ReadResponseProfile(client, profile.HeaderMask)
|
||||
if err != nil || status != wire.StatusOK || !bytes.Equal(body, makeBHTTPProbe(bhttpModeDownload, 256)) {
|
||||
t.Fatalf("clear BP status=%d len=%d err=%v", status, len(body), err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
payload := make([]byte, 11)
|
||||
copy(payload[:4], wire.ProbeMagic[:])
|
||||
payload[4] = wire.ProbeDownload
|
||||
binary.BigEndian.PutUint32(payload[7:11], 256)
|
||||
if err := wire.WriteRequestProfileEncoding(client, wire.ModeProbe, sid, 7, payload, profile.HeaderMask, true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
status, body, err := wire.ReadResponseProfile(client, profile.HeaderMask)
|
||||
if err != nil || status != wire.StatusData || !bytes.Equal(body, probePattern(256)) {
|
||||
t.Fatalf("clear B status=%d len=%d err=%v", status, len(body), err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user