github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/blobserver/handlers/stat.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 "log" 22 "net/http" 23 "strconv" 24 "time" 25 26 "camlistore.org/pkg/blob" 27 "camlistore.org/pkg/blobserver" 28 "camlistore.org/pkg/blobserver/protocol" 29 "camlistore.org/pkg/httputil" 30 ) 31 32 func CreateStatHandler(storage blobserver.BlobStatter) http.Handler { 33 return http.HandlerFunc(func(conn http.ResponseWriter, req *http.Request) { 34 handleStat(conn, req, storage) 35 }) 36 } 37 38 const maxStatBlobs = 1000 39 40 func handleStat(conn http.ResponseWriter, req *http.Request, storage blobserver.BlobStatter) { 41 res := new(protocol.StatResponse) 42 43 if configer, ok := storage.(blobserver.Configer); ok { 44 if conf := configer.Config(); conf != nil { 45 res.CanLongPoll = conf.CanLongPoll 46 } 47 } 48 49 needStat := map[blob.Ref]bool{} 50 51 switch req.Method { 52 case "POST": 53 fallthrough 54 case "GET", "HEAD": 55 camliVersion := req.FormValue("camliversion") 56 if camliVersion == "" { 57 httputil.BadRequestError(conn, "No camliversion") 58 return 59 } 60 n := 0 61 for { 62 n++ 63 key := fmt.Sprintf("blob%v", n) 64 value := req.FormValue(key) 65 if value == "" { 66 n-- 67 break 68 } 69 if n > maxStatBlobs { 70 httputil.BadRequestError(conn, "Too many stat blob checks") 71 return 72 } 73 ref, ok := blob.Parse(value) 74 if !ok { 75 httputil.BadRequestError(conn, "Bogus blobref for key "+key) 76 return 77 } 78 needStat[ref] = true 79 } 80 default: 81 httputil.BadRequestError(conn, "Invalid method.") 82 return 83 84 } 85 86 waitSeconds := 0 87 if waitStr := req.FormValue("maxwaitsec"); waitStr != "" { 88 waitSeconds, _ = strconv.Atoi(waitStr) 89 switch { 90 case waitSeconds < 0: 91 waitSeconds = 0 92 case waitSeconds > 30: 93 // TODO: don't hard-code 30. push this up into a blobserver interface 94 // for getting the configuration of the server (ultimately a flag in 95 // in the binary) 96 waitSeconds = 30 97 } 98 } 99 100 deadline := time.Now().Add(time.Duration(waitSeconds) * time.Second) 101 102 toStat := make([]blob.Ref, 0, len(needStat)) 103 buildToStat := func() { 104 toStat = toStat[:0] 105 for br := range needStat { 106 toStat = append(toStat, br) 107 } 108 } 109 110 for len(needStat) > 0 { 111 buildToStat() 112 blobch := make(chan blob.SizedRef) 113 resultch := make(chan error, 1) 114 go func() { 115 err := storage.StatBlobs(blobch, toStat) 116 close(blobch) 117 resultch <- err 118 }() 119 120 for sb := range blobch { 121 res.Stat = append(res.Stat, &protocol.RefAndSize{ 122 Ref: sb.Ref, 123 Size: uint32(sb.Size), 124 }) 125 delete(needStat, sb.Ref) 126 } 127 128 err := <-resultch 129 if err != nil { 130 log.Printf("Stat error: %v", err) 131 conn.WriteHeader(http.StatusInternalServerError) 132 return 133 } 134 135 if len(needStat) == 0 || waitSeconds == 0 || time.Now().After(deadline) { 136 break 137 } 138 139 buildToStat() 140 blobserver.WaitForBlob(storage, deadline, toStat) 141 } 142 143 httputil.ReturnJSON(conn, res) 144 }