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

Check the Last-Modified header of the remote file #19

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
7 changes: 7 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,13 @@ func (c *Client) doHTTPRequest(hreq *http.Request, resp *Response) (bool, error)
hreq.Header.Set("User-Agent", c.UserAgent)
}

// TODO: set If-Modified-Since header
// https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25
// if resp.fi != nil {
// since := resp.fi.ModTime().Format(http.TimeFormat)
// hreq.Header.Set("If-Modified-Since", since)
// }

hresp, err := c.HTTPClient.Do(hreq)
if err != nil {
return false, err
Expand Down
41 changes: 41 additions & 0 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,3 +427,44 @@ func TestRemoteTime(t *testing.T) {
t.Errorf("expected %v, got %v", time.Unix(lastmod, 0), fi.ModTime())
}
}

// TestTimeModified
func TestLastModified(t *testing.T) {
filename := "./.testLastModified"
defer os.Remove(filename)

oldURL := fmt.Sprintf("%s?lastmod=%d", ts.URL, time.Now().Add(-24*time.Hour).Unix())
newURL := fmt.Sprintf("%s?lastmod=%d", ts.URL, time.Now().Unix())

// download initial file
if _, err := Get(filename, oldURL); err != nil {
panic(err)
}

// skip unmodifed remote file
resp, err := Get(filename, oldURL)
if err != nil {
panic(err)
}
if !resp.DidResume || resp.bytesResumed < 1 {
t.Errorf("expected client to skip unmodified file")
}

// download modified remote file
resp, err = Get(filename, newURL)
if err != nil {
panic(err)
}
if resp.DidResume || resp.bytesResumed != 0 {
t.Errorf("expected client to download modified file")
}

// skip unmodifed remote file
resp, err = Get(filename, oldURL)
if err != nil {
panic(err)
}
if !resp.DidResume || resp.bytesResumed < 1 {
t.Errorf("expected client to skip unmodified file")
}
}
2 changes: 1 addition & 1 deletion grab_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ var ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http
panic(err)
}
lastmodt := time.Unix(lastmodi, 0).UTC()
lastmod := lastmodt.Format("Mon, 02 Jan 2006 15:04:05") + " GMT"
lastmod := lastmodt.Format(http.TimeFormat)
w.Header().Set("Last-Modified", lastmod)
}

Expand Down
7 changes: 4 additions & 3 deletions request.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ type Request struct {
// exist.
NoCreateDirectories bool

// IgnoreRemoteTime specifies that grab should not attempt to set the
// timestamp of the local file to match the remote file.
IgnoreRemoteTime bool
// NoSetLocalModTime specifies that grab should not attempt to set the
// timestamp of the local file to match the remote file. The operating system
// will instead set the timestamp to the current system time.
NoSetLocalModTime bool

// Size specifies the expected size of the file transfer if known. If the
// server response size does not match, the transfer is cancelled and
Expand Down
14 changes: 11 additions & 3 deletions response.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,6 @@ func (c *Response) readResponse(resp *http.Response) error {
//
// If a checksum has been requested, it will be executed on the existing file
// and an error returned if it fails validation.
//
// TODO: check timestamps and/or E-Tags
func (c *Response) checkExisting() (bool, error) {
if c.fi == nil {
return false, nil
Expand All @@ -265,6 +263,16 @@ func (c *Response) checkExisting() (bool, error) {
return true, ErrFileExists
}

// compare last-modified timestamp
// BUG: resumed downloads always compute the local partial to be most recent
if c.HTTPResponse != nil {
// discard error as zero-time is never greater than fi.ModTime
lastmod, _ := http.ParseTime(c.HTTPResponse.Header.Get("Last-Modified"))
if lastmod.Unix() > c.fi.ModTime().Unix() {
return false, nil
}
}

// determine expected file size
size := c.Request.Size
if size == 0 && c.HTTPResponse != nil {
Expand Down Expand Up @@ -441,7 +449,7 @@ func (c *Response) copy() error {
}

// set timestamp
if !c.Request.IgnoreRemoteTime {
if !c.Request.NoSetLocalModTime {
if err := setLocalTimestamp(c.HTTPResponse, c.Filename); err != nil {
return c.close(err)
}
Expand Down
2 changes: 1 addition & 1 deletion util.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func isCanceled(ctx context.Context) error {
}
}

// copyBuffer behaves similarly to io.CopyBuffer except that is checks for
// copyBuffer behaves similarly to io.CopyBuffer except that it checks for
// cancelation of the given context.Context.
func copyBuffer(ctx context.Context, dst io.Writer, src io.Reader, buf []byte) (written int64, err error) {
if buf == nil {
Expand Down