github.com/vmware/govmomi@v0.37.2/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  }