From 3c6c4207bebd07320b431ce201be3ef1970acf62 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Thu, 9 Jun 2011 18:33:26 -0700 Subject: [PATCH] download handler: support for verifying a digest in a HEAD request for use in javascript uploads. --- lib/go/camli/blobref/blobref.go | 2 +- server/go/camlistored/ui.go | 33 ++++++++++++++++++++++++++++----- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/lib/go/camli/blobref/blobref.go b/lib/go/camli/blobref/blobref.go index 3cbca2429..e11108178 100644 --- a/lib/go/camli/blobref/blobref.go +++ b/lib/go/camli/blobref/blobref.go @@ -85,7 +85,7 @@ func (o *BlobRef) Equals(other *BlobRef) bool { func (o *BlobRef) Hash() hash.Hash { fn, ok := supportedDigests[o.hashName] if !ok { - return nil + return nil // TODO: return an error here, not nil } return fn() } diff --git a/server/go/camlistored/ui.go b/server/go/camlistored/ui.go index 75d4064d2..a1b7d61d7 100644 --- a/server/go/camlistored/ui.go +++ b/server/go/camlistored/ui.go @@ -211,6 +211,10 @@ func (ui *UIHandler) storageSeekFetcher() (blobref.SeekFetcher, os.Error) { } func (ui *UIHandler) serveDownload(rw http.ResponseWriter, req *http.Request) { + if req.Method != "GET" && req.Method != "HEAD" { + http.Error(rw, "Invalid download method", 400) + return + } if ui.Storage == nil { http.Error(rw, "No BlobRoot configured", 500) return @@ -230,8 +234,8 @@ func (ui *UIHandler) serveDownload(rw http.ResponseWriter, req *http.Request) { return } - blobref := blobref.Parse(m[1]) - if blobref == nil { + fbr := blobref.Parse(m[1]) + if fbr == nil { http.Error(rw, "Invalid blobref", 400) return } @@ -241,24 +245,43 @@ func (ui *UIHandler) serveDownload(rw http.ResponseWriter, req *http.Request) { filename = filename[1:] // remove leading slash } - fr, err := schema.NewFileReader(fetchSeeker, blobref) + fr, err := schema.NewFileReader(fetchSeeker, fbr) if err != nil { http.Error(rw, "Can't serve file: "+err.String(), 500) return } + defer fr.Close() // TODO: fr.FileSchema() and guess a mime type? For now: schema := fr.FileSchema() rw.Header().Set("Content-Type", "application/octet-stream") rw.Header().Set("Content-Length", fmt.Sprintf("%d", schema.Size)) + + if req.Method == "HEAD" { + vbr := blobref.Parse(req.FormValue("verifycontents")) + if vbr == nil { + return + } + hash := vbr.Hash() + if hash == nil { + return + } + io.Copy(hash, fr) // ignore errors, caught later + if vbr.HashMatches(hash) { + rw.Header().Set("X-Camli-Contents", vbr.String()) + } + return + } + n, err := io.Copy(rw, fr) + log.Printf("For %q request of %s: copied %d, %v", req.Method, req.URL.RawPath, n, err) if err != nil { - log.Printf("error serving download of file schema %s: %v", blobref, err) + log.Printf("error serving download of file schema %s: %v", fbr, err) return } if n != int64(schema.Size) { log.Printf("error serving download of file schema %s: sent %d, expected size of %d", - blobref, n, schema.Size) + fbr, n, schema.Size) return } }