func handleEnumerateBlobs(conn http.ResponseWriter, req *http.Request, storage blobserver.BlobEnumerator) { if w, ok := storage.(blobserver.ContextWrapper); ok { storage = w.WrapContext(req) } // Potential input parameters formValueLimit := req.FormValue("limit") formValueMaxWaitSec := req.FormValue("maxwaitsec") formValueAfter := req.FormValue("after") maxEnumerate := defaultMaxEnumerate if config, ok := blobserver.Unwrap(storage).(blobserver.MaxEnumerateConfig); ok { maxEnumerate = config.MaxEnumerate() - 1 // Since we'll add one below. } limit := defaultEnumerateSize if formValueLimit != "" { n, err := strconv.ParseUint(formValueLimit, 10, 32) if err != nil || n > uint64(maxEnumerate) { limit = maxEnumerate } else { limit = int(n) } } waitSeconds := 0 if formValueMaxWaitSec != "" { waitSeconds, _ = strconv.Atoi(formValueMaxWaitSec) if waitSeconds != 0 && formValueAfter != "" { conn.WriteHeader(http.StatusBadRequest) fmt.Fprintf(conn, errMsgMaxWaitSecWithAfter) return } switch { case waitSeconds < 0: waitSeconds = 0 case waitSeconds > 30: // TODO: don't hard-code 30. push this up into a blobserver interface // for getting the configuration of the server (ultimately a flag in // in the binary) waitSeconds = 30 } } conn.Header().Set("Content-Type", "text/javascript; charset=utf-8") fmt.Fprintf(conn, "{\n \"blobs\": [\n") blobch := make(chan blobref.SizedBlobRef, 100) resultch := make(chan error, 1) go func() { resultch <- storage.EnumerateBlobs(blobch, formValueAfter, limit+1, time.Duration(waitSeconds)*time.Second) }() after := "" needsComma := false endsReached := 0 gotBlobs := 0 for endsReached < 2 { select { case sb, ok := <-blobch: if !ok { endsReached++ if gotBlobs <= limit { after = "" } continue } gotBlobs++ if gotBlobs > limit { // We requested one more from storage than the user asked for. // Now we know to return a "continueAfter" response key. // But we don't return this blob. continue } blobName := sb.BlobRef.String() if needsComma { fmt.Fprintf(conn, ",\n") } fmt.Fprintf(conn, " {\"blobRef\": \"%s\", \"size\": %d}", blobName, sb.Size) after = blobName needsComma = true case err := <-resultch: if err != nil { log.Printf("Error during enumerate: %v", err) fmt.Fprintf(conn, "{{{ SERVER ERROR }}}") return } endsReached++ } } fmt.Fprintf(conn, "\n ]") if after != "" { fmt.Fprintf(conn, ",\n \"continueAfter\": \"%s\"", after) } const longPollSupported = true if longPollSupported { fmt.Fprintf(conn, ",\n \"canLongPoll\": true") } fmt.Fprintf(conn, "\n}\n") }
func handleEnumerateBlobs(rw http.ResponseWriter, req *http.Request, storage blobserver.BlobEnumerator) { // Potential input parameters formValueLimit := req.FormValue("limit") formValueMaxWaitSec := req.FormValue("maxwaitsec") formValueAfter := req.FormValue("after") maxEnumerate := defaultMaxEnumerate if config, ok := storage.(blobserver.MaxEnumerateConfig); ok { maxEnumerate = config.MaxEnumerate() } limit := defaultEnumerateSize if formValueLimit != "" { n, err := strconv.ParseUint(formValueLimit, 10, 32) if err != nil || n > uint64(maxEnumerate) { limit = maxEnumerate } else { limit = int(n) } } waitSeconds := 0 if formValueMaxWaitSec != "" { waitSeconds, _ = strconv.Atoi(formValueMaxWaitSec) if waitSeconds != 0 && formValueAfter != "" { rw.WriteHeader(http.StatusBadRequest) fmt.Fprintf(rw, errMsgMaxWaitSecWithAfter) return } switch { case waitSeconds < 0: waitSeconds = 0 case waitSeconds > 30: // TODO: don't hard-code 30. push this up into a blobserver interface // for getting the configuration of the server (ultimately a flag in // in the binary) waitSeconds = 30 } } rw.Header().Set("Content-Type", "text/javascript; charset=utf-8") io.WriteString(rw, "{\n \"blobs\": [\n") loop := true needsComma := false deadline := time.Now().Add(time.Duration(waitSeconds) * time.Second) after := "" for loop && (waitSeconds == 0 || time.Now().After(deadline)) { if waitSeconds == 0 { loop = false } blobch := make(chan blob.SizedRef, 100) resultch := make(chan error, 1) go func() { resultch <- storage.EnumerateBlobs(context.TODO(), blobch, formValueAfter, limit) }() gotBlobs := 0 for sb := range blobch { gotBlobs++ loop = false blobName := sb.Ref.String() if needsComma { io.WriteString(rw, ",\n") } fmt.Fprintf(rw, " {\"blobRef\": \"%s\", \"size\": %d}", blobName, sb.Size) after = blobName needsComma = true } if gotBlobs < limit { after = "" } if err := <-resultch; err != nil { log.Printf("Error during enumerate: %v", err) fmt.Fprintf(rw, "{{{ SERVER ERROR }}}") return } if loop { blobserver.WaitForBlob(storage, deadline, nil) } } io.WriteString(rw, "\n ]") if after != "" { fmt.Fprintf(rw, ",\n \"continueAfter\": \"%s\"", after) } const longPollSupported = true if longPollSupported { io.WriteString(rw, ",\n \"canLongPoll\": true") } io.WriteString(rw, "\n}\n") }