Skip to content

Commit df7ef2a

Browse files
LionsPhilstapelberg
authored andcommitted
robustsession: respect context cancellation in server()
As a result, we can increase the request timeout closer to the assumed IRC client limit.
1 parent 6c649ca commit df7ef2a

File tree

1 file changed

+18
-11
lines changed

1 file changed

+18
-11
lines changed

robustsession/robustsession.go

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ const (
3131
pathDeleteSession = "/robustirc/v1/%s"
3232
pathPostMessage = "/robustirc/v1/%s/message"
3333
pathGetMessages = "/robustirc/v1/%s/messages?lastseen=%s"
34-
requestTimeout = 4 * time.Minute
34+
// IRC clients generally time out around five minutes.
35+
// We want to beat them to it give better error diagnostics.
36+
requestTimeout = 290 * time.Second
3537
)
3638

3739
const Version = "RobustIRC Bridge v1.11"
@@ -176,8 +178,8 @@ func newNetwork(networkname string) (*Network, error) {
176178

177179
// server (eventually) returns the host:port to which we should connect to. In
178180
// case back-off prevents us from connecting anywhere right now, the function
179-
// blocks until back-off is over.
180-
func (n *Network) server(random bool) string {
181+
// blocks until back-off is over. Returns error on context cancellation.
182+
func (n *Network) server(ctx context.Context, random bool) (string, error) {
181183
n.mu.RLock()
182184
defer n.mu.RUnlock()
183185

@@ -190,7 +192,7 @@ func (n *Network) server(random bool) string {
190192
server := n.servers[rand.Intn(len(n.servers))]
191193
wait := n.backoff[server].next.Sub(time.Now())
192194
if wait <= 0 {
193-
return server
195+
return server, nil
194196
}
195197
}
196198
// Try to use the next available server, searching in offset order
@@ -200,18 +202,23 @@ func (n *Network) server(random bool) string {
200202
wait := n.backoff[n.servers[idx]].next.Sub(time.Now())
201203
if wait <= 0 {
202204
n.idxOffset = (idx + 1) % len(n.servers)
203-
return n.servers[idx]
205+
return n.servers[idx], nil
204206
}
205207
if wait < soonest {
206208
soonest = wait
207209
}
208210
}
209211

210-
time.Sleep(soonest)
212+
select {
213+
case <-ctx.Done():
214+
log.Print("Server selection aborted by context cancellation\n")
215+
return "", ctx.Err()
216+
case <-time.After(soonest):
217+
}
211218
}
212219

213220
// Unreached, but necessary for compiling with go1.0.2 (debian stable).
214-
return ""
221+
return "", fmt.Errorf("unreachable code reached")
215222
}
216223

217224
func (n *Network) setServers(servers []string) {
@@ -328,14 +335,14 @@ func (s *RobustSession) String() string {
328335

329336
func (s *RobustSession) sendRequest(ctx context.Context, method, path string, data []byte) (string, *http.Response, error) {
330337
for !s.isDeleted() {
331-
// NOTE: The Sleep() in server() can cause us to delay responding to
332-
// context cancellation up to the maximum backoff in failed(). (And thus
333-
// overrun timeouts by that much.)
334338
if err := ctx.Err(); err != nil {
335339
return "", nil, err
336340
}
337341
// GET requests are for read-only state and can be answered by any server.
338-
target := s.network.server(method == "GET")
342+
target, err := s.network.server(ctx, method == "GET")
343+
if err != nil {
344+
return "", nil, err
345+
}
339346
requrl := fmt.Sprintf("https://%s%s", target, path)
340347
nonHTTPTransport := strings.Contains(target, "/")
341348
if nonHTTPTransport {

0 commit comments

Comments
 (0)