github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/go/vcweb/vcstest/vcstest.go (about)

     1  // Copyright 2022 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package vcstest serves the repository scripts in cmd/go/testdata/vcstest
     6  // using the [vcweb] script engine.
     7  package vcstest
     8  
     9  import (
    10  	"crypto/tls"
    11  	"crypto/x509"
    12  	"encoding/pem"
    13  	"fmt"
    14  	"io"
    15  	"log"
    16  	"net/http"
    17  	"net/http/httptest"
    18  	"net/url"
    19  	"os"
    20  	"path/filepath"
    21  	"testing"
    22  
    23  	"github.com/go-asm/go/cmd/go/vcs"
    24  	"github.com/go-asm/go/cmd/go/vcweb"
    25  	"github.com/go-asm/go/cmd/go/web"
    26  	"github.com/go-asm/go/testenv"
    27  )
    28  
    29  var Hosts = []string{
    30  	"vcs-test.golang.org",
    31  }
    32  
    33  type Server struct {
    34  	vcweb   *vcweb.Server
    35  	workDir string
    36  	HTTP    *httptest.Server
    37  	HTTPS   *httptest.Server
    38  }
    39  
    40  // NewServer returns a new test-local vcweb server that serves VCS requests
    41  // for modules with paths that begin with "vcs-test.golang.org" using the
    42  // scripts in cmd/go/testdata/vcstest.
    43  func NewServer() (srv *Server, err error) {
    44  	if vcs.VCSTestRepoURL != "" {
    45  		panic("vcs URL hooks already set")
    46  	}
    47  
    48  	scriptDir := filepath.Join(testenv.GOROOT(nil), "src/cmd/go/testdata/vcstest")
    49  
    50  	workDir, err := os.MkdirTemp("", "vcstest")
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	defer func() {
    55  		if err != nil {
    56  			os.RemoveAll(workDir)
    57  		}
    58  	}()
    59  
    60  	logger := log.Default()
    61  	if !testing.Verbose() {
    62  		logger = log.New(io.Discard, "", log.LstdFlags)
    63  	}
    64  	handler, err := vcweb.NewServer(scriptDir, workDir, logger)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  	defer func() {
    69  		if err != nil {
    70  			handler.Close()
    71  		}
    72  	}()
    73  
    74  	srvHTTP := httptest.NewServer(handler)
    75  	httpURL, err := url.Parse(srvHTTP.URL)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	defer func() {
    80  		if err != nil {
    81  			srvHTTP.Close()
    82  		}
    83  	}()
    84  
    85  	srvHTTPS := httptest.NewTLSServer(handler)
    86  	httpsURL, err := url.Parse(srvHTTPS.URL)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  	defer func() {
    91  		if err != nil {
    92  			srvHTTPS.Close()
    93  		}
    94  	}()
    95  
    96  	srv = &Server{
    97  		vcweb:   handler,
    98  		workDir: workDir,
    99  		HTTP:    srvHTTP,
   100  		HTTPS:   srvHTTPS,
   101  	}
   102  	vcs.VCSTestRepoURL = srv.HTTP.URL
   103  	vcs.VCSTestHosts = Hosts
   104  
   105  	var interceptors []web.Interceptor
   106  	for _, host := range Hosts {
   107  		interceptors = append(interceptors,
   108  			web.Interceptor{Scheme: "http", FromHost: host, ToHost: httpURL.Host, Client: srv.HTTP.Client()},
   109  			web.Interceptor{Scheme: "https", FromHost: host, ToHost: httpsURL.Host, Client: srv.HTTPS.Client()})
   110  	}
   111  	web.EnableTestHooks(interceptors)
   112  
   113  	fmt.Fprintln(os.Stderr, "vcs-test.golang.org rerouted to "+srv.HTTP.URL)
   114  	fmt.Fprintln(os.Stderr, "https://vcs-test.golang.org rerouted to "+srv.HTTPS.URL)
   115  
   116  	return srv, nil
   117  }
   118  
   119  func (srv *Server) Close() error {
   120  	if vcs.VCSTestRepoURL != srv.HTTP.URL {
   121  		panic("vcs URL hooks modified before Close")
   122  	}
   123  	vcs.VCSTestRepoURL = ""
   124  	vcs.VCSTestHosts = nil
   125  	web.DisableTestHooks()
   126  
   127  	srv.HTTP.Close()
   128  	srv.HTTPS.Close()
   129  	err := srv.vcweb.Close()
   130  	if rmErr := os.RemoveAll(srv.workDir); err == nil {
   131  		err = rmErr
   132  	}
   133  	return err
   134  }
   135  
   136  func (srv *Server) WriteCertificateFile() (string, error) {
   137  	b := pem.EncodeToMemory(&pem.Block{
   138  		Type:  "CERTIFICATE",
   139  		Bytes: srv.HTTPS.Certificate().Raw,
   140  	})
   141  
   142  	filename := filepath.Join(srv.workDir, "cert.pem")
   143  	if err := os.WriteFile(filename, b, 0644); err != nil {
   144  		return "", err
   145  	}
   146  	return filename, nil
   147  }
   148  
   149  // TLSClient returns an http.Client that can talk to the httptest.Server
   150  // whose certificate is written to the given file path.
   151  func TLSClient(certFile string) (*http.Client, error) {
   152  	client := &http.Client{
   153  		Transport: http.DefaultTransport.(*http.Transport).Clone(),
   154  	}
   155  
   156  	pemBytes, err := os.ReadFile(certFile)
   157  	if err != nil {
   158  		return nil, err
   159  	}
   160  
   161  	certpool := x509.NewCertPool()
   162  	if !certpool.AppendCertsFromPEM(pemBytes) {
   163  		return nil, fmt.Errorf("no certificates found in %s", certFile)
   164  	}
   165  	client.Transport.(*http.Transport).TLSClientConfig = &tls.Config{
   166  		RootCAs: certpool,
   167  	}
   168  
   169  	return client, nil
   170  }