go.uber.org/yarpc@v1.72.1/internal/net/httpserver.go (about) 1 // Copyright (c) 2022 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package net 22 23 import ( 24 "context" 25 "errors" 26 "net" 27 "net/http" 28 "sync" 29 30 "go.uber.org/atomic" 31 ) 32 33 var ( 34 errServerStopped = errors.New("the server has been stopped") 35 errAlreadyListening = errors.New("the server is already listening") 36 ) 37 38 // HTTPServer wraps an http.Server to listen asynchronously and allow stopping 39 // it. 40 type HTTPServer struct { 41 *http.Server 42 43 lock sync.RWMutex 44 listener net.Listener 45 done chan error 46 stopped atomic.Bool 47 } 48 49 // NewHTTPServer wraps the given http.Server into an HTTPServer. 50 func NewHTTPServer(s *http.Server) *HTTPServer { 51 return &HTTPServer{ 52 Server: s, 53 done: make(chan error, 1), 54 } 55 } 56 57 // Listener returns the listener for this server or nil if the server isn't 58 // yet listening. 59 func (h *HTTPServer) Listener() net.Listener { 60 h.lock.RLock() 61 listener := h.listener 62 h.lock.RUnlock() 63 return listener 64 } 65 66 // ListenAndServe starts the given HTTP server up in the background and 67 // returns immediately. The server listens on the configured Addr or ":http" 68 // if unconfigured. 69 // 70 // An error is returned if the server failed to start up, if the server was 71 // already listening, or if the server was stopped with Stop(). 72 func (h *HTTPServer) ListenAndServe() error { 73 if h.stopped.Load() { 74 return errServerStopped 75 } 76 77 h.lock.Lock() 78 defer h.lock.Unlock() 79 80 addr := h.Server.Addr 81 if addr == "" { 82 addr = ":http" 83 } 84 85 if h.listener != nil { 86 return errAlreadyListening 87 } 88 89 var err error 90 h.listener, err = net.Listen("tcp", addr) 91 if err != nil { 92 return err 93 } 94 95 go h.serve(h.listener) 96 return nil 97 } 98 99 // Serve starts the HTTP server up in the background on the given 100 // listener and returns immediately. 101 // 102 // An error is returned if the server failed to start up, if the server was 103 // already listening, or if the server was stopped with Stop(). 104 func (h *HTTPServer) Serve(lis net.Listener) error { 105 if h.stopped.Load() { 106 return errServerStopped 107 } 108 109 h.lock.Lock() 110 defer h.lock.Unlock() 111 112 if h.listener != nil { 113 return errAlreadyListening 114 } 115 116 h.listener = lis 117 go h.serve(h.listener) 118 return nil 119 } 120 121 func (h *HTTPServer) serve(listener net.Listener) { 122 // Serve always returns a non-nil error. For us, it's an error only if 123 // we didn't call Stop(). 124 err := h.Server.Serve(listener) 125 if !h.stopped.Load() { 126 h.done <- err 127 } else { 128 h.done <- nil 129 } 130 } 131 132 // Shutdown stops the server. An error is returned if the server stopped 133 // unexpectedly. 134 // 135 // Once a server is stopped, it cannot be started again with ListenAndServe. 136 func (h *HTTPServer) Shutdown(ctx context.Context) error { 137 if h.stopped.Swap(true) { 138 return nil 139 } 140 141 wasRunning, closeErr := h.shutdownServer(ctx) 142 if !wasRunning { 143 return nil 144 } 145 146 serveErr := <-h.done // wait until Serve() stops 147 if closeErr != nil { 148 return closeErr 149 } 150 return serveErr 151 } 152 153 func (h *HTTPServer) shutdownServer(ctx context.Context) (wasRunning bool, _ error) { 154 h.lock.Lock() 155 defer h.lock.Unlock() 156 157 if h.listener == nil { 158 return false, nil 159 } 160 161 err := h.Server.Shutdown(ctx) 162 163 // It's possible that the serve goroutine hasn't yet started, so the server 164 // might not know about the listener. We ignore errors since we may b 165 // closing the same listener twice. 166 h.listener.Close() 167 168 h.listener = nil 169 return true, err 170 }