package provider import ( "context" "io/ioutil" "net/http" "time" ) // Vehicle Type const ( File VehicleType = iota HTTP Compatible ) // VehicleType defined type VehicleType int func (v VehicleType) String() string { switch v { case File: return "File" case HTTP: return "HTTP" case Compatible: return "Compatible" default: return "Unknown" } } type Vehicle interface { Read() ([]byte, error) Path() string Type() VehicleType } type FileVehicle struct { path string } func (f *FileVehicle) Type() VehicleType { return File } func (f *FileVehicle) Path() string { return f.path } func (f *FileVehicle) Read() ([]byte, error) { return ioutil.ReadFile(f.path) } func NewFileVehicle(path string) *FileVehicle { return &FileVehicle{path: path} } type HTTPVehicle struct { url string path string } func (h *HTTPVehicle) Type() VehicleType { return HTTP } func (h *HTTPVehicle) Path() string { return h.path } func (h *HTTPVehicle) Read() ([]byte, error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) defer cancel() req, err := http.NewRequest(http.MethodGet, h.url, nil) if err != nil { return nil, err } req = req.WithContext(ctx) transport := &http.Transport{ // from http.DefaultTransport MaxIdleConns: 100, IdleConnTimeout: 90 * time.Second, TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, } client := http.Client{Transport: transport} resp, err := client.Do(req) if err != nil { return nil, err } buf, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } if err := ioutil.WriteFile(h.path, buf, fileMode); err != nil { return nil, err } return buf, nil } func NewHTTPVehicle(url string, path string) *HTTPVehicle { return &HTTPVehicle{url, path} }