diff --git a/server/go/camlistored/download.go b/server/go/camlistored/download.go new file mode 100644 index 000000000..65f825d52 --- /dev/null +++ b/server/go/camlistored/download.go @@ -0,0 +1,92 @@ +/* +Copyright 2011 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "fmt" + "http" + "log" + "io" + "os" + + "camli/blobref" + "camli/blobserver" + "camli/schema" +) + +type DownloadHandler struct { + Fetcher blobref.StreamingFetcher + Cache blobserver.Storage +} + +func (dh *DownloadHandler) storageSeekFetcher() (blobref.SeekFetcher, os.Error) { + return blobref.SeekerFromStreamingFetcher(dh.Fetcher) // TODO: pass dh.Cache? +} + +func (dh *DownloadHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request, file *blobref.BlobRef) { + if req.Method != "GET" && req.Method != "HEAD" { + http.Error(rw, "Invalid download method", 400) + return + } + + fetchSeeker, err := dh.storageSeekFetcher() + if err != nil { + http.Error(rw, err.String(), 500) + return + } + + fr, err := schema.NewFileReader(fetchSeeker, file) + 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", file, err) + return + } + if n != int64(schema.Size) { + log.Printf("error serving download of file schema %s: sent %d, expected size of %d", + file, n, schema.Size) + return + } + +} diff --git a/server/go/camlistored/ui.go b/server/go/camlistored/ui.go index c359a99a8..629be0fac 100644 --- a/server/go/camlistored/ui.go +++ b/server/go/camlistored/ui.go @@ -253,23 +253,12 @@ 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 } - fetchSeeker, err := ui.storageSeekFetcher() - if err != nil { - http.Error(rw, err.String(), 500) - return - } - suffix := req.Header.Get("X-PrefixHandler-PathSuffix") - m := downloadPattern.FindStringSubmatch(suffix) if m == nil { httputil.ErrorRouting(rw, req) @@ -282,50 +271,11 @@ func (ui *UIHandler) serveDownload(rw http.ResponseWriter, req *http.Request) { return } - filename := m[2] - if len(filename) > 0 { - filename = filename[1:] // remove leading slash - } - - 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", fbr, err) - return - } - if n != int64(schema.Size) { - log.Printf("error serving download of file schema %s: sent %d, expected size of %d", - fbr, n, schema.Size) - return + dh := &DownloadHandler{ + Fetcher: ui.Storage, + Cache: ui.Cache, } + dh.ServeHTTP(rw, req, fbr) } func (ui *UIHandler) serveThumbnail(rw http.ResponseWriter, req *http.Request) { @@ -365,11 +315,6 @@ func (ui *UIHandler) serveThumbnail(rw http.ResponseWriter, req *http.Request) { return } - filename := m[2] - if len(filename) > 0 { - filename = filename[1:] // remove leading slash - } - fr, err := schema.NewFileReader(fetchSeeker, blobref) if err != nil { http.Error(rw, "Can't serve file: "+err.String(), 500)