@@ -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
3739const 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
217224func (n * Network ) setServers (servers []string ) {
@@ -328,14 +335,14 @@ func (s *RobustSession) String() string {
328335
329336func (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