Home Download Docs Code Community
     1	/*
     2	Copyright 2011 The Perkeep Authors
     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		"perkeep.org/internal/httputil"
    27		"perkeep.org/pkg/blob"
    28		"perkeep.org/pkg/blobserver"
    29		"perkeep.org/pkg/blobserver/protocol"
    30	)
    31	
    32	func CreateStatHandler(storage blobserver.BlobStatter) http.Handler {
    33		return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
    34			handleStat(rw, req, storage)
    35		})
    36	}
    37	
    38	const maxStatBlobs = 1000
    39	
    40	func handleStat(rw 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", "GET", "HEAD":
    53		default:
    54			httputil.BadRequestError(rw, "Invalid method.")
    55			return
    56		}
    57		camliVersion := req.FormValue("camliversion")
    58		if camliVersion == "" {
    59			httputil.BadRequestError(rw, "No camliversion")
    60			return
    61		}
    62		n := 0
    63		for {
    64			n++
    65			key := fmt.Sprintf("blob%v", n)
    66			value := req.FormValue(key)
    67			if value == "" {
    68				n--
    69				break
    70			}
    71			if n > maxStatBlobs {
    72				httputil.BadRequestError(rw, "Too many stat blob checks")
    73				return
    74			}
    75			ref, ok := blob.Parse(value)
    76			if !ok {
    77				httputil.BadRequestError(rw, "Bogus blobref for key "+key)
    78				return
    79			}
    80			needStat[ref] = true
    81		}
    82	
    83		waitSeconds := 0
    84		if waitStr := req.FormValue("maxwaitsec"); waitStr != "" {
    85			waitSeconds, _ = strconv.Atoi(waitStr)
    86			switch {
    87			case waitSeconds < 0:
    88				waitSeconds = 0
    89			case waitSeconds > 30:
    90				// TODO: don't hard-code 30.  push this up into a blobserver interface
    91				// for getting the configuration of the server (ultimately a flag in
    92				// in the binary)
    93				waitSeconds = 30
    94			}
    95		}
    96	
    97		deadline := time.Now().Add(time.Duration(waitSeconds) * time.Second)
    98	
    99		toStat := make([]blob.Ref, 0, len(needStat))
   100		buildToStat := func() {
   101			toStat = toStat[:0]
   102			for br := range needStat {
   103				toStat = append(toStat, br)
   104			}
   105		}
   106	
   107		buildToStat()
   108		for len(needStat) > 0 {
   109			err := storage.StatBlobs(req.Context(), toStat, func(sb blob.SizedRef) error {
   110				res.Stat = append(res.Stat, sb)
   111				delete(needStat, sb.Ref)
   112				return nil
   113			})
   114			if err != nil {
   115				log.Printf("Stat error: %v", err)
   116				rw.WriteHeader(http.StatusInternalServerError)
   117				return
   118			}
   119			if len(needStat) == 0 || waitSeconds == 0 || time.Now().After(deadline) {
   120				break
   121			}
   122			buildToStat()
   123			blobserver.WaitForBlob(storage, deadline, toStat)
   124		}
   125	
   126		httputil.ReturnJSON(rw, res)
   127	}
Website layout inspired by memcached.
Content by the authors.