istio

Форк
0
/
httpfetcher_test.go 
268 строк · 7.5 Кб
1
// Copyright Istio Authors
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15
package wasm
16

17
import (
18
	"archive/tar"
19
	"bytes"
20
	"compress/gzip"
21
	"context"
22
	"fmt"
23
	"net/http"
24
	"net/http/httptest"
25
	"regexp"
26
	"strings"
27
	"testing"
28
	"time"
29

30
	"github.com/google/go-cmp/cmp"
31
)
32

33
func TestWasmHTTPFetch(t *testing.T) {
34
	var ts *httptest.Server
35

36
	cases := []struct {
37
		name           string
38
		handler        func(http.ResponseWriter, *http.Request, int)
39
		timeout        time.Duration
40
		wantNumRequest int
41
		wantErrorRegex string
42
	}{
43
		{
44
			name: "download ok",
45
			handler: func(w http.ResponseWriter, r *http.Request, num int) {
46
				fmt.Fprintln(w, "wasm")
47
			},
48
			timeout:        5 * time.Second,
49
			wantNumRequest: 1,
50
		},
51
		{
52
			name: "download retry",
53
			handler: func(w http.ResponseWriter, r *http.Request, num int) {
54
				if num <= 2 {
55
					w.WriteHeader(http.StatusInternalServerError)
56
				} else {
57
					fmt.Fprintln(w, "wasm")
58
				}
59
			},
60
			timeout:        5 * time.Second,
61
			wantNumRequest: 4,
62
		},
63
		{
64
			name: "download max retry",
65
			handler: func(w http.ResponseWriter, r *http.Request, num int) {
66
				w.WriteHeader(http.StatusInternalServerError)
67
			},
68
			timeout:        5 * time.Second,
69
			wantNumRequest: 5,
70
			wantErrorRegex: "wasm module download failed after 5 attempts, last error: wasm module download request failed: status code 500",
71
		},
72
		{
73
			name: "download is never tried by immediate context timeout",
74
			handler: func(w http.ResponseWriter, r *http.Request, num int) {
75
				w.WriteHeader(http.StatusInternalServerError)
76
			},
77
			timeout:        0, // Immediately timeout in the context level.
78
			wantNumRequest: 0, // Should not retried because it is already timed out.
79
			wantErrorRegex: "wasm module download failed after 1 attempts, last error: Get \"[^\"]+\": context deadline exceeded",
80
		},
81
	}
82

83
	for _, c := range cases {
84
		t.Run(c.name, func(t *testing.T) {
85
			gotNumRequest := 0
86
			wantWasmModule := "wasm\n"
87
			ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
88
				c.handler(w, r, gotNumRequest)
89
				gotNumRequest++
90
			}))
91
			defer ts.Close()
92
			fetcher := NewHTTPFetcher(DefaultHTTPRequestTimeout, DefaultHTTPRequestMaxRetries)
93
			fetcher.initialBackoff = time.Microsecond
94
			ctx, cancel := context.WithTimeout(context.Background(), c.timeout)
95
			defer cancel()
96
			b, err := fetcher.Fetch(ctx, ts.URL, false)
97
			if c.wantNumRequest != gotNumRequest {
98
				t.Errorf("Wasm download request got %v, want %v", gotNumRequest, c.wantNumRequest)
99
			}
100
			if c.wantErrorRegex != "" {
101
				if err == nil {
102
					t.Errorf("Wasm download got no error, want error regex `%v`", c.wantErrorRegex)
103
				} else if matched, regexErr := regexp.MatchString(c.wantErrorRegex, err.Error()); regexErr != nil || !matched {
104
					t.Errorf("Wasm download got error `%v`, want error regex `%v`", err, c.wantErrorRegex)
105
				}
106
			} else if string(b) != wantWasmModule {
107
				t.Errorf("downloaded wasm module got %v, want wasm", string(b))
108
			}
109
		})
110
	}
111
}
112

113
func TestWasmHTTPInsecureServer(t *testing.T) {
114
	var ts *httptest.Server
115

116
	cases := []struct {
117
		name            string
118
		handler         func(http.ResponseWriter, *http.Request, int)
119
		insecure        bool
120
		wantNumRequest  int
121
		wantErrorSuffix string
122
	}{
123
		{
124
			name: "download fail",
125
			handler: func(w http.ResponseWriter, r *http.Request, num int) {
126
				fmt.Fprintln(w, "wasm")
127
			},
128
			insecure:        false,
129
			wantErrorSuffix: "x509: certificate signed by unknown authority",
130
			wantNumRequest:  0,
131
		},
132
		{
133
			name: "download ok",
134
			handler: func(w http.ResponseWriter, r *http.Request, num int) {
135
				fmt.Fprintln(w, "wasm")
136
			},
137
			insecure:       true,
138
			wantNumRequest: 1,
139
		},
140
	}
141

142
	for _, c := range cases {
143
		t.Run(c.name, func(t *testing.T) {
144
			gotNumRequest := 0
145
			wantWasmModule := "wasm\n"
146
			ts = httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
147
				c.handler(w, r, gotNumRequest)
148
				gotNumRequest++
149
			}))
150
			defer ts.Close()
151
			fetcher := NewHTTPFetcher(DefaultHTTPRequestTimeout, DefaultHTTPRequestMaxRetries)
152
			fetcher.initialBackoff = time.Microsecond
153
			ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
154
			defer cancel()
155
			b, err := fetcher.Fetch(ctx, ts.URL, c.insecure)
156
			if c.wantNumRequest != gotNumRequest {
157
				t.Errorf("Wasm download request got %v, want %v", gotNumRequest, c.wantNumRequest)
158
			}
159
			if c.wantErrorSuffix != "" {
160
				if err == nil {
161
					t.Errorf("Wasm download got no error, want error suffix `%v`", c.wantErrorSuffix)
162
				} else if !strings.HasSuffix(err.Error(), c.wantErrorSuffix) {
163
					t.Errorf("Wasm download got error `%v`, want error suffix `%v`", err, c.wantErrorSuffix)
164
				}
165
			} else if string(b) != wantWasmModule {
166
				t.Errorf("downloaded wasm module got %v, want wasm", string(b))
167
			}
168
		})
169
	}
170
}
171

172
func createTar(t *testing.T, b []byte) []byte {
173
	t.Helper()
174
	var buf bytes.Buffer
175
	tw := tar.NewWriter(&buf)
176
	hdr := &tar.Header{
177
		Name: "plugin.wasm",
178
		Mode: 0o600,
179
		Size: int64(len(b)),
180
	}
181
	if err := tw.WriteHeader(hdr); err != nil {
182
		t.Fatal(err)
183
	}
184
	if _, err := tw.Write(b); err != nil {
185
		t.Fatal(err)
186
	}
187
	if err := tw.Close(); err != nil {
188
		t.Fatal(err)
189
	}
190
	return buf.Bytes()
191
}
192

193
func createGZ(t *testing.T, b []byte) []byte {
194
	t.Helper()
195
	var buf bytes.Buffer
196
	zw := gzip.NewWriter(&buf)
197
	if _, err := zw.Write(b); err != nil {
198
		t.Fatal(err)
199
	}
200

201
	if err := zw.Close(); err != nil {
202
		t.Fatal(err)
203
	}
204

205
	return buf.Bytes()
206
}
207

208
func TestWasmHTTPFetchCompressedOrTarFile(t *testing.T) {
209
	wasmBinary := append(wasmMagicNumber, 0x00, 0x00, 0x00, 0x00)
210
	tarball := createTar(t, wasmBinary)
211
	gz := createGZ(t, wasmBinary)
212
	gzTarball := createGZ(t, tarball)
213
	cases := []struct {
214
		name    string
215
		handler func(http.ResponseWriter, *http.Request, int)
216
	}{
217
		{
218
			name: "plain wasm binary",
219
			handler: func(w http.ResponseWriter, r *http.Request, num int) {
220
				w.Write(wasmBinary)
221
			},
222
		},
223
		{
224
			name: "tarball of wasm binary",
225
			handler: func(w http.ResponseWriter, r *http.Request, num int) {
226
				w.Write(tarball)
227
			},
228
		},
229
		{
230
			name: "gzipped wasm binary",
231
			handler: func(w http.ResponseWriter, r *http.Request, num int) {
232
				w.Write(gz)
233
			},
234
		},
235
		{
236
			name: "gzipped tarball of wasm binary",
237
			handler: func(w http.ResponseWriter, r *http.Request, num int) {
238
				w.Write(gzTarball)
239
			},
240
		},
241
	}
242

243
	for _, c := range cases {
244
		t.Run(c.name, func(t *testing.T) {
245
			gotNumRequest := 0
246
			ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
247
				c.handler(w, r, gotNumRequest)
248
				gotNumRequest++
249
			}))
250
			defer ts.Close()
251
			fetcher := NewHTTPFetcher(DefaultHTTPRequestTimeout, DefaultHTTPRequestMaxRetries)
252
			fetcher.initialBackoff = time.Microsecond
253
			ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
254
			defer cancel()
255
			b, err := fetcher.Fetch(ctx, ts.URL, false)
256
			if err != nil {
257
				t.Errorf("Wasm download got an unexpected error: %v", err)
258
			}
259

260
			if diff := cmp.Diff(wasmBinary, b); diff != "" {
261
				if len(diff) > 500 {
262
					diff = diff[:500]
263
				}
264
				t.Errorf("unexpected binary: (-want, +got)\n%v", diff)
265
			}
266
		})
267
	}
268
}
269

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.