1
// Copyright Istio Authors
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
7
// http://www.apache.org/licenses/LICENSE-2.0
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.
30
"github.com/google/go-cmp/cmp"
33
func TestWasmHTTPFetch(t *testing.T) {
34
var ts *httptest.Server
38
handler func(http.ResponseWriter, *http.Request, int)
45
handler: func(w http.ResponseWriter, r *http.Request, num int) {
46
fmt.Fprintln(w, "wasm")
48
timeout: 5 * time.Second,
52
name: "download retry",
53
handler: func(w http.ResponseWriter, r *http.Request, num int) {
55
w.WriteHeader(http.StatusInternalServerError)
57
fmt.Fprintln(w, "wasm")
60
timeout: 5 * time.Second,
64
name: "download max retry",
65
handler: func(w http.ResponseWriter, r *http.Request, num int) {
66
w.WriteHeader(http.StatusInternalServerError)
68
timeout: 5 * time.Second,
70
wantErrorRegex: "wasm module download failed after 5 attempts, last error: wasm module download request failed: status code 500",
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)
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",
83
for _, c := range cases {
84
t.Run(c.name, func(t *testing.T) {
86
wantWasmModule := "wasm\n"
87
ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
88
c.handler(w, r, gotNumRequest)
92
fetcher := NewHTTPFetcher(DefaultHTTPRequestTimeout, DefaultHTTPRequestMaxRetries)
93
fetcher.initialBackoff = time.Microsecond
94
ctx, cancel := context.WithTimeout(context.Background(), c.timeout)
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)
100
if c.wantErrorRegex != "" {
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)
106
} else if string(b) != wantWasmModule {
107
t.Errorf("downloaded wasm module got %v, want wasm", string(b))
113
func TestWasmHTTPInsecureServer(t *testing.T) {
114
var ts *httptest.Server
118
handler func(http.ResponseWriter, *http.Request, int)
121
wantErrorSuffix string
124
name: "download fail",
125
handler: func(w http.ResponseWriter, r *http.Request, num int) {
126
fmt.Fprintln(w, "wasm")
129
wantErrorSuffix: "x509: certificate signed by unknown authority",
134
handler: func(w http.ResponseWriter, r *http.Request, num int) {
135
fmt.Fprintln(w, "wasm")
142
for _, c := range cases {
143
t.Run(c.name, func(t *testing.T) {
145
wantWasmModule := "wasm\n"
146
ts = httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
147
c.handler(w, r, gotNumRequest)
151
fetcher := NewHTTPFetcher(DefaultHTTPRequestTimeout, DefaultHTTPRequestMaxRetries)
152
fetcher.initialBackoff = time.Microsecond
153
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
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)
159
if c.wantErrorSuffix != "" {
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)
165
} else if string(b) != wantWasmModule {
166
t.Errorf("downloaded wasm module got %v, want wasm", string(b))
172
func createTar(t *testing.T, b []byte) []byte {
175
tw := tar.NewWriter(&buf)
181
if err := tw.WriteHeader(hdr); err != nil {
184
if _, err := tw.Write(b); err != nil {
187
if err := tw.Close(); err != nil {
193
func createGZ(t *testing.T, b []byte) []byte {
196
zw := gzip.NewWriter(&buf)
197
if _, err := zw.Write(b); err != nil {
201
if err := zw.Close(); err != nil {
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)
215
handler func(http.ResponseWriter, *http.Request, int)
218
name: "plain wasm binary",
219
handler: func(w http.ResponseWriter, r *http.Request, num int) {
224
name: "tarball of wasm binary",
225
handler: func(w http.ResponseWriter, r *http.Request, num int) {
230
name: "gzipped wasm binary",
231
handler: func(w http.ResponseWriter, r *http.Request, num int) {
236
name: "gzipped tarball of wasm binary",
237
handler: func(w http.ResponseWriter, r *http.Request, num int) {
243
for _, c := range cases {
244
t.Run(c.name, func(t *testing.T) {
246
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
247
c.handler(w, r, gotNumRequest)
251
fetcher := NewHTTPFetcher(DefaultHTTPRequestTimeout, DefaultHTTPRequestMaxRetries)
252
fetcher.initialBackoff = time.Microsecond
253
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
255
b, err := fetcher.Fetch(ctx, ts.URL, false)
257
t.Errorf("Wasm download got an unexpected error: %v", err)
260
if diff := cmp.Diff(wasmBinary, b); diff != "" {
264
t.Errorf("unexpected binary: (-want, +got)\n%v", diff)