Skip to content

Commit a52d98e

Browse files
authored
Fix infinite wait for Shutdown() of local server (#51)
1 parent acbbdd6 commit a52d98e

File tree

1 file changed

+31
-25
lines changed

1 file changed

+31
-25
lines changed

server.go

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net"
88
"net/http"
99
"sync"
10+
"time"
1011

1112
"github.com/int128/listener"
1213
"golang.org/x/sync/errgroup"
@@ -27,34 +28,13 @@ func receiveCodeViaLocalServer(ctx context.Context, c *Config) (string, error) {
2728
respCh: respCh,
2829
}),
2930
}
31+
shutdownCh := make(chan struct{})
3032
var resp *authorizationResponse
3133
var eg errgroup.Group
32-
eg.Go(func() error {
33-
select {
34-
case gotResp, ok := <-respCh:
35-
if !ok {
36-
c.Logf("oauth2cli: response channel has been closed")
37-
return nil
38-
}
39-
resp = gotResp
40-
c.Logf("oauth2cli: shutting down the server at %s", l.Addr())
41-
if err := server.Shutdown(ctx); err != nil {
42-
return fmt.Errorf("could not shutdown the local server: %w", err)
43-
}
44-
return nil
45-
case <-ctx.Done():
46-
c.Logf("oauth2cli: context cancelled: %s", ctx.Err())
47-
c.Logf("oauth2cli: shutting down the server at %s", l.Addr())
48-
if err := server.Shutdown(ctx); err != nil {
49-
return fmt.Errorf("could not shutdown the local server: %w", err)
50-
}
51-
return fmt.Errorf("context cancelled while waiting for authorization response: %w", ctx.Err())
52-
}
53-
})
5434
eg.Go(func() error {
5535
defer close(respCh)
56-
c.Logf("oauth2cli: starting a local server at %s", l.Addr())
57-
defer c.Logf("oauth2cli: stopped the local server at %s", l.Addr())
36+
c.Logf("oauth2cli: starting a server at %s", l.Addr())
37+
defer c.Logf("oauth2cli: stopped the server")
5838
if c.isLocalServerHTTPS() {
5939
if err := server.ServeTLS(l, c.LocalServerCertFile, c.LocalServerKeyFile); err != nil && err != http.ErrServerClosed {
6040
return fmt.Errorf("could not start HTTPS server: %w", err)
@@ -66,13 +46,39 @@ func receiveCodeViaLocalServer(ctx context.Context, c *Config) (string, error) {
6646
}
6747
return nil
6848
})
49+
eg.Go(func() error {
50+
defer close(shutdownCh)
51+
select {
52+
case gotResp, ok := <-respCh:
53+
if ok {
54+
resp = gotResp
55+
}
56+
return nil
57+
case <-ctx.Done():
58+
return ctx.Err()
59+
}
60+
})
61+
eg.Go(func() error {
62+
<-shutdownCh
63+
// Gracefully shutdown the server in the timeout.
64+
// If the server has not started, Shutdown returns nil and this returns immediately.
65+
// If Shutdown has failed, force-close the server.
66+
c.Logf("oauth2cli: shutting down the server")
67+
ctx, cancel := context.WithTimeout(ctx, 500*time.Millisecond)
68+
defer cancel()
69+
if err := server.Shutdown(ctx); err != nil {
70+
c.Logf("oauth2cli: force-closing the server: shutdown failed: %s", err)
71+
_ = server.Close()
72+
return nil
73+
}
74+
return nil
75+
})
6976
eg.Go(func() error {
7077
if c.LocalServerReadyChan == nil {
7178
return nil
7279
}
7380
select {
7481
case c.LocalServerReadyChan <- c.OAuth2Config.RedirectURL:
75-
c.Logf("oauth2cli: wrote a URL to LocalServerReadyChan: %s", c.OAuth2Config.RedirectURL)
7682
return nil
7783
case <-ctx.Done():
7884
return ctx.Err()

0 commit comments

Comments
 (0)