package cli import ( "context" "errors" "fmt" "io" "net/http" "strings" "time" "github.com/urfave/cli/v3" ) // 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. It is // followed by the code, as in "unexpected HTTP status 404". var errHTTPStatus = errors.New("unexpected HTTP status") // errManifestTooLarge indicates a manifest download that passed // CLIApp.maxManifestSize. var errManifestTooLarge = errors.New("file exceeds maximum allowed size") // 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( ctx context.Context, pathOrURL string, ) (io.ReadCloser, error) { if isHTTPURL(pathOrURL) { client := &http.Client{Timeout: manifestFetchTimeout} // The *url.Error that NewRequestWithContext and Do return names // the URL. req, err := http.NewRequestWithContext(ctx, http.MethodGet, pathOrURL, nil) if err != nil { return nil, fmt.Errorf("download manifest: %w", err) } resp, err := client.Do(req) if err != nil { return nil, fmt.Errorf("download manifest: %w", err) } if resp.StatusCode != http.StatusOK { _ = resp.Body.Close() return nil, fmt.Errorf("download manifest %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. // If no argument is given, the current directory is searched. func (mfa *CLIApp) resolveManifestArg(cmd *cli.Command) (string, error) { if cmd.Args().Len() > 0 { arg := cmd.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, ".") }