Start of blobserver support for share/via requests.

This commit is contained in:
Brad Fitzpatrick 2011-01-25 22:46:21 -08:00
parent e4d009ee77
commit 6132d7f9e7
2 changed files with 86 additions and 6 deletions

View File

@ -32,7 +32,7 @@ func handleCamli(conn http.ResponseWriter, req *http.Request) {
case "/camli/enumerate-blobs":
handler = auth.RequireAuth(handleEnumerateBlobs)
default:
handler = auth.RequireAuth(createGetHandler(blobFetcher))
handler = createGetHandler(blobFetcher)
}
case "POST":
switch req.URL.Path {

View File

@ -1,26 +1,106 @@
package main
import (
"camli/auth"
"camli/blobref"
"camli/httputil"
"fmt"
"http"
"os"
"io"
"json"
"log"
"strings"
"time"
)
func createGetHandler(fetcher blobref.Fetcher) func (http.ResponseWriter, *http.Request) {
return func (conn http.ResponseWriter, req *http.Request) {
handleGet(conn, req, fetcher);
func createGetHandler(fetcher blobref.Fetcher) func(http.ResponseWriter, *http.Request) {
return func(conn http.ResponseWriter, req *http.Request) {
handleGet(conn, req, fetcher)
}
}
const fetchFailureDelayNs = 200e6 // 200 ms
const maxJsonSize = 10 * 1024
func handleGet(conn http.ResponseWriter, req *http.Request, fetcher blobref.Fetcher) {
isOwner := auth.IsAuthorized(req)
blobRef := BlobFromUrlPath(req.URL.Path)
if blobRef == nil {
httputil.BadRequestError(conn, "Malformed GET URL.")
return
}
var viaBlobs []*blobref.BlobRef
if !isOwner {
viaPathOkay := false
startTime := time.Nanoseconds()
defer func() {
if !viaPathOkay {
// Insert a delay, to hide timing attacks probing
// for the existence of blobs.
sleep := fetchFailureDelayNs - (time.Nanoseconds() - startTime)
if sleep > 0 {
time.Sleep(sleep)
}
}
}()
viaBlobs = make([]*blobref.BlobRef, 0)
if via := req.FormValue("via"); via != "" {
for _, vs := range strings.Split("via", ",", -1) {
if br := blobref.Parse(vs); br == nil {
httputil.BadRequestError(conn, "Malformed blobref in via param")
return
} else {
viaBlobs = append(viaBlobs, br)
}
}
}
fetchChain := make([]*blobref.BlobRef, 0)
fetchChain = append(fetchChain, viaBlobs...)
fetchChain = append(fetchChain, blobRef)
for i, br := range fetchChain {
switch i {
case 0:
file, size, err := fetcher.Fetch(br)
if err != nil {
log.Printf("Fetch chain 0 of %s failed: %v", br.String(), err)
conn.WriteHeader(http.StatusUnauthorized)
return
}
defer file.Close()
if size > maxJsonSize {
log.Printf("Fetch chain 0 of %s too large", br.String())
conn.WriteHeader(http.StatusUnauthorized)
return
}
jd := json.NewDecoder(file)
m := make(map[string]interface{})
if err := jd.Decode(&m); err != nil {
log.Printf("Fetch chain 0 of %s wasn't JSON: %v", br.String(), err)
conn.WriteHeader(http.StatusUnauthorized)
return
}
if m["camliType"].(string) != "share" {
log.Printf("Fetch chain 0 of %s wasn't a share", br.String())
conn.WriteHeader(http.StatusUnauthorized)
return
}
if len(fetchChain) > 1 && fetchChain[1].String() != m["target"].(string) {
log.Printf("Fetch chain 0->1 (%s -> %q) unauthorized, expected hop to %q",
br.String(), fetchChain[1].String(), m["target"])
conn.WriteHeader(http.StatusUnauthorized)
return
}
default:
log.Printf("TODO: FETCH %s", br.String())
}
}
viaPathOkay = true
}
file, size, err := fetcher.Fetch(blobRef)
switch err {
case nil:
@ -60,8 +140,8 @@ func handleGet(conn http.ResponseWriter, req *http.Request, fetcher blobref.Fetc
if !reqRange.IsWholeFile() {
conn.SetHeader("Content-Range",
fmt.Sprintf("bytes %d-%d/%d", reqRange.SkipBytes,
reqRange.SkipBytes + remainBytes,
size))
reqRange.SkipBytes+remainBytes,
size))
conn.WriteHeader(http.StatusPartialContent)
}
bytesCopied, err := io.Copy(conn, input)