blob: f4333068849264ac3a54dc051b62e2128ce51334 [file] [log] [blame]
// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package wasm
import (
"fmt"
"io"
"net/http"
"time"
)
import (
"github.com/cenkalti/backoff/v4"
)
// HTTPFetcher fetches remote wasm module with HTTP get.
type HTTPFetcher struct {
defaultClient *http.Client
initialBackoff time.Duration
}
// NewHTTPFetcher create a new HTTP remote wasm module fetcher.
func NewHTTPFetcher() *HTTPFetcher {
return &HTTPFetcher{
defaultClient: &http.Client{
Timeout: 5 * time.Second,
},
initialBackoff: time.Millisecond * 500,
}
}
// Fetch downloads a wasm module with HTTP get.
func (f *HTTPFetcher) Fetch(url string, timeout time.Duration) ([]byte, error) {
c := f.defaultClient
if timeout != 0 {
c = &http.Client{
Timeout: timeout,
}
}
attempts := 0
b := backoff.NewExponentialBackOff()
b.InitialInterval = f.initialBackoff
b.Reset()
var lastError error
for attempts < 5 {
attempts++
resp, err := c.Get(url)
if err != nil {
lastError = err
wasmLog.Debugf("wasm module download request failed: %v", err)
time.Sleep(b.NextBackOff())
continue
}
if resp.StatusCode == http.StatusOK {
body, err := io.ReadAll(resp.Body)
resp.Body.Close()
return body, err
}
lastError = fmt.Errorf("wasm module download request failed: status code %v", resp.StatusCode)
if retryable(resp.StatusCode) {
body, _ := io.ReadAll(resp.Body)
wasmLog.Debugf("wasm module download failed: status code %v, body %v", resp.StatusCode, string(body))
resp.Body.Close()
time.Sleep(b.NextBackOff())
continue
}
resp.Body.Close()
break
}
return nil, fmt.Errorf("wasm module download failed, last error: %v", lastError)
}
func retryable(code int) bool {
return code >= 500 &&
!(code == http.StatusNotImplemented ||
code == http.StatusHTTPVersionNotSupported ||
code == http.StatusNetworkAuthenticationRequired)
}