1
2
3
4
5
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
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
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
151
152
153
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
167 var localhostKey = []byte(testingKey(`-----BEGIN TESTING KEY-----
168 MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgY1B1eL/Bbwf/MDcs
169 rnvvWhFNr1aGmJJR59PdCN9lVVqhRANCAARhWRF8p8X9scgW7JjqAwI9nYV8jtkd
170 hqAXG9gyEgnaFNN5Ze9l3Tp1R9yCDBMNsGmsPyfMPe5Jrha/LmjgR1G9
171 -----END TESTING KEY-----`))
172
173
174 func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "PRIVATE KEY") }
175
View as plain text