github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/httputil/httputil.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 httputil contains a bunch of HTTP utility code, some generic,
    18  // and some Camlistore-specific.
    19  package httputil
    20  
    21  import (
    22  	"bytes"
    23  	"encoding/json"
    24  	"fmt"
    25  	"io"
    26  	"log"
    27  	"net"
    28  	"net/http"
    29  	"net/url"
    30  	"os"
    31  	"path"
    32  	"strconv"
    33  	"strings"
    34  
    35  	"camlistore.org/pkg/blob"
    36  )
    37  
    38  // IsGet reports whether r.Method is a GET or HEAD request.
    39  func IsGet(r *http.Request) bool {
    40  	return r.Method == "GET" || r.Method == "HEAD"
    41  }
    42  
    43  func ErrorRouting(conn http.ResponseWriter, req *http.Request) {
    44  	http.Error(conn, "Handlers wired up wrong; this path shouldn't be hit", 500)
    45  	log.Printf("Internal routing error on %q", req.URL.Path)
    46  }
    47  
    48  func BadRequestError(conn http.ResponseWriter, errorMessage string, args ...interface{}) {
    49  	conn.WriteHeader(http.StatusBadRequest)
    50  	log.Printf("Bad request: %s", fmt.Sprintf(errorMessage, args...))
    51  	fmt.Fprintf(conn, "<h1>Bad Request</h1>")
    52  }
    53  
    54  func ForbiddenError(conn http.ResponseWriter, errorMessage string, args ...interface{}) {
    55  	conn.WriteHeader(http.StatusForbidden)
    56  	log.Printf("Forbidden: %s", fmt.Sprintf(errorMessage, args...))
    57  	fmt.Fprintf(conn, "<h1>Forbidden</h1>")
    58  }
    59  
    60  func RequestEntityTooLargeError(conn http.ResponseWriter) {
    61  	conn.WriteHeader(http.StatusRequestEntityTooLarge)
    62  	fmt.Fprintf(conn, "<h1>Request entity is too large</h1>")
    63  }
    64  
    65  func ServeError(conn http.ResponseWriter, req *http.Request, err error) {
    66  	conn.WriteHeader(http.StatusInternalServerError)
    67  	if IsLocalhost(req) || os.Getenv("CAMLI_DEV_CAMLI_ROOT") != "" {
    68  		fmt.Fprintf(conn, "Server error: %s\n", err)
    69  		return
    70  	}
    71  	fmt.Fprintf(conn, "An internal error occured, sorry.")
    72  }
    73  
    74  func ReturnJSON(rw http.ResponseWriter, data interface{}) {
    75  	ReturnJSONCode(rw, 200, data)
    76  }
    77  
    78  func ReturnJSONCode(rw http.ResponseWriter, code int, data interface{}) {
    79  	rw.Header().Set("Content-Type", "text/javascript")
    80  	js, err := json.MarshalIndent(data, "", "  ")
    81  	if err != nil {
    82  		BadRequestError(rw, fmt.Sprintf("JSON serialization error: %v", err))
    83  		return
    84  	}
    85  	rw.Header().Set("Content-Length", strconv.Itoa(len(js)+1))
    86  	rw.WriteHeader(code)
    87  	rw.Write(js)
    88  	rw.Write([]byte("\n"))
    89  }
    90  
    91  // PrefixHandler wraps another Handler and verifies that all requests'
    92  // Path begin with Prefix. If they don't, a 500 error is returned.
    93  // If they do, the headers PathBaseHeader and PathSuffixHeader are set
    94  // on the request before proxying to Handler.
    95  // PathBaseHeader is just the value of Prefix.
    96  // PathSuffixHeader is the part of the path that follows Prefix.
    97  type PrefixHandler struct {
    98  	Prefix  string
    99  	Handler http.Handler
   100  }
   101  
   102  const (
   103  	PathBaseHeader   = "X-Prefixhandler-Pathbase"
   104  	PathSuffixHeader = "X-Prefixhandler-Pathsuffix"
   105  )
   106  
   107  func (p *PrefixHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
   108  	if !strings.HasPrefix(req.URL.Path, p.Prefix) {
   109  		http.Error(rw, "Inconfigured PrefixHandler", 500)
   110  		return
   111  	}
   112  	req.Header.Set(PathBaseHeader, p.Prefix)
   113  	req.Header.Set(PathSuffixHeader, strings.TrimPrefix(req.URL.Path, p.Prefix))
   114  	p.Handler.ServeHTTP(rw, req)
   115  }
   116  
   117  // PathBase returns a Request's base path, if it went via a PrefixHandler.
   118  func PathBase(req *http.Request) string { return req.Header.Get(PathBaseHeader) }
   119  
   120  // PathSuffix returns a Request's suffix path, if it went via a PrefixHandler.
   121  func PathSuffix(req *http.Request) string { return req.Header.Get(PathSuffixHeader) }
   122  
   123  // BaseURL returns the base URL (scheme + host and optional port +
   124  // blobserver prefix) that should be used for requests (and responses)
   125  // subsequent to req. The returned URL does not end in a trailing slash.
   126  // The scheme and host:port are taken from urlStr if present,
   127  // or derived from req otherwise.
   128  // The prefix part comes from urlStr.
   129  func BaseURL(urlStr string, req *http.Request) (string, error) {
   130  	var baseURL string
   131  	defaultURL, err := url.Parse(urlStr)
   132  	if err != nil {
   133  		return baseURL, err
   134  	}
   135  	prefix := path.Clean(defaultURL.Path)
   136  	scheme := "http"
   137  	if req.TLS != nil {
   138  		scheme = "https"
   139  	}
   140  	host := req.Host
   141  	if defaultURL.Host != "" {
   142  		host = defaultURL.Host
   143  	}
   144  	if defaultURL.Scheme != "" {
   145  		scheme = defaultURL.Scheme
   146  	}
   147  	baseURL = scheme + "://" + host + prefix
   148  	return baseURL, nil
   149  }
   150  
   151  // RequestTargetPort returns the port targetted by the client
   152  // in req. If not present, it returns 80, or 443 if TLS is used.
   153  func RequestTargetPort(req *http.Request) int {
   154  	_, portStr, err := net.SplitHostPort(req.Host)
   155  	if err == nil && portStr != "" {
   156  		port, err := strconv.ParseInt(portStr, 0, 64)
   157  		if err == nil {
   158  			return int(port)
   159  		}
   160  	}
   161  	if req.TLS != nil {
   162  		return 443
   163  	}
   164  	return 80
   165  }
   166  
   167  // Recover is meant to be used at the top of handlers with "defer"
   168  // to catch errors from MustGet, etc:
   169  //
   170  //   func handler(rw http.ResponseWriter, req *http.Request) {
   171  //       defer httputil.Recover(rw, req)
   172  //       id := req.MustGet("id")
   173  //       ....
   174  //
   175  // Recover will send the proper HTTP error type and message (e.g.
   176  // a 400 Bad Request for MustGet)
   177  func Recover(rw http.ResponseWriter, req *http.Request) {
   178  	RecoverJSON(rw, req) // TODO: for now. alternate format?
   179  }
   180  
   181  // RecoverJSON is like Recover but returns with a JSON response.
   182  func RecoverJSON(rw http.ResponseWriter, req *http.Request) {
   183  	e := recover()
   184  	if e == nil {
   185  		return
   186  	}
   187  	ServeJSONError(rw, e)
   188  }
   189  
   190  type httpCoder interface {
   191  	HTTPCode() int
   192  }
   193  
   194  // An InvalidMethodError is returned when an HTTP handler is invoked
   195  // with an unsupported method.
   196  type InvalidMethodError struct{}
   197  
   198  func (InvalidMethodError) Error() string { return "invalid method" }
   199  func (InvalidMethodError) HTTPCode() int { return http.StatusMethodNotAllowed }
   200  
   201  // A MissingParameterError represents a missing HTTP parameter.
   202  // The underlying string is the missing parameter name.
   203  type MissingParameterError string
   204  
   205  func (p MissingParameterError) Error() string { return fmt.Sprintf("Missing parameter %q", string(p)) }
   206  func (MissingParameterError) HTTPCode() int   { return http.StatusBadRequest }
   207  
   208  // An InvalidParameterError represents an invalid HTTP parameter.
   209  // The underlying string is the invalid parameter name, not value.
   210  type InvalidParameterError string
   211  
   212  func (p InvalidParameterError) Error() string { return fmt.Sprintf("Invalid parameter %q", string(p)) }
   213  func (InvalidParameterError) HTTPCode() int   { return http.StatusBadRequest }
   214  
   215  // A ServerError is a generic 500 error.
   216  type ServerError string
   217  
   218  func (e ServerError) Error() string { return string(e) }
   219  func (ServerError) HTTPCode() int   { return http.StatusInternalServerError }
   220  
   221  // MustGet returns a non-empty GET (or HEAD) parameter param and panics
   222  // with a special error as caught by a deferred httputil.Recover.
   223  func MustGet(req *http.Request, param string) string {
   224  	if !IsGet(req) {
   225  		panic(InvalidMethodError{})
   226  	}
   227  	v := req.FormValue(param)
   228  	if v == "" {
   229  		panic(MissingParameterError(param))
   230  	}
   231  	return v
   232  }
   233  
   234  // MustGetBlobRef returns a non-nil BlobRef from req, as given by param.
   235  // If it doesn't, it panics with a value understood by Recover or RecoverJSON.
   236  func MustGetBlobRef(req *http.Request, param string) blob.Ref {
   237  	br, ok := blob.Parse(MustGet(req, param))
   238  	if !ok {
   239  		panic(InvalidParameterError(param))
   240  	}
   241  	return br
   242  }
   243  
   244  // OptionalInt returns the integer in req given by param, or 0 if not present.
   245  // If the form value is not an integer, it panics with a a value understood by Recover or RecoverJSON.
   246  func OptionalInt(req *http.Request, param string) int {
   247  	v := req.FormValue(param)
   248  	if v == "" {
   249  		return 0
   250  	}
   251  	i, err := strconv.Atoi(v)
   252  	if err != nil {
   253  		panic(InvalidParameterError(param))
   254  	}
   255  	return i
   256  }
   257  
   258  // ServeJSONError sends a JSON error response to rw for the provided
   259  // error value.
   260  func ServeJSONError(rw http.ResponseWriter, err interface{}) {
   261  	code := 500
   262  	if i, ok := err.(httpCoder); ok {
   263  		code = i.HTTPCode()
   264  	}
   265  	msg := fmt.Sprint(err)
   266  	log.Printf("Sending error %v to client for: %v", code, msg)
   267  	ReturnJSONCode(rw, code, map[string]interface{}{
   268  		"error":     msg,
   269  		"errorType": http.StatusText(code),
   270  	})
   271  }
   272  
   273  // TODO: use a sync.Pool if/when Go 1.3 includes it and Camlistore depends on that.
   274  var freeBuf = make(chan *bytes.Buffer, 2)
   275  
   276  func getBuf() *bytes.Buffer {
   277  	select {
   278  	case b := <-freeBuf:
   279  		b.Reset()
   280  		return b
   281  	default:
   282  		return new(bytes.Buffer)
   283  	}
   284  }
   285  
   286  func putBuf(b *bytes.Buffer) {
   287  	select {
   288  	case freeBuf <- b:
   289  	default:
   290  	}
   291  }
   292  
   293  // DecodeJSON decodes the JSON in res.Body into dest and then closes
   294  // res.Body.
   295  // It defensively caps the JSON at 8 MB for now.
   296  func DecodeJSON(res *http.Response, dest interface{}) error {
   297  	defer CloseBody(res.Body)
   298  	buf := getBuf()
   299  	defer putBuf(buf)
   300  	if err := json.NewDecoder(io.TeeReader(io.LimitReader(res.Body, 8<<20), buf)).Decode(dest); err != nil {
   301  		return fmt.Errorf("httputil.DecodeJSON: %v, on input: %s", err, buf.Bytes())
   302  	}
   303  	return nil
   304  }
   305  
   306  // CloseBody should be used to close an http.Response.Body.
   307  //
   308  // It does a final little Read to maybe see EOF (to trigger connection
   309  // re-use) before calling Close.
   310  func CloseBody(rc io.ReadCloser) {
   311  	// Go 1.2 pseudo-bug: the NewDecoder(res.Body).Decode never
   312  	// sees an EOF, so we have to do this 0-byte copy here to
   313  	// force the http Transport to see its own EOF and recycle the
   314  	// connection. In Go 1.1 at least, the Close would cause it to
   315  	// read to EOF and recycle the connection, but in Go 1.2, a
   316  	// Close before EOF kills the underlying TCP connection.
   317  	//
   318  	// Will hopefully be fixed in Go 1.3, at least for bodies with
   319  	// Content-Length.  Or maybe Go 1.3's Close itself would look
   320  	// to see if we're at EOF even if it hasn't been Read.
   321  
   322  	// TODO: use a bytepool package somewhere for this byte?
   323  	// Justification for 3 byte reads: two for up to "\r\n" after
   324  	// a JSON/XML document, and then 1 to see EOF if we haven't yet.
   325  	buf := make([]byte, 1)
   326  	for i := 0; i < 3; i++ {
   327  		_, err := rc.Read(buf)
   328  		if err != nil {
   329  			break
   330  		}
   331  	}
   332  	rc.Close()
   333  }
   334  
   335  func IsWebsocketUpgrade(req *http.Request) bool {
   336  	return req.Method == "GET" && req.Header.Get("Upgrade") == "websocket"
   337  }