github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/blobserver/handlers/enumerate.go (about) 1 /* 2 Copyright 2011 Google Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package handlers 18 19 import ( 20 "fmt" 21 "io" 22 "log" 23 "net/http" 24 "strconv" 25 "time" 26 27 "camlistore.org/pkg/blob" 28 "camlistore.org/pkg/blobserver" 29 "camlistore.org/pkg/context" 30 ) 31 32 const defaultMaxEnumerate = 10000 33 const defaultEnumerateSize = 100 34 35 func CreateEnumerateHandler(storage blobserver.BlobEnumerator) http.Handler { 36 return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { 37 handleEnumerateBlobs(rw, req, storage) 38 }) 39 } 40 41 const errMsgMaxWaitSecWithAfter = "Can't use 'maxwaitsec' with 'after'.\n" 42 43 func handleEnumerateBlobs(rw http.ResponseWriter, req *http.Request, storage blobserver.BlobEnumerator) { 44 // Potential input parameters 45 formValueLimit := req.FormValue("limit") 46 formValueMaxWaitSec := req.FormValue("maxwaitsec") 47 formValueAfter := req.FormValue("after") 48 49 maxEnumerate := defaultMaxEnumerate 50 if config, ok := storage.(blobserver.MaxEnumerateConfig); ok { 51 maxEnumerate = config.MaxEnumerate() 52 } 53 54 limit := defaultEnumerateSize 55 if formValueLimit != "" { 56 n, err := strconv.ParseUint(formValueLimit, 10, 32) 57 if err != nil || n > uint64(maxEnumerate) { 58 limit = maxEnumerate 59 } else { 60 limit = int(n) 61 } 62 } 63 64 waitSeconds := 0 65 if formValueMaxWaitSec != "" { 66 waitSeconds, _ = strconv.Atoi(formValueMaxWaitSec) 67 if waitSeconds != 0 && formValueAfter != "" { 68 rw.WriteHeader(http.StatusBadRequest) 69 fmt.Fprintf(rw, errMsgMaxWaitSecWithAfter) 70 return 71 } 72 switch { 73 case waitSeconds < 0: 74 waitSeconds = 0 75 case waitSeconds > 30: 76 // TODO: don't hard-code 30. push this up into a blobserver interface 77 // for getting the configuration of the server (ultimately a flag in 78 // in the binary) 79 waitSeconds = 30 80 } 81 } 82 83 rw.Header().Set("Content-Type", "text/javascript; charset=utf-8") 84 io.WriteString(rw, "{\n \"blobs\": [\n") 85 86 loop := true 87 needsComma := false 88 deadline := time.Now().Add(time.Duration(waitSeconds) * time.Second) 89 after := "" 90 for loop && (waitSeconds == 0 || time.Now().After(deadline)) { 91 if waitSeconds == 0 { 92 loop = false 93 } 94 95 blobch := make(chan blob.SizedRef, 100) 96 resultch := make(chan error, 1) 97 go func() { 98 resultch <- storage.EnumerateBlobs(context.TODO(), blobch, formValueAfter, limit) 99 }() 100 101 gotBlobs := 0 102 for sb := range blobch { 103 gotBlobs++ 104 loop = false 105 blobName := sb.Ref.String() 106 if needsComma { 107 io.WriteString(rw, ",\n") 108 } 109 fmt.Fprintf(rw, " {\"blobRef\": \"%s\", \"size\": %d}", 110 blobName, sb.Size) 111 after = blobName 112 needsComma = true 113 } 114 if gotBlobs < limit { 115 after = "" 116 } 117 if err := <-resultch; err != nil { 118 log.Printf("Error during enumerate: %v", err) 119 fmt.Fprintf(rw, "{{{ SERVER ERROR }}}") 120 return 121 } 122 123 if loop { 124 blobserver.WaitForBlob(storage, deadline, nil) 125 } 126 } 127 io.WriteString(rw, "\n ]") 128 if after != "" { 129 fmt.Fprintf(rw, ",\n \"continueAfter\": \"%s\"", after) 130 } 131 const longPollSupported = true 132 if longPollSupported { 133 io.WriteString(rw, ",\n \"canLongPoll\": true") 134 } 135 io.WriteString(rw, "\n}\n") 136 }