internal/https: wait for both handlers in ListenAndServe
Fixes golang/go#29941
Change-Id: I15a50983bb8c52b4f55bfbfb4ebe5aee1b10c73a
Reviewed-on: https://go-review.googlesource.com/c/build/+/159701
Run-TryBot: Bryan C. Mills <bcmills@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/internal/https/https.go b/internal/https/https.go
index 54821c9..d9305da 100644
--- a/internal/https/https.go
+++ b/internal/https/https.go
@@ -47,22 +47,39 @@
if err != nil {
return fmt.Errorf(`net.Listen("tcp", %q): %v`, opt.Addr, err)
}
+ defer ln.Close()
+ if opt.AutocertCacheBucket == "" {
+ err := http.Serve(ln, handler)
+ ln.Close()
+ return fmt.Errorf("http.Serve = %v", err)
+ }
+
+ // handler is served primarily via HTTPS, so just redirect HTTP to HTTPS.
+ redirect := &http.Server{
+ Addr: ln.Addr().String(),
+ Handler: http.HandlerFunc(redirectToHTTPS),
+ }
errc := make(chan error)
go func() {
- var h http.Handler
- if opt.AutocertCacheBucket != "" {
- // handler is served primarily via HTTPS, so just redirect HTTP to HTTPS.
- h = http.HandlerFunc(redirectToHTTPS)
- } else {
- h = handler
- }
- errc <- fmt.Errorf("http.Serve = %v", http.Serve(ln, h))
+ err := redirect.Serve(ln)
+ errc <- fmt.Errorf("http.Serve = %v", err)
}()
- if opt.AutocertCacheBucket != "" {
- go func() { errc <- serveAutocertTLS(handler, opt.AutocertCacheBucket) }()
- }
- return <-errc
+
+ ctx, cancel := context.WithCancel(context.TODO())
+ go func() {
+ errc <- serveAutocertTLS(ctx, handler, opt.AutocertCacheBucket)
+ }()
+
+ // Wait for the first error.
+ err = <-errc
+
+ // Stop the other handler (whichever it may be) and wait for it to return.
+ redirect.Close()
+ cancel()
+ <-errc
+
+ return err
}
// redirectToHTTPS will redirect to the https version of the URL requested. If
@@ -80,13 +97,30 @@
// for its autocert cache. It will only serve on domains of the form *.golang.org.
//
// serveAutocertTLS always returns a non-nil error.
-func serveAutocertTLS(h http.Handler, bucket string) error {
+func serveAutocertTLS(ctx context.Context, h http.Handler, bucket string) error {
ln, err := net.Listen("tcp", ":443")
if err != nil {
return err
}
defer ln.Close()
- sc, err := storage.NewClient(context.Background())
+
+ ctx, cancel := context.WithCancel(ctx)
+ server := &http.Server{
+ Addr: ln.Addr().String(),
+ Handler: h,
+ }
+ done := make(chan struct{})
+ go func() {
+ <-ctx.Done()
+ server.Close()
+ close(done)
+ }()
+ defer func() {
+ cancel()
+ <-done
+ }()
+
+ sc, err := storage.NewClient(ctx)
if err != nil {
return fmt.Errorf("storage.NewClient: %v", err)
}
@@ -107,10 +141,6 @@
NextProtos: []string{"h2", "http/1.1"},
}
tlsLn := tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, config)
- server := &http.Server{
- Addr: ln.Addr().String(),
- Handler: h,
- }
if err := http2.ConfigureServer(server, nil); err != nil {
return fmt.Errorf("http2.ConfigureServer: %v", err)
}