Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split req and client #230

Merged
merged 6 commits into from
Feb 4, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
vendor
25 changes: 11 additions & 14 deletions checksum.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package getter
import (
"bufio"
"bytes"
"context"
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
Expand Down Expand Up @@ -93,7 +94,7 @@ func (c *FileChecksum) checksum(source string) error {
// <checksum> *file2
//
// see parseChecksumLine for more detail on checksum file parsing
func (c *Client) extractChecksum(u *url.URL) (*FileChecksum, error) {
func (c *Client) extractChecksum(ctx context.Context, u *url.URL) (*FileChecksum, error) {
azr marked this conversation as resolved.
Show resolved Hide resolved
q := u.Query()
v := q.Get("checksum")

Expand All @@ -115,7 +116,7 @@ func (c *Client) extractChecksum(u *url.URL) (*FileChecksum, error) {

switch checksumType {
case "file":
return c.ChecksumFromFile(checksumValue, u)
return c.ChecksumFromFile(ctx, checksumValue, u)
default:
return newChecksumFromType(checksumType, checksumValue, filepath.Base(u.EscapedPath()))
}
Expand Down Expand Up @@ -190,7 +191,7 @@ func newChecksumFromValue(checksumValue, filename string) (*FileChecksum, error)
//
// ChecksumFromFile will only return checksums for files that match file
// behind src
func (c *Client) ChecksumFromFile(checksumFile string, src *url.URL) (*FileChecksum, error) {
func (c *Client) ChecksumFromFile(ctx context.Context, checksumFile string, src *url.URL) (*FileChecksum, error) {
azr marked this conversation as resolved.
Show resolved Hide resolved
checksumFileURL, err := urlhelper.Parse(checksumFile)
if err != nil {
return nil, err
Expand All @@ -202,18 +203,14 @@ func (c *Client) ChecksumFromFile(checksumFile string, src *url.URL) (*FileCheck
}
defer os.Remove(tempfile)

c2 := &Client{
Ctx: c.Ctx,
Getters: c.Getters,
Decompressors: c.Decompressors,
Detectors: c.Detectors,
Pwd: c.Pwd,
Dir: false,
Src: checksumFile,
Dst: tempfile,
ProgressListener: c.ProgressListener,
req := &Request{
// Pwd: c.Pwd, TODO(adrien): pass pwd ?
Dir: false,
Src: checksumFile,
Dst: tempfile,
// ProgressListener: c.ProgressListener, TODO(adrien): pass progress bar ?
}
if err = c2.Get(); err != nil {
if err = c.Get(ctx, req); err != nil {
return nil, fmt.Errorf(
"Error downloading checksum file: %s", err)
}
Expand Down
121 changes: 44 additions & 77 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,6 @@ import (
// Using a client directly allows more fine-grained control over how downloading
// is done, as well as customizing the protocols supported.
type Client struct {
// Ctx for cancellation
Ctx context.Context

// Src is the source URL to get.
//
// Dst is the path to save the downloaded thing as. If Dir is set to
// true, then this should be a directory. If the directory doesn't exist,
// it will be created for you.
//
// Pwd is the working directory for detection. If this isn't set, some
// detection may fail. Client will not default pwd to the current
// working directory for security reasons.
Src string
Dst string
Pwd string

// Mode is the method of download the client will use. See ClientMode
// for documentation.
Mode ClientMode

// Detectors is the list of detectors that are tried on the source.
// If this is nil, then the default Detectors will be used.
Expand All @@ -50,68 +31,54 @@ type Client struct {
// Getters is the map of protocols supported by this client. If this
// is nil, then the default Getters variable will be used.
Getters map[string]Getter

// Dir, if true, tells the Client it is downloading a directory (versus
// a single file). This distinction is necessary since filenames and
// directory names follow the same format so disambiguating is impossible
// without knowing ahead of time.
//
// WARNING: deprecated. If Mode is set, that will take precedence.
Dir bool

// ProgressListener allows to track file downloads.
// By default a no op progress listener is used.
ProgressListener ProgressTracker

Options []ClientOption
}

// Get downloads the configured source to the destination.
func (c *Client) Get() error {
if err := c.Configure(c.Options...); err != nil {
func (c *Client) Get(ctx context.Context, req *Request) error {
if err := c.Configure(); err != nil {
return err
}

// Store this locally since there are cases we swap this
mode := c.Mode
if mode == ClientModeInvalid {
if c.Dir {
mode = ClientModeDir
if req.Mode == ClientModeInvalid {
if req.Dir {
req.Mode = ClientModeDir
} else {
mode = ClientModeFile
req.Mode = ClientModeFile
}
}

src, err := Detect(c.Src, c.Pwd, c.Detectors)
var err error
req.Src, err = Detect(req.Src, req.Pwd, c.Detectors)
if err != nil {
return err
}

var force string
// Determine if we have a forced protocol, i.e. "git::http://..."
force, src := getForcedGetter(src)
force, req.Src = getForcedGetter(req.Src)

// If there is a subdir component, then we download the root separately
// and then copy over the proper subdir.
var realDst string
dst := c.Dst
src, subDir := SourceDirSubdir(src)
var realDst, subDir string
req.Src, subDir = SourceDirSubdir(req.Src)
if subDir != "" {
td, tdcloser, err := safetemp.Dir("", "getter")
if err != nil {
return err
}
defer tdcloser.Close()

realDst = dst
dst = td
realDst = req.Dst
req.Dst = td
}

u, err := urlhelper.Parse(src)
req.u, err = urlhelper.Parse(req.Src)
if err != nil {
return err
}
if force == "" {
force = u.Scheme
force = req.u.Scheme
}

g, ok := c.Getters[force]
Expand All @@ -121,15 +88,15 @@ func (c *Client) Get() error {
}

// We have magic query parameters that we use to signal different features
q := u.Query()
q := req.u.Query()

// Determine if we have an archive type
archiveV := q.Get("archive")
if archiveV != "" {
// Delete the paramter since it is a magic parameter we don't
// want to pass on to the Getter
q.Del("archive")
u.RawQuery = q.Encode()
req.u.RawQuery = q.Encode()

// If we can parse the value as a bool and it is false, then
// set the archive to "-" which should never map to a decompressor
Expand All @@ -141,7 +108,7 @@ func (c *Client) Get() error {
// We don't appear to... but is it part of the filename?
matchingLen := 0
for k := range c.Decompressors {
if strings.HasSuffix(u.Path, "."+k) && len(k) > matchingLen {
if strings.HasSuffix(req.u.Path, "."+k) && len(k) > matchingLen {
archiveV = k
matchingLen = len(k)
}
Expand All @@ -166,65 +133,65 @@ func (c *Client) Get() error {

// Swap the download directory to be our temporary path and
// store the old values.
decompressDst = dst
decompressDir = mode != ClientModeFile
dst = filepath.Join(td, "archive")
mode = ClientModeFile
decompressDst = req.Dst
decompressDir = req.Mode != ClientModeFile
req.Dst = filepath.Join(td, "archive")
req.Mode = ClientModeFile
}

// Determine checksum if we have one
checksum, err := c.extractChecksum(u)
checksum, err := c.extractChecksum(ctx, req.u)
if err != nil {
return fmt.Errorf("invalid checksum: %s", err)
}

// Delete the query parameter if we have it.
q.Del("checksum")
u.RawQuery = q.Encode()
req.u.RawQuery = q.Encode()

if mode == ClientModeAny {
if req.Mode == ClientModeAny {
// Ask the getter which client mode to use
mode, err = g.ClientMode(u)
req.Mode, err = g.ClientMode(ctx, req.u)
if err != nil {
return err
}

// Destination is the base name of the URL path in "any" mode when
// a file source is detected.
if mode == ClientModeFile {
filename := filepath.Base(u.Path)
if req.Mode == ClientModeFile {
filename := filepath.Base(req.u.Path)

// Determine if we have a custom file name
if v := q.Get("filename"); v != "" {
// Delete the query parameter if we have it.
q.Del("filename")
u.RawQuery = q.Encode()
req.u.RawQuery = q.Encode()

filename = v
}

dst = filepath.Join(dst, filename)
req.Dst = filepath.Join(req.Dst, filename)
}
}

// If we're not downloading a directory, then just download the file
// and return.
if mode == ClientModeFile {
if req.Mode == ClientModeFile {
getFile := true
if checksum != nil {
if err := checksum.checksum(dst); err == nil {
if err := checksum.checksum(req.Dst); err == nil {
// don't get the file if the checksum of dst is correct
getFile = false
}
}
if getFile {
err := g.GetFile(dst, u)
err := g.GetFile(ctx, req)
if err != nil {
return err
}

if checksum != nil {
if err := checksum.checksum(dst); err != nil {
if err := checksum.checksum(req.Dst); err != nil {
return err
}
}
Expand All @@ -233,24 +200,24 @@ func (c *Client) Get() error {
if decompressor != nil {
// We have a decompressor, so decompress the current destination
// into the final destination with the proper mode.
err := decompressor.Decompress(decompressDst, dst, decompressDir)
err := decompressor.Decompress(decompressDst, req.Dst, decompressDir)
if err != nil {
return err
}

// Swap the information back
dst = decompressDst
req.Dst = decompressDst
if decompressDir {
mode = ClientModeAny
req.Mode = ClientModeAny
} else {
mode = ClientModeFile
req.Mode = ClientModeFile
}
}

// We check the dir value again because it can be switched back
// if we were unarchiving. If we're still only Get-ing a file, then
// we're done.
if mode == ClientModeFile {
if req.Mode == ClientModeFile {
return nil
}
}
Expand All @@ -269,9 +236,9 @@ func (c *Client) Get() error {

// We're downloading a directory, which might require a bit more work
// if we're specifying a subdir.
err := g.Get(dst, u)
err := g.Get(ctx, req)
if err != nil {
err = fmt.Errorf("error downloading '%s': %s", src, err)
err = fmt.Errorf("error downloading '%s': %s", req.Src, err)
return err
}
}
Expand All @@ -286,12 +253,12 @@ func (c *Client) Get() error {
}

// Process any globs
subDir, err := SubdirGlob(dst, subDir)
subDir, err := SubdirGlob(req.Dst, subDir)
if err != nil {
return err
}

return copyDir(c.Ctx, realDst, subDir, false)
return copyDir(ctx, realDst, subDir, false)
}

return nil
Expand Down
23 changes: 1 addition & 22 deletions client_option.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,10 @@
package getter

import "context"

// A ClientOption allows to configure a client
type ClientOption func(*Client) error
azr marked this conversation as resolved.
Show resolved Hide resolved

// Configure configures a client with options.
func (c *Client) Configure(opts ...ClientOption) error {
if c.Ctx == nil {
c.Ctx = context.Background()
}
c.Options = opts
for _, opt := range opts {
err := opt(c)
if err != nil {
return err
}
}
func (c *Client) Configure() error {
azr marked this conversation as resolved.
Show resolved Hide resolved
// Default decompressor values
if c.Decompressors == nil {
c.Decompressors = Decompressors
Expand All @@ -35,12 +23,3 @@ func (c *Client) Configure(opts ...ClientOption) error {
}
return nil
}

// WithContext allows to pass a context to operation
// in order to be able to cancel a download in progress.
func WithContext(ctx context.Context) func(*Client) error {
return func(c *Client) error {
c.Ctx = ctx
return nil
}
}
12 changes: 0 additions & 12 deletions client_option_progress.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,6 @@ import (
"io"
)

// WithProgress allows for a user to track
// the progress of a download.
// For example by displaying a progress bar with
// current download.
// Not all getters have progress support yet.
func WithProgress(pl ProgressTracker) func(*Client) error {
return func(c *Client) error {
c.ProgressListener = pl
return nil
}
}

// ProgressTracker allows to track the progress of downloads.
type ProgressTracker interface {
// TrackProgress should be called when
Expand Down
Loading