1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: Apache-2.0
19 Addr string // host:port where the server is listening.
23 listener *net.TCPListener
27 // Start is essentially (*http.Server)ListenAndServe() with two more
28 // features: (1) by the time Start() returns, Addr is changed to the
29 // address:port we ended up listening to -- which makes listening on
30 // ":0" useful in test suites -- and (2) the server can be shut down
31 // without killing the process -- which is useful in test cases, and
32 // makes it possible to shut down gracefully on SIGTERM without
33 // killing active connections.
34 func (srv *Server) Start() error {
35 addr, err := net.ResolveTCPAddr("tcp", srv.Addr)
39 srv.listener, err = listenTCP("tcp", addr)
43 srv.Addr = srv.listener.Addr().String()
45 mutex := &sync.RWMutex{}
46 srv.cond = sync.NewCond(mutex.RLocker())
49 lnr := tcpKeepAliveListener{srv.listener}
50 if srv.TLSConfig != nil {
51 err = srv.ServeTLS(lnr, "", "")
66 // Close shuts down the server and returns when it has stopped.
67 func (srv *Server) Close() error {
73 // Wait returns when the server has shut down.
74 func (srv *Server) Wait() error {
79 defer srv.cond.L.Unlock()
86 // tcpKeepAliveListener is copied from net/http because not exported.
87 type tcpKeepAliveListener struct {
91 func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
92 tc, err := ln.AcceptTCP()
97 tc.SetKeepAlivePeriod(3 * time.Minute)
101 // net.ListenTCP, but retry after "address already in use" for up to 5
102 // minutes if running inside the arvados test suite.
103 func listenTCP(network string, addr *net.TCPAddr) (*net.TCPListener, error) {
104 if os.Getenv("ARVADOS_TEST_API_HOST") == "" {
105 return net.ListenTCP("tcp", addr)
107 timeout := 5 * time.Minute
108 deadline := time.Now().Add(timeout)
111 ln, err := net.ListenTCP("tcp", addr)
112 if err != nil && strings.Contains(err.Error(), "address already in use") && time.Now().Before(deadline) {
114 log.Printf("listenTCP: retrying up to %v after error: %s", timeout, err)
117 time.Sleep(time.Second)