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 }