16 func cacheDir() (string, error) {
17 d, err := os.UserCacheDir()
18 return path.Join(d, "tarweb"), err
21 func Extract(r *tar.Reader, dir string) error {
25 break // End of archive
31 if hdr.FileInfo().IsDir() {
32 err := os.MkdirAll(path.Join(dir, hdr.Name), 0755)
33 if err != nil && !errors.Is(err, fs.ErrExist) {
34 return fmt.Errorf("extract %s: %w", hdr.Name, err)
38 dst := path.Join(dir, hdr.Name)
39 f, err := os.Create(dst)
41 return fmt.Errorf("extract %s: %v", hdr.Name, err)
43 if _, err := io.Copy(f, r); err != nil {
44 return fmt.Errorf("extract %s: %v", hdr.Name, err)
51 var urls []string = []string{"http://127.0.0.1:8000/test.tar"}
53 var archives = make(map[string]time.Time)
55 func getIfModified(url string, since time.Time) (*http.Response, error) {
56 req, err := http.NewRequest(http.MethodGet, url, nil)
60 req.Header.Add("If-Modified-Since", since.UTC().Format(time.RFC1123Z))
61 return http.DefaultClient.Do(req)
64 func fetchloop(root string, sleep time.Duration) {
66 for url, since := range archives {
67 resp, err := getIfModified(url, since)
69 log.Println("get archive:", err)
72 if resp.StatusCode == http.StatusNotModified {
73 log.Printf("%s unmodified", url)
76 if resp.Header.Get("Last-Modified") != "" {
77 modified, err := time.Parse(time.RFC1123, resp.Header.Get("Last-Modified"))
79 log.Printf("parse last modified time for %s: %v", url, err)
82 archives[url] = modified
84 rd := tar.NewReader(resp.Body)
85 if err := Extract(rd, root); err != nil {
86 log.Printf("extract %s: %v", url, err)
95 archives["http://127.0.0.1:8000/test.tar"] = time.Time{}
96 archives["http://127.0.0.1:8000/meerkat.tar"] = time.Time{}
97 cache, err := cacheDir()
101 if err := os.MkdirAll(cache, 0755); err != nil {
104 log.Println("serving archives from", cache)
105 go fetchloop(cache, 20*time.Second)
106 log.Fatal(http.ListenAndServe(":6969", http.FileServer(http.FS(os.DirFS(cache)))))