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  }