generated from soypat/go-module-template
-
Notifications
You must be signed in to change notification settings - Fork 5
/
seqsinternal_test.go
187 lines (166 loc) · 5.24 KB
/
seqsinternal_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
package seqs
import (
"errors"
"fmt"
"testing"
)
// Here we define internal testing helpers that may be used in any *_test.go file
// but are not exported.
// Exchange represents a single exchange of segments.
type Exchange struct {
Outgoing *Segment
Incoming *Segment
WantPending *Segment // Expected pending segment. If nil not checked.
WantState State // Expected end state.
WantPeerState State // Expected end state of peer. Not necessary when calling HelperExchange but can aid with logging information.
}
func (tcb *ControlBlock) HelperExchange(t *testing.T, exchange []Exchange) {
t.Helper()
const pfx = "exchange"
t.Log(tcb.state, "Exchange start")
for i, ex := range exchange {
if ex.Outgoing != nil && ex.Incoming != nil {
t.Fatalf(pfx+"[%d] cannot send and receive in the same exchange, please split into two exchanges.", i)
} else if ex.Outgoing == nil && ex.Incoming == nil {
t.Fatalf(pfx+"[%d] must send or receive a segment.", i)
}
if ex.Outgoing != nil {
prevInflight := tcb.snd.inFlight()
err := tcb.Send(*ex.Outgoing)
gotSent := tcb.snd.inFlight() - prevInflight
if err != nil {
t.Fatalf(pfx+"[%d] snd: %s\nseg=%+v\nrcv=%+v\nsnd=%+v", i, err, *ex.Outgoing, tcb.rcv, tcb.snd)
} else if gotSent != ex.Outgoing.LEN() {
t.Fatalf(pfx+"[%d] snd: expected %d data sent, calculated inflight %d", i, ex.Outgoing.LEN(), gotSent)
}
}
if ex.Incoming != nil {
err := tcb.Recv(*ex.Incoming)
if err != nil {
msg := fmt.Sprintf(pfx+"[%d] rcv: %s\nseg=%+v\nrcv=%+v\nsnd=%+v", i, err, *ex.Incoming, tcb.rcv, tcb.snd)
if IsDroppedErr(err) {
t.Log(msg)
} else {
t.Fatalf(msg)
}
}
}
t.Log(ex.RFC9293String(tcb.state, ex.WantPeerState))
state := tcb.State()
if state != ex.WantState {
t.Errorf(pfx+"[%d] unexpected state:\n got=%s\nwant=%s", i, state, ex.WantState)
}
pending, ok := tcb.PendingSegment(0)
if !ok && ex.WantPending != nil {
t.Fatalf(pfx+"[%d] pending:got none, want=%+v", i, *ex.WantPending)
} else if ex.WantPending != nil && pending != *ex.WantPending {
t.Errorf(pfx+"[%d] pending:\n got=%+v\nwant=%+v", i, pending, *ex.WantPending)
} else if ok && ex.WantPending == nil {
t.Errorf(pfx+"[%d] pending:\n got=%+v\nwant=none", i, pending)
}
}
}
func (tcb *ControlBlock) HelperInitState(state State, localISS, localNXT Value, localWindow Size) {
tcb.state = state
tcb.snd = sendSpace{
ISS: localISS,
UNA: localISS,
NXT: localNXT,
WND: 1, // 1 byte window, so we can test the SEQ field.
// UP, WL1, WL2 defaults to zero values.
}
tcb.rcv = recvSpace{
WND: localWindow,
}
}
func (tcb *ControlBlock) HelperInitRcv(irs, nxt Value, remoteWindow Size) {
tcb.rcv.IRS = irs
tcb.rcv.NXT = nxt
tcb.snd.WND = remoteWindow
}
func (tcb *ControlBlock) RelativeSendSpace() sendSpace {
snd := tcb.snd
snd.NXT -= snd.ISS
snd.UNA -= snd.ISS
snd.ISS = 0
return snd
}
func (tcb *ControlBlock) RelativeRecvSpace() recvSpace {
rcv := tcb.rcv
rcv.NXT -= rcv.IRS
rcv.IRS = 0
return rcv
}
func (tcb *ControlBlock) RelativeRecvSegment(seg Segment) Segment {
seg.SEQ -= tcb.rcv.IRS
seg.ACK -= tcb.snd.ISS
return seg
}
func (tcb *ControlBlock) RelativeSendSegment(seg Segment) Segment {
seg.SEQ -= tcb.snd.ISS
seg.ACK -= tcb.rcv.IRS
return seg
}
func (tcb *ControlBlock) RelativeAutoSegment(seg Segment) Segment {
rcv := tcb.RelativeRecvSegment(seg)
snd := tcb.RelativeSendSegment(seg)
if rcv.SEQ > snd.SEQ {
return snd
}
return rcv
}
func (tcb *ControlBlock) HelperPrintSegment(t *testing.T, isReceive bool, seg Segment) {
const fmtmsg = "\nSeg=%+v\nRcvSpace=%s\nSndSpace=%s"
rcv := tcb.RelativeRecvSpace()
rcvStr := rcv.RelativeGoString()
snd := tcb.RelativeSendSpace()
sndStr := snd.RelativeGoString()
t.Helper()
if isReceive {
t.Logf("RECV:"+fmtmsg, seg.RelativeGoString(tcb.rcv.IRS, tcb.snd.ISS), rcvStr, sndStr)
} else {
t.Logf("SEND:"+fmtmsg, seg.RelativeGoString(tcb.snd.ISS, tcb.rcv.IRS), rcvStr, sndStr)
}
}
func (rcv recvSpace) RelativeGoString() string {
return fmt.Sprintf("{NXT:%d} ", rcv.NXT-rcv.IRS)
}
func (rcv sendSpace) RelativeGoString() string {
nxt := rcv.NXT - rcv.ISS
una := rcv.UNA - rcv.ISS
unaLen := Sizeof(una, nxt)
if unaLen != 0 {
return fmt.Sprintf("{NXT:%d UNA:%d} (%d unacked)", nxt, una, unaLen)
}
return fmt.Sprintf("{NXT:%d UNA:%d}", nxt, una)
}
func (seg Segment) RelativeGoString(iseq, iack Value) string {
seglen := seg.LEN()
if seglen != seg.DATALEN {
// If SYN/FIN is set print out the length of the segment.
return fmt.Sprintf("{SEQ:%d ACK:%d DATALEN:%d Flags:%s} (LEN:%d)", seg.SEQ-iseq, seg.ACK-iack, seg.DATALEN, seg.Flags, seglen)
}
return fmt.Sprintf("{SEQ:%d ACK:%d DATALEN:%d Flags:%s} ", seg.SEQ-iseq, seg.ACK-iack, seg.DATALEN, seg.Flags)
}
// https://datatracker.ietf.org/doc/html/rfc9293#section-3.8.6.2.1
func (tcb *ControlBlock) UsableWindow() Size {
return Sizeof(tcb.snd.NXT, tcb.snd.UNA) + tcb.snd.WND
}
const (
RSTJump = rstJump
)
func IsDroppedErr(err error) bool {
return err != nil && errors.Is(err, errDropSegment)
}
func (ex *Exchange) RFC9293String(A, B State) string {
var seg Segment
sentByA := ex.Outgoing != nil
if sentByA {
seg = *ex.Outgoing
} else if ex.Incoming != nil {
seg = *ex.Incoming
} else {
return ""
}
return StringExchange(seg, A, B, !sentByA)
}