package cli import ( "context" "errors" "fmt" "io" "net/http" "strings" "time" "github.com/urfave/cli/v2" ) // manifestFetchTimeout bounds HTTP requests made to fetch a manifest. const manifestFetchTimeout = 30 * time.Second // errHTTPStatus indicates an HTTP response with a non-OK status code. // // Its text is the literal "HTTP" prefix of the rendered "HTTP " // message that mfer has always printed, so that wrapping it does not // change any user-visible output. Match it with errors.Is; do not read // its message. var errHTTPStatus = errors.New("HTTP") // isHTTPURL returns true if the string starts with http:// or https://. func isHTTPURL(s string) bool { return strings.HasPrefix(s, "http://") || strings.HasPrefix(s, "https://") } // openManifestReader opens a manifest from a path or URL and returns a ReadCloser. // The caller must close the returned reader. func (mfa *CLIApp) openManifestReader(pathOrURL string) (io.ReadCloser, error) { if isHTTPURL(pathOrURL) { client := &http.Client{Timeout: manifestFetchTimeout} req, err := http.NewRequestWithContext( context.Background(), http.MethodGet, pathOrURL, nil, ) if err != nil { return nil, fmt.Errorf("failed to fetch %s: %w", pathOrURL, err) } resp, err := client.Do(req) if err != nil { return nil, fmt.Errorf("failed to fetch %s: %w", pathOrURL, err) } if resp.StatusCode != http.StatusOK { _ = resp.Body.Close() return nil, fmt.Errorf("failed to fetch %s: %w %d", pathOrURL, errHTTPStatus, resp.StatusCode) } return resp.Body, nil } f, err := mfa.Fs.Open(pathOrURL) if err != nil { return nil, err } return f, nil } // resolveManifestArg resolves the manifest path from CLI arguments. // HTTP(S) URLs are returned as-is. Directories are searched for index.mf/.index.mf. // If no argument is given, the current directory is searched. func (mfa *CLIApp) resolveManifestArg(ctx *cli.Context) (string, error) { if ctx.Args().Len() > 0 { arg := ctx.Args().Get(0) if isHTTPURL(arg) { return arg, nil } info, statErr := mfa.Fs.Stat(arg) if statErr == nil && info.IsDir() { return findManifest(mfa.Fs, arg) } return arg, nil } return findManifest(mfa.Fs, ".") }