github.com/vmware/govmomi@v0.37.1/simulator/internal/server.go (about) 1 /* 2 Copyright (c) 2014-2022 VMware, Inc. All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package internal 18 19 import ( 20 "crypto/tls" 21 "crypto/x509" 22 "fmt" 23 "log" 24 "net" 25 "net/http" 26 "strings" 27 "sync" 28 "time" 29 ) 30 31 // A Server is an HTTP server listening on a system-chosen port on the 32 // local loopback interface, for use in end-to-end HTTP tests. 33 type Server struct { 34 URL string // base URL of form http://ipaddr:port with no trailing slash 35 Listener net.Listener 36 37 // TLS is the optional TLS configuration, populated with a new config 38 // after TLS is started. If set on an unstarted server before StartTLS 39 // is called, existing fields are copied into the new config. 40 TLS *tls.Config 41 42 // Config may be changed after calling NewUnstartedServer and 43 // before Start or StartTLS. 44 Config *http.Server 45 46 // certificate is a parsed version of the TLS config certificate, if present. 47 certificate *x509.Certificate 48 49 // wg counts the number of outstanding HTTP requests on this server. 50 // Close blocks until all requests are finished. 51 wg sync.WaitGroup 52 53 mu sync.Mutex // guards closed and conns 54 closed bool 55 conns map[net.Conn]http.ConnState // except terminal states 56 57 // client is configured for use with the server. 58 // Its transport is automatically closed when Close is called. 59 client *http.Client 60 } 61 62 func newLocalListener(serve string) net.Listener { 63 if serve != "" { 64 l, err := net.Listen("tcp", serve) 65 if err != nil { 66 panic(fmt.Sprintf("httptest: failed to listen on %v: %v", serve, err)) 67 } 68 return l 69 } 70 l, err := net.Listen("tcp", "127.0.0.1:0") 71 if err != nil { 72 if l, err = net.Listen("tcp6", "[::1]:0"); err != nil { 73 panic(fmt.Sprintf("httptest: failed to listen on a port: %v", err)) 74 } 75 } 76 return l 77 } 78 79 // NewServer starts and returns a new Server. 80 // The caller should call Close when finished, to shut it down. 81 func NewServer(handler http.Handler) *Server { 82 ts := NewUnstartedServer(handler, "") 83 ts.Start() 84 return ts 85 } 86 87 // NewUnstartedServer returns a new Server but doesn't start it. 88 // 89 // After changing its configuration, the caller should call Start or 90 // StartTLS. 91 // 92 // The caller should call Close when finished, to shut it down. 93 // serve allows the server's listen address to be specified. 94 func NewUnstartedServer(handler http.Handler, serve string) *Server { 95 return &Server{ 96 Listener: newLocalListener(serve), 97 Config: &http.Server{Handler: handler}, 98 } 99 } 100 101 // Start starts a server from NewUnstartedServer. 102 func (s *Server) Start() { 103 if s.URL != "" { 104 panic("Server already started") 105 } 106 if s.client == nil { 107 s.client = &http.Client{Transport: &http.Transport{}} 108 } 109 s.URL = "http://" + s.Listener.Addr().String() 110 s.wrap() 111 s.goServe() 112 } 113 114 // StartTLS starts TLS on a server from NewUnstartedServer. 115 func (s *Server) StartTLS() { 116 if s.URL != "" { 117 panic("Server already started") 118 } 119 if s.client == nil { 120 s.client = &http.Client{Transport: &http.Transport{}} 121 } 122 cert, err := tls.X509KeyPair(LocalhostCert, LocalhostKey) 123 if err != nil { 124 panic(fmt.Sprintf("httptest: NewTLSServer: %v", err)) 125 } 126 127 existingConfig := s.TLS 128 if existingConfig != nil { 129 s.TLS = existingConfig.Clone() 130 } else { 131 s.TLS = new(tls.Config) 132 } 133 if s.TLS.NextProtos == nil { 134 s.TLS.NextProtos = []string{"http/1.1"} 135 } 136 if len(s.TLS.Certificates) == 0 { 137 s.TLS.Certificates = []tls.Certificate{cert} 138 } 139 s.certificate, err = x509.ParseCertificate(s.TLS.Certificates[0].Certificate[0]) 140 if err != nil { 141 panic(fmt.Sprintf("httptest: NewTLSServer: %v", err)) 142 } 143 certpool := x509.NewCertPool() 144 certpool.AddCert(s.certificate) 145 s.client.Transport = &http.Transport{ 146 TLSClientConfig: &tls.Config{ 147 RootCAs: certpool, 148 }, 149 } 150 s.Listener = tls.NewListener(s.Listener, s.TLS) 151 s.URL = "https://" + s.Listener.Addr().String() 152 s.wrap() 153 s.goServe() 154 } 155 156 // NewTLSServer starts and returns a new Server using TLS. 157 // The caller should call Close when finished, to shut it down. 158 func NewTLSServer(handler http.Handler) *Server { 159 ts := NewUnstartedServer(handler, "") 160 ts.StartTLS() 161 return ts 162 } 163 164 type closeIdleTransport interface { 165 CloseIdleConnections() 166 } 167 168 // Close shuts down the server and blocks until all outstanding 169 // requests on this server have completed. 170 func (s *Server) Close() { 171 s.mu.Lock() 172 if !s.closed { 173 s.closed = true 174 s.Listener.Close() 175 s.Config.SetKeepAlivesEnabled(false) 176 for c, st := range s.conns { 177 // Force-close any idle connections (those between 178 // requests) and new connections (those which connected 179 // but never sent a request). StateNew connections are 180 // super rare and have only been seen (in 181 // previously-flaky tests) in the case of 182 // socket-late-binding races from the http Client 183 // dialing this server and then getting an idle 184 // connection before the dial completed. There is thus 185 // a connected connection in StateNew with no 186 // associated Request. We only close StateIdle and 187 // StateNew because they're not doing anything. It's 188 // possible StateNew is about to do something in a few 189 // milliseconds, but a previous CL to check again in a 190 // few milliseconds wasn't liked (early versions of 191 // https://golang.org/cl/15151) so now we just 192 // forcefully close StateNew. The docs for Server.Close say 193 // we wait for "outstanding requests", so we don't close things 194 // in StateActive. 195 if st == http.StateIdle || st == http.StateNew { 196 s.closeConn(c) 197 } 198 } 199 // If this server doesn't shut down in 5 seconds, tell the user why. 200 t := time.AfterFunc(5*time.Second, s.logCloseHangDebugInfo) 201 defer t.Stop() 202 } 203 s.mu.Unlock() 204 205 // Not part of httptest.Server's correctness, but assume most 206 // users of httptest.Server will be using the standard 207 // transport, so help them out and close any idle connections for them. 208 if t, ok := http.DefaultTransport.(closeIdleTransport); ok { 209 t.CloseIdleConnections() 210 } 211 212 // Also close the client idle connections. 213 if s.client != nil { 214 if t, ok := s.client.Transport.(closeIdleTransport); ok { 215 t.CloseIdleConnections() 216 } 217 } 218 219 s.wg.Wait() 220 } 221 222 func (s *Server) logCloseHangDebugInfo() { 223 s.mu.Lock() 224 defer s.mu.Unlock() 225 var buf strings.Builder 226 buf.WriteString("httptest.Server blocked in Close after 5 seconds, waiting for connections:\n") 227 for c, st := range s.conns { 228 fmt.Fprintf(&buf, " %T %p %v in state %v\n", c, c, c.RemoteAddr(), st) 229 } 230 log.Print(buf.String()) 231 } 232 233 // CloseClientConnections closes any open HTTP connections to the test Server. 234 func (s *Server) CloseClientConnections() { 235 s.mu.Lock() 236 nconn := len(s.conns) 237 ch := make(chan struct{}, nconn) 238 for c := range s.conns { 239 go s.closeConnChan(c, ch) 240 } 241 s.mu.Unlock() 242 243 // Wait for outstanding closes to finish. 244 // 245 // Out of paranoia for making a late change in Go 1.6, we 246 // bound how long this can wait, since golang.org/issue/14291 247 // isn't fully understood yet. At least this should only be used 248 // in tests. 249 timer := time.NewTimer(5 * time.Second) 250 defer timer.Stop() 251 for i := 0; i < nconn; i++ { 252 select { 253 case <-ch: 254 case <-timer.C: 255 // Too slow. Give up. 256 return 257 } 258 } 259 } 260 261 // Certificate returns the certificate used by the server, or nil if 262 // the server doesn't use TLS. 263 func (s *Server) Certificate() *x509.Certificate { 264 return s.certificate 265 } 266 267 // Client returns an HTTP client configured for making requests to the server. 268 // It is configured to trust the server's TLS test certificate and will 269 // close its idle connections on Server.Close. 270 func (s *Server) Client() *http.Client { 271 return s.client 272 } 273 274 func (s *Server) goServe() { 275 s.wg.Add(1) 276 go func() { 277 defer s.wg.Done() 278 s.Config.Serve(s.Listener) 279 }() 280 } 281 282 // wrap installs the connection state-tracking hook to know which 283 // connections are idle. 284 func (s *Server) wrap() { 285 oldHook := s.Config.ConnState 286 s.Config.ConnState = func(c net.Conn, cs http.ConnState) { 287 s.mu.Lock() 288 defer s.mu.Unlock() 289 290 switch cs { 291 case http.StateNew: 292 if _, exists := s.conns[c]; exists { 293 panic("invalid state transition") 294 } 295 if s.conns == nil { 296 s.conns = make(map[net.Conn]http.ConnState) 297 } 298 // Add c to the set of tracked conns and increment it to the 299 // waitgroup. 300 s.wg.Add(1) 301 s.conns[c] = cs 302 if s.closed { 303 // Probably just a socket-late-binding dial from 304 // the default transport that lost the race (and 305 // thus this connection is now idle and will 306 // never be used). 307 s.closeConn(c) 308 } 309 case http.StateActive: 310 if oldState, ok := s.conns[c]; ok { 311 if oldState != http.StateNew && oldState != http.StateIdle { 312 panic("invalid state transition") 313 } 314 s.conns[c] = cs 315 } 316 case http.StateIdle: 317 if oldState, ok := s.conns[c]; ok { 318 if oldState != http.StateActive { 319 panic("invalid state transition") 320 } 321 s.conns[c] = cs 322 } 323 if s.closed { 324 s.closeConn(c) 325 } 326 case http.StateHijacked, http.StateClosed: 327 // Remove c from the set of tracked conns and decrement it from the 328 // waitgroup, unless it was previously removed. 329 if _, ok := s.conns[c]; ok { 330 delete(s.conns, c) 331 // Keep Close from returning until the user's ConnState hook 332 // (if any) finishes. 333 defer s.wg.Done() 334 } 335 } 336 if oldHook != nil { 337 oldHook(c, cs) 338 } 339 } 340 } 341 342 // closeConn closes c. 343 // s.mu must be held. 344 func (s *Server) closeConn(c net.Conn) { s.closeConnChan(c, nil) } 345 346 // closeConnChan is like closeConn, but takes an optional channel to receive a value 347 // when the goroutine closing c is done. 348 func (s *Server) closeConnChan(c net.Conn, done chan<- struct{}) { 349 c.Close() 350 if done != nil { 351 done <- struct{}{} 352 } 353 }