Skip to content

Commit d968e9e

Browse files
committed
Add timeouts to the inital subflow dialing
Otherwise if you give multipath a bunch of dialers, with some of them not working (in the timeout failure case) the multipath package will not setup a connection in a timely manner, with this it will only wait 2 seconds for a subflow to come up online before moving on to the next one
1 parent 8fae22f commit d968e9e

File tree

2 files changed

+58
-54
lines changed

2 files changed

+58
-54
lines changed

multipath/dialer.go

+42-39
Original file line numberDiff line numberDiff line change
@@ -86,58 +86,61 @@ func NewDialer(dest string, dialers []Dialer) Dialer {
8686
return d
8787
}
8888

89+
func (mpd *mpDialer) dialOne(d *subflowDialer, cid connectionID, bc *mpConn, ctx context.Context) (connectionID, bool, *mpConn) {
90+
conn, err := d.DialContext(ctx)
91+
if err != nil {
92+
log.Errorf("failed to dial %s: %v", d.Label(), err)
93+
return zeroCID, false, bc
94+
}
95+
probeStart := time.Now()
96+
newCID, err := mpd.handshake(conn, cid)
97+
if err != nil {
98+
log.Errorf("failed to handshake %s, continuing: %v", d.Label(), err)
99+
conn.Close()
100+
return zeroCID, false, bc
101+
}
102+
if cid == zeroCID {
103+
bc = newMPConn(newCID)
104+
go func() {
105+
for {
106+
time.Sleep(time.Second)
107+
bc.pendingAckMu.RLock()
108+
oldest := time.Duration(0)
109+
oldestFN := uint64(0)
110+
for fn, frame := range bc.pendingAckMap {
111+
if time.Since(frame.sentAt) > oldest {
112+
oldest = time.Since(frame.sentAt)
113+
oldestFN = fn
114+
}
115+
}
116+
bc.pendingAckMu.RUnlock()
117+
if oldest > time.Second {
118+
log.Debugf("Frame %d has not been acked for %v\n", oldestFN, oldest)
119+
}
120+
}
121+
}()
122+
}
123+
bc.add(fmt.Sprintf("%x(%s)", newCID, d.label), conn, true, probeStart, d)
124+
return newCID, true, bc
125+
}
126+
89127
// DialContext dials the addr using all dialers and returns a connection
90128
// contains subflows from whatever dialers available.
91129
func (mpd *mpDialer) DialContext(ctx context.Context) (net.Conn, error) {
92130
var bc *mpConn
93-
dialOne := func(d *subflowDialer, cid connectionID) (connectionID, bool) {
94-
conn, err := d.DialContext(ctx)
95-
if err != nil {
96-
log.Errorf("failed to dial %s: %v", d.Label(), err)
97-
return zeroCID, false
98-
}
99-
probeStart := time.Now()
100-
newCID, err := mpd.handshake(conn, cid)
101-
if err != nil {
102-
log.Errorf("failed to handshake %s, continuing: %v", d.Label(), err)
103-
conn.Close()
104-
return zeroCID, false
105-
}
106-
if cid == zeroCID {
107-
bc = newMPConn(newCID)
108-
go func() {
109-
for {
110-
time.Sleep(time.Second)
111-
bc.pendingAckMu.RLock()
112-
oldest := time.Duration(0)
113-
oldestFN := uint64(0)
114-
for fn, frame := range bc.pendingAckMap {
115-
if time.Since(frame.sentAt) > oldest {
116-
oldest = time.Since(frame.sentAt)
117-
oldestFN = fn
118-
}
119-
}
120-
bc.pendingAckMu.RUnlock()
121-
if oldest > time.Second {
122-
log.Debugf("Frame %d has not been acked for %v\n", oldestFN, oldest)
123-
}
124-
}
125-
}()
126-
}
127-
bc.add(fmt.Sprintf("%x(%s)", newCID, d.label), conn, true, probeStart, d)
128-
return newCID, true
129-
}
130131
dialers := mpd.sorted()
131132
for i, d := range dialers {
132133
// dial the first connection with zero connection ID
133-
cid, ok := dialOne(d, zeroCID)
134+
dialctx, dialcancel := context.WithTimeout(ctx, time.Second*2)
135+
defer dialcancel()
136+
cid, ok, bc := mpd.dialOne(d, zeroCID, bc, dialctx)
134137
if !ok {
135138
continue
136139
}
137140
if i < len(dialers)-1 {
138141
// dial the rest in parallel with server assigned connection ID
139142
for _, d := range dialers[i+1:] {
140-
go dialOne(d, cid)
143+
go mpd.dialOne(d, cid, bc, ctx)
141144
}
142145
}
143146
return bc, nil

multipath/multipath.go

+16-15
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
// first subflow, the client sends an all-zero connnection ID (CID) and the
1111
// server sends the assigned CID back. Subsequent subflows use the same CID.
1212
//
13-
// ----------------------------------------------------
14-
// | version(1) | cid(16) | frames (...) |
15-
// ----------------------------------------------------
13+
// ----------------------------------------------------
14+
// | version(1) | cid(16) | frames (...) |
15+
// ----------------------------------------------------
1616
//
1717
// There are two types of frames. Data frame carries application data while ack
1818
// frame carries acknowledgement to the frame just received. When one data
@@ -21,28 +21,29 @@
2121
// variable-length integer encoding as described here:
2222
// https://tools.ietf.org/html/draft-ietf-quic-transport-29#section-16
2323
//
24-
// --------------------------------------------------------
25-
// | payload size(1-8) | frame number (1-8) | payload |
26-
// --------------------------------------------------------
24+
// --------------------------------------------------------
25+
// | payload size(1-8) | frame number (1-8) | payload |
26+
// --------------------------------------------------------
2727
//
28-
// ---------------------------------------
29-
// | 00000000 | ack frame number (1-8) |
30-
// ---------------------------------------
28+
// ---------------------------------------
29+
// | 00000000 | ack frame number (1-8) |
30+
// ---------------------------------------
3131
//
3232
// Ack frames with frame number < 10 are reserved for control. For now only 0
3333
// and 1 are used, for ping and pong frame respectively. They are for updating
3434
// RTT on inactive subflows and detecting recovered subflows.
3535
//
3636
// Ping frame:
37-
// -------------------------
38-
// | 00000000 | 00000000 |
39-
// -------------------------
37+
//
38+
// -------------------------
39+
// | 00000000 | 00000000 |
40+
// -------------------------
4041
//
4142
// Pong frame:
42-
// -------------------------
43-
// | 00000000 | 00000001 |
44-
// -------------------------
4543
//
44+
// -------------------------
45+
// | 00000000 | 00000001 |
46+
// -------------------------
4647
package multipath
4748

4849
import (

0 commit comments

Comments
 (0)