8784: Fix test for latest firefox.
[arvados.git] / sdk / go / httpserver / httpserver.go
1 package httpserver
2
3 import (
4         "net"
5         "net/http"
6         "sync"
7         "time"
8 )
9
10 type Server struct {
11         http.Server
12         Addr     string // host:port where the server is listening.
13         err      error
14         cond     *sync.Cond
15         running  bool
16         listener *net.TCPListener
17         wantDown bool
18 }
19
20 // Start is essentially (*http.Server)ListenAndServe() with two more
21 // features: (1) by the time Start() returns, Addr is changed to the
22 // address:port we ended up listening to -- which makes listening on
23 // ":0" useful in test suites -- and (2) the server can be shut down
24 // without killing the process -- which is useful in test cases, and
25 // makes it possible to shut down gracefully on SIGTERM without
26 // killing active connections.
27 func (srv *Server) Start() error {
28         addr, err := net.ResolveTCPAddr("tcp", srv.Addr)
29         if err != nil {
30                 return err
31         }
32         srv.listener, err = net.ListenTCP("tcp", addr)
33         if err != nil {
34                 return err
35         }
36         srv.Addr = srv.listener.Addr().String()
37
38         mutex := &sync.RWMutex{}
39         srv.cond = sync.NewCond(mutex.RLocker())
40         srv.running = true
41         go func() {
42                 err = srv.Serve(tcpKeepAliveListener{srv.listener})
43                 if !srv.wantDown {
44                         srv.err = err
45                 }
46                 mutex.Lock()
47                 srv.running = false
48                 srv.cond.Broadcast()
49                 mutex.Unlock()
50         }()
51         return nil
52 }
53
54 // Close shuts down the server and returns when it has stopped.
55 func (srv *Server) Close() error {
56         srv.wantDown = true
57         srv.listener.Close()
58         return srv.Wait()
59 }
60
61 // Wait returns when the server has shut down.
62 func (srv *Server) Wait() error {
63         if srv.cond == nil {
64                 return nil
65         }
66         srv.cond.L.Lock()
67         defer srv.cond.L.Unlock()
68         for srv.running {
69                 srv.cond.Wait()
70         }
71         return srv.err
72 }
73
74 // tcpKeepAliveListener is copied from net/http because not exported.
75 type tcpKeepAliveListener struct {
76         *net.TCPListener
77 }
78
79 func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
80         tc, err := ln.AcceptTCP()
81         if err != nil {
82                 return
83         }
84         tc.SetKeepAlive(true)
85         tc.SetKeepAlivePeriod(3 * time.Minute)
86         return tc, nil
87 }