func (s *SlimeExt) Retrieve(e *external.External, key, file string) error { resp, err := http.Get(s.keyURL(key)) if err != nil { return fmt.Errorf("Couldn't GET %v: %v", s.keyURL(key), err) } defer resp.Body.Close() if resp.StatusCode < 200 || resp.StatusCode >= 300 { return fmt.Errorf("Couldn't GET %v: %v", resp.Request.URL, resp.Status) } fh, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { return err } sha := sha256.New() reader := io.TeeReader(external.NewProgressReader(resp.Body, e), sha) _, err = io.Copy(fh, reader) if err != nil { fh.Close() return err } err = fh.Close() if err != nil { return err } if resp.Header.Get("X-Content-SHA256") != "" { have := sha.Sum(nil) want, _ := hex.DecodeString(resp.Header.Get("X-Content-SHA256")) if !bytes.Equal(have, want) { return errors.New("bad checksum of response") } } return nil }
func (s *SlimeExt) Store(e *external.External, key, file string) error { fh, err := os.Open(file) if err != nil { return err } defer fh.Close() shaDone := make(chan struct{}) var sha []byte var length int64 var shaError error go func() { defer close(shaDone) hash := sha256.New() length, shaError = io.Copy(hash, fh) if shaError != nil { return } sha = hash.Sum(nil) _, shaError = fh.Seek(0, 0) }() req, err := http.NewRequest("HEAD", s.keyURL(key), nil) if err != nil { return fmt.Errorf("Couldn't create request for %s: %v", s.keyURL(key), err) } resp, err := http.DefaultClient.Do(req) if err != nil { return fmt.Errorf("Couldn't HEAD %v: %v", req.URL, err) } resp.Body.Close() <-shaDone if resp.StatusCode >= 200 && resp.StatusCode < 300 { if oldSHA := resp.Header.Get("X-Content-SHA256"); oldSHA != "" { if strings.ToLower(oldSHA) == hex.EncodeToString(sha) { // Key already exists with the correct data return nil } } } req, err = http.NewRequest("PUT", s.keyURL(key), external.NewProgressReader(fh, e)) if err != nil { return fmt.Errorf("Couldn't create request for %s: %v", s.keyURL(key), err) } req.Header.Set("X-Content-SHA256", hex.EncodeToString(sha)) req.Header.Set("Content-Length", strconv.FormatInt(length, 10)) resp, err = http.DefaultClient.Do(req) if err != nil { return fmt.Errorf("Couldn't PUT to %v: %v", req.URL, err) } resp.Body.Close() if resp.StatusCode < 200 || resp.StatusCode >= 300 { return fmt.Errorf("Couldn't PUT to %v: %v", req.URL, resp.Status) } return nil }