golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/internal/quic/cmd/interop/main_test.go (about) 1 // Copyright 2023 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 //go:build go1.21 6 7 package main 8 9 import ( 10 "bufio" 11 "bytes" 12 "context" 13 "net" 14 "os" 15 "os/exec" 16 "path/filepath" 17 "strings" 18 "sync" 19 "testing" 20 ) 21 22 func init() { 23 // We reexec the test binary with CMD_INTEROP_MAIN=1 to run main. 24 if os.Getenv("CMD_INTEROP_MAIN") == "1" { 25 main() 26 os.Exit(0) 27 } 28 } 29 30 var ( 31 tryExecOnce sync.Once 32 tryExecErr error 33 ) 34 35 // needsExec skips the test if we can't use exec.Command. 36 func needsExec(t *testing.T) { 37 tryExecOnce.Do(func() { 38 cmd := exec.Command(os.Args[0], "-test.list=^$") 39 cmd.Env = []string{} 40 tryExecErr = cmd.Run() 41 }) 42 if tryExecErr != nil { 43 t.Skipf("skipping test: cannot exec subprocess: %v", tryExecErr) 44 } 45 } 46 47 type interopTest struct { 48 donec chan struct{} 49 addr string 50 cmd *exec.Cmd 51 } 52 53 func run(ctx context.Context, t *testing.T, name, testcase string, args []string) *interopTest { 54 needsExec(t) 55 ctx, cancel := context.WithCancel(ctx) 56 cmd := exec.CommandContext(ctx, os.Args[0], args...) 57 out, err := cmd.StderrPipe() 58 if err != nil { 59 t.Fatal(err) 60 } 61 cmd.Stdout = cmd.Stderr 62 cmd.Env = []string{ 63 "CMD_INTEROP_MAIN=1", 64 "TESTCASE=" + testcase, 65 } 66 t.Logf("run %v: %v", name, args) 67 err = cmd.Start() 68 if err != nil { 69 t.Fatal(err) 70 } 71 72 addrc := make(chan string, 1) 73 donec := make(chan struct{}) 74 go func() { 75 defer close(addrc) 76 defer close(donec) 77 defer t.Logf("%v done", name) 78 s := bufio.NewScanner(out) 79 for s.Scan() { 80 line := s.Text() 81 t.Logf("%v: %v", name, line) 82 _, addr, ok := strings.Cut(line, "listening on ") 83 if ok { 84 select { 85 case addrc <- addr: 86 default: 87 } 88 } 89 } 90 }() 91 92 t.Cleanup(func() { 93 cancel() 94 <-donec 95 }) 96 97 addr, ok := <-addrc 98 if !ok { 99 t.Fatal(cmd.Wait()) 100 } 101 _, port, _ := net.SplitHostPort(addr) 102 addr = net.JoinHostPort("localhost", port) 103 104 iop := &interopTest{ 105 cmd: cmd, 106 donec: donec, 107 addr: addr, 108 } 109 return iop 110 } 111 112 func (iop *interopTest) wait() { 113 <-iop.donec 114 } 115 116 func TestTransfer(t *testing.T) { 117 ctx := context.Background() 118 src := t.TempDir() 119 dst := t.TempDir() 120 certs := t.TempDir() 121 certFile := filepath.Join(certs, "cert.pem") 122 keyFile := filepath.Join(certs, "key.pem") 123 sourceName := "source" 124 content := []byte("hello, world\n") 125 126 os.WriteFile(certFile, localhostCert, 0600) 127 os.WriteFile(keyFile, localhostKey, 0600) 128 os.WriteFile(filepath.Join(src, sourceName), content, 0600) 129 130 srv := run(ctx, t, "server", "transfer", []string{ 131 "-listen", "localhost:0", 132 "-cert", filepath.Join(certs, "cert.pem"), 133 "-key", filepath.Join(certs, "key.pem"), 134 "-root", src, 135 }) 136 cli := run(ctx, t, "client", "transfer", []string{ 137 "-output", dst, "https://" + srv.addr + "/" + sourceName, 138 }) 139 cli.wait() 140 141 got, err := os.ReadFile(filepath.Join(dst, "source")) 142 if err != nil { 143 t.Fatalf("reading downloaded file: %v", err) 144 } 145 if !bytes.Equal(got, content) { 146 t.Fatalf("got downloaded file: %q, want %q", string(got), string(content)) 147 } 148 } 149 150 // localhostCert is a PEM-encoded TLS cert with SAN IPs 151 // "127.0.0.1" and "[::1]", expiring at Jan 29 16:00:00 2084 GMT. 152 // generated from src/crypto/tls: 153 // go run generate_cert.go --ecdsa-curve P256 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h 154 var localhostCert = []byte(`-----BEGIN CERTIFICATE----- 155 MIIBrDCCAVKgAwIBAgIPCvPhO+Hfv+NW76kWxULUMAoGCCqGSM49BAMCMBIxEDAO 156 BgNVBAoTB0FjbWUgQ28wIBcNNzAwMTAxMDAwMDAwWhgPMjA4NDAxMjkxNjAwMDBa 157 MBIxEDAOBgNVBAoTB0FjbWUgQ28wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARh 158 WRF8p8X9scgW7JjqAwI9nYV8jtkdhqAXG9gyEgnaFNN5Ze9l3Tp1R9yCDBMNsGms 159 PyfMPe5Jrha/LmjgR1G9o4GIMIGFMA4GA1UdDwEB/wQEAwIChDATBgNVHSUEDDAK 160 BggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSOJri/wLQxq6oC 161 Y6ZImms/STbTljAuBgNVHREEJzAlggtleGFtcGxlLmNvbYcEfwAAAYcQAAAAAAAA 162 AAAAAAAAAAAAATAKBggqhkjOPQQDAgNIADBFAiBUguxsW6TGhixBAdORmVNnkx40 163 HjkKwncMSDbUaeL9jQIhAJwQ8zV9JpQvYpsiDuMmqCuW35XXil3cQ6Drz82c+fvE 164 -----END CERTIFICATE-----`) 165 166 // localhostKey is the private key for localhostCert. 167 var localhostKey = []byte(testingKey(`-----BEGIN TESTING KEY----- 168 MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgY1B1eL/Bbwf/MDcs 169 rnvvWhFNr1aGmJJR59PdCN9lVVqhRANCAARhWRF8p8X9scgW7JjqAwI9nYV8jtkd 170 hqAXG9gyEgnaFNN5Ze9l3Tp1R9yCDBMNsGmsPyfMPe5Jrha/LmjgR1G9 171 -----END TESTING KEY-----`)) 172 173 // testingKey helps keep security scanners from getting excited about a private key in this file. 174 func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "PRIVATE KEY") }