@@ -13,6 +13,7 @@ import (
1313 "path"
1414 "path/filepath"
1515 "strings"
16+ "time"
1617
1718 "github.com/cheggaaa/pb/v3"
1819 "github.com/containerd/continuity/fs"
@@ -44,6 +45,8 @@ const (
4445type Result struct {
4546 Status Status
4647 CachePath string // "/Users/foo/Library/Caches/lima/download/by-url-sha256/<SHA256_OF_URL>/data"
48+ LastModified time.Time
49+ ContentType string
4750 ValidatedDigest bool
4851}
4952
@@ -118,6 +121,38 @@ func WithExpectedDigest(expectedDigest digest.Digest) Opt {
118121 }
119122}
120123
124+ func readFile (path string ) string {
125+ if path == "" {
126+ return ""
127+ }
128+ if _ , err := os .Stat (path ); err != nil {
129+ return ""
130+ }
131+ b , err := os .ReadFile (path )
132+ if err != nil {
133+ return ""
134+ }
135+ return string (b )
136+ }
137+
138+ func readTime (path string ) time.Time {
139+ if path == "" {
140+ return time.Time {}
141+ }
142+ if _ , err := os .Stat (path ); err != nil {
143+ return time.Time {}
144+ }
145+ b , err := os .ReadFile (path )
146+ if err != nil {
147+ return time.Time {}
148+ }
149+ t , err := time .Parse (http .TimeFormat , string (b ))
150+ if err != nil {
151+ return time.Time {}
152+ }
153+ return t
154+ }
155+
121156// Download downloads the remote resource into the local path.
122157//
123158// Download caches the remote resource if WithCache or WithCacheDir option is specified.
@@ -175,7 +210,7 @@ func Download(ctx context.Context, local, remote string, opts ...Opt) (*Result,
175210 }
176211
177212 if o .cacheDir == "" {
178- if err := downloadHTTP (ctx , localPath , remote , o .description , o .expectedDigest ); err != nil {
213+ if err := downloadHTTP (ctx , localPath , "" , "" , remote , o .description , o .expectedDigest ); err != nil {
179214 return nil , err
180215 }
181216 res := & Result {
@@ -187,6 +222,8 @@ func Download(ctx context.Context, local, remote string, opts ...Opt) (*Result,
187222
188223 shad := cacheDirectoryPath (o .cacheDir , remote )
189224 shadData := filepath .Join (shad , "data" )
225+ shadTime := filepath .Join (shad , "time" )
226+ shadType := filepath .Join (shad , "type" )
190227 shadDigest , err := cacheDigestPath (shad , o .expectedDigest )
191228 if err != nil {
192229 return nil , err
@@ -210,6 +247,8 @@ func Download(ctx context.Context, local, remote string, opts ...Opt) (*Result,
210247 res := & Result {
211248 Status : StatusUsedCache ,
212249 CachePath : shadData ,
250+ LastModified : readTime (shadTime ),
251+ ContentType : readFile (shadType ),
213252 ValidatedDigest : o .expectedDigest != "" ,
214253 }
215254 return res , nil
@@ -224,7 +263,7 @@ func Download(ctx context.Context, local, remote string, opts ...Opt) (*Result,
224263 if err := os .WriteFile (shadURL , []byte (remote ), 0o644 ); err != nil {
225264 return nil , err
226265 }
227- if err := downloadHTTP (ctx , shadData , remote , o .description , o .expectedDigest ); err != nil {
266+ if err := downloadHTTP (ctx , shadData , shadTime , shadType , remote , o .description , o .expectedDigest ); err != nil {
228267 return nil , err
229268 }
230269 // no need to pass the digest to copyLocal(), as we already verified the digest
@@ -239,6 +278,8 @@ func Download(ctx context.Context, local, remote string, opts ...Opt) (*Result,
239278 res := & Result {
240279 Status : StatusDownloaded ,
241280 CachePath : shadData ,
281+ LastModified : readTime (shadTime ),
282+ ContentType : readFile (shadType ),
242283 ValidatedDigest : o .expectedDigest != "" ,
243284 }
244285 return res , nil
@@ -266,6 +307,8 @@ func Cached(remote string, opts ...Opt) (*Result, error) {
266307
267308 shad := cacheDirectoryPath (o .cacheDir , remote )
268309 shadData := filepath .Join (shad , "data" )
310+ shadTime := filepath .Join (shad , "time" )
311+ shadType := filepath .Join (shad , "type" )
269312 shadDigest , err := cacheDigestPath (shad , o .expectedDigest )
270313 if err != nil {
271314 return nil , err
@@ -285,6 +328,8 @@ func Cached(remote string, opts ...Opt) (*Result, error) {
285328 res := & Result {
286329 Status : StatusUsedCache ,
287330 CachePath : shadData ,
331+ LastModified : readTime (shadTime ),
332+ ContentType : readFile (shadType ),
288333 ValidatedDigest : o .expectedDigest != "" ,
289334 }
290335 return res , nil
@@ -293,6 +338,8 @@ func Cached(remote string, opts ...Opt) (*Result, error) {
293338// cacheDirectoryPath returns the cache subdirectory path.
294339// - "url" file contains the url
295340// - "data" file contains the data
341+ // - "time" file contains the time (Last-Modified header)
342+ // - "type" file contains the type (Content-Type header)
296343func cacheDirectoryPath (cacheDir , remote string ) string {
297344 return filepath .Join (cacheDir , "download" , "by-url-sha256" , fmt .Sprintf ("%x" , sha256 .Sum256 ([]byte (remote ))))
298345}
@@ -470,7 +517,7 @@ func validateLocalFileDigest(localPath string, expectedDigest digest.Digest) err
470517 return nil
471518}
472519
473- func downloadHTTP (ctx context.Context , localPath , url , description string , expectedDigest digest.Digest ) error {
520+ func downloadHTTP (ctx context.Context , localPath , lastModified , contentType , url , description string , expectedDigest digest.Digest ) error {
474521 if localPath == "" {
475522 return fmt .Errorf ("downloadHTTP: got empty localPath" )
476523 }
@@ -489,6 +536,18 @@ func downloadHTTP(ctx context.Context, localPath, url, description string, expec
489536 if err != nil {
490537 return err
491538 }
539+ if lastModified != "" {
540+ lm := resp .Header .Get ("Last-Modified" )
541+ if err := os .WriteFile (lastModified , []byte (lm ), 0o644 ); err != nil {
542+ return err
543+ }
544+ }
545+ if contentType != "" {
546+ ct := resp .Header .Get ("Content-Type" )
547+ if err := os .WriteFile (contentType , []byte (ct ), 0o644 ); err != nil {
548+ return err
549+ }
550+ }
492551 defer resp .Body .Close ()
493552 bar , err := progressbar .New (resp .ContentLength )
494553 if err != nil {
0 commit comments