Updated Dependencies
This commit is contained in:
Generated
-4
@@ -14,10 +14,6 @@
|
||||
"ImportPath": "github.com/gorilla/context",
|
||||
"Rev": "215affda49addc4c8ef7e2534915df2c8c35c6cd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gorilla/handlers",
|
||||
"Rev": "0e84b7d810c16aed432217e330206be156bafae0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gorilla/mux",
|
||||
"Rev": "e444e69cbd2e2e3e0749a2f3c717cec491552bbf"
|
||||
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.0
|
||||
- 1.1
|
||||
- 1.2
|
||||
- 1.3
|
||||
- tip
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
gorilla/handlers
|
||||
================
|
||||
[](https://travis-ci.org/gorilla/handlers)
|
||||
|
||||
*Warning:* This package is a work in progress and the APIs are subject to change.
|
||||
Consider this a v0 project.
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
// Copyright 2013 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"compress/flate"
|
||||
"compress/gzip"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type compressResponseWriter struct {
|
||||
io.Writer
|
||||
http.ResponseWriter
|
||||
}
|
||||
|
||||
func (w *compressResponseWriter) Header() http.Header {
|
||||
return w.ResponseWriter.Header()
|
||||
}
|
||||
|
||||
func (w *compressResponseWriter) Write(b []byte) (int, error) {
|
||||
h := w.ResponseWriter.Header()
|
||||
if h.Get("Content-Type") == "" {
|
||||
h.Set("Content-Type", http.DetectContentType(b))
|
||||
}
|
||||
|
||||
return w.Writer.Write(b)
|
||||
}
|
||||
|
||||
func CompressHandler(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
L:
|
||||
for _, enc := range strings.Split(r.Header.Get("Accept-Encoding"), ",") {
|
||||
switch strings.TrimSpace(enc) {
|
||||
case "gzip":
|
||||
w.Header().Set("Content-Encoding", "gzip")
|
||||
w.Header().Add("Vary", "Accept-Encoding")
|
||||
|
||||
gw := gzip.NewWriter(w)
|
||||
defer gw.Close()
|
||||
|
||||
w = &compressResponseWriter{
|
||||
Writer: gw,
|
||||
ResponseWriter: w,
|
||||
}
|
||||
break L
|
||||
case "deflate":
|
||||
w.Header().Set("Content-Encoding", "deflate")
|
||||
w.Header().Add("Vary", "Accept-Encoding")
|
||||
|
||||
fw, _ := flate.NewWriter(w, flate.DefaultCompression)
|
||||
defer fw.Close()
|
||||
|
||||
w = &compressResponseWriter{
|
||||
Writer: fw,
|
||||
ResponseWriter: w,
|
||||
}
|
||||
break L
|
||||
}
|
||||
}
|
||||
|
||||
h.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
// Copyright 2013 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func compressedRequest(w *httptest.ResponseRecorder, compression string) {
|
||||
CompressHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
for i := 0; i < 1024; i++ {
|
||||
io.WriteString(w, "Gorilla!\n")
|
||||
}
|
||||
})).ServeHTTP(w, &http.Request{
|
||||
Method: "GET",
|
||||
Header: http.Header{
|
||||
"Accept-Encoding": []string{compression},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestCompressHandlerGzip(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
compressedRequest(w, "gzip")
|
||||
if w.HeaderMap.Get("Content-Encoding") != "gzip" {
|
||||
t.Fatalf("wrong content encoding, got %d want %d", w.HeaderMap.Get("Content-Encoding"), "gzip")
|
||||
}
|
||||
if w.HeaderMap.Get("Content-Type") != "text/plain; charset=utf-8" {
|
||||
t.Fatalf("wrong content type, got %s want %s", w.HeaderMap.Get("Content-Type"), "text/plain; charset=utf-8")
|
||||
}
|
||||
if w.Body.Len() != 72 {
|
||||
t.Fatalf("wrong len, got %d want %d", w.Body.Len(), 72)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompressHandlerDeflate(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
compressedRequest(w, "deflate")
|
||||
if w.HeaderMap.Get("Content-Encoding") != "deflate" {
|
||||
t.Fatalf("wrong content encoding, got %d want %d", w.HeaderMap.Get("Content-Encoding"), "deflate")
|
||||
}
|
||||
if w.HeaderMap.Get("Content-Type") != "text/plain; charset=utf-8" {
|
||||
t.Fatalf("wrong content type, got %s want %s", w.HeaderMap.Get("Content-Type"), "text/plain; charset=utf-8")
|
||||
}
|
||||
if w.Body.Len() != 54 {
|
||||
t.Fatalf("wrong len, got %d want %d", w.Body.Len(), 54)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompressHandlerGzipDeflate(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
compressedRequest(w, "gzip, deflate ")
|
||||
if w.HeaderMap.Get("Content-Encoding") != "gzip" {
|
||||
t.Fatalf("wrong content encoding, got %s want %s", w.HeaderMap.Get("Content-Encoding"), "gzip")
|
||||
}
|
||||
if w.HeaderMap.Get("Content-Type") != "text/plain; charset=utf-8" {
|
||||
t.Fatalf("wrong content type, got %s want %s", w.HeaderMap.Get("Content-Type"), "text/plain; charset=utf-8")
|
||||
}
|
||||
}
|
||||
-356
@@ -1,356 +0,0 @@
|
||||
// Copyright 2013 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package handlers is a collection of handlers for use with Go's net/http package.
|
||||
*/
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// MethodHandler is an http.Handler that dispatches to a handler whose key in the MethodHandler's
|
||||
// map matches the name of the HTTP request's method, eg: GET
|
||||
//
|
||||
// If the request's method is OPTIONS and OPTIONS is not a key in the map then the handler
|
||||
// responds with a status of 200 and sets the Allow header to a comma-separated list of
|
||||
// available methods.
|
||||
//
|
||||
// If the request's method doesn't match any of its keys the handler responds with
|
||||
// a status of 406, Method not allowed and sets the Allow header to a comma-separated list
|
||||
// of available methods.
|
||||
type MethodHandler map[string]http.Handler
|
||||
|
||||
func (h MethodHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
if handler, ok := h[req.Method]; ok {
|
||||
handler.ServeHTTP(w, req)
|
||||
} else {
|
||||
allow := []string{}
|
||||
for k := range h {
|
||||
allow = append(allow, k)
|
||||
}
|
||||
sort.Strings(allow)
|
||||
w.Header().Set("Allow", strings.Join(allow, ", "))
|
||||
if req.Method == "OPTIONS" {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
} else {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// loggingHandler is the http.Handler implementation for LoggingHandlerTo and its friends
|
||||
type loggingHandler struct {
|
||||
writer io.Writer
|
||||
handler http.Handler
|
||||
}
|
||||
|
||||
// combinedLoggingHandler is the http.Handler implementation for LoggingHandlerTo and its friends
|
||||
type combinedLoggingHandler struct {
|
||||
writer io.Writer
|
||||
handler http.Handler
|
||||
}
|
||||
|
||||
func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
t := time.Now()
|
||||
var logger loggingResponseWriter
|
||||
if _, ok := w.(http.Hijacker); ok {
|
||||
logger = &hijackLogger{responseLogger: responseLogger{w: w}}
|
||||
} else {
|
||||
logger = &responseLogger{w: w}
|
||||
}
|
||||
url := *req.URL
|
||||
h.handler.ServeHTTP(logger, req)
|
||||
writeLog(h.writer, req, url, t, logger.Status(), logger.Size())
|
||||
}
|
||||
|
||||
func (h combinedLoggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
t := time.Now()
|
||||
var logger loggingResponseWriter
|
||||
if _, ok := w.(http.Hijacker); ok {
|
||||
logger = &hijackLogger{responseLogger: responseLogger{w: w}}
|
||||
} else {
|
||||
logger = &responseLogger{w: w}
|
||||
}
|
||||
url := *req.URL
|
||||
h.handler.ServeHTTP(logger, req)
|
||||
writeCombinedLog(h.writer, req, url, t, logger.Status(), logger.Size())
|
||||
}
|
||||
|
||||
type loggingResponseWriter interface {
|
||||
http.ResponseWriter
|
||||
Status() int
|
||||
Size() int
|
||||
}
|
||||
|
||||
// responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP status
|
||||
// code and body size
|
||||
type responseLogger struct {
|
||||
w http.ResponseWriter
|
||||
status int
|
||||
size int
|
||||
}
|
||||
|
||||
func (l *responseLogger) Header() http.Header {
|
||||
return l.w.Header()
|
||||
}
|
||||
|
||||
func (l *responseLogger) Write(b []byte) (int, error) {
|
||||
if l.status == 0 {
|
||||
// The status will be StatusOK if WriteHeader has not been called yet
|
||||
l.status = http.StatusOK
|
||||
}
|
||||
size, err := l.w.Write(b)
|
||||
l.size += size
|
||||
return size, err
|
||||
}
|
||||
|
||||
func (l *responseLogger) WriteHeader(s int) {
|
||||
l.w.WriteHeader(s)
|
||||
l.status = s
|
||||
}
|
||||
|
||||
func (l *responseLogger) Status() int {
|
||||
return l.status
|
||||
}
|
||||
|
||||
func (l *responseLogger) Size() int {
|
||||
return l.size
|
||||
}
|
||||
|
||||
type hijackLogger struct {
|
||||
responseLogger
|
||||
}
|
||||
|
||||
func (l *hijackLogger) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
h := l.responseLogger.w.(http.Hijacker)
|
||||
conn, rw, err := h.Hijack()
|
||||
if err == nil && l.responseLogger.status == 0 {
|
||||
// The status will be StatusSwitchingProtocols if there was no error and WriteHeader has not been called yet
|
||||
l.responseLogger.status = http.StatusSwitchingProtocols
|
||||
}
|
||||
return conn, rw, err
|
||||
}
|
||||
|
||||
const lowerhex = "0123456789abcdef"
|
||||
|
||||
func appendQuoted(buf []byte, s string) []byte {
|
||||
var runeTmp [utf8.UTFMax]byte
|
||||
for width := 0; len(s) > 0; s = s[width:] {
|
||||
r := rune(s[0])
|
||||
width = 1
|
||||
if r >= utf8.RuneSelf {
|
||||
r, width = utf8.DecodeRuneInString(s)
|
||||
}
|
||||
if width == 1 && r == utf8.RuneError {
|
||||
buf = append(buf, `\x`...)
|
||||
buf = append(buf, lowerhex[s[0]>>4])
|
||||
buf = append(buf, lowerhex[s[0]&0xF])
|
||||
continue
|
||||
}
|
||||
if r == rune('"') || r == '\\' { // always backslashed
|
||||
buf = append(buf, '\\')
|
||||
buf = append(buf, byte(r))
|
||||
continue
|
||||
}
|
||||
if strconv.IsPrint(r) {
|
||||
n := utf8.EncodeRune(runeTmp[:], r)
|
||||
buf = append(buf, runeTmp[:n]...)
|
||||
continue
|
||||
}
|
||||
switch r {
|
||||
case '\a':
|
||||
buf = append(buf, `\a`...)
|
||||
case '\b':
|
||||
buf = append(buf, `\b`...)
|
||||
case '\f':
|
||||
buf = append(buf, `\f`...)
|
||||
case '\n':
|
||||
buf = append(buf, `\n`...)
|
||||
case '\r':
|
||||
buf = append(buf, `\r`...)
|
||||
case '\t':
|
||||
buf = append(buf, `\t`...)
|
||||
case '\v':
|
||||
buf = append(buf, `\v`...)
|
||||
default:
|
||||
switch {
|
||||
case r < ' ':
|
||||
buf = append(buf, `\x`...)
|
||||
buf = append(buf, lowerhex[s[0]>>4])
|
||||
buf = append(buf, lowerhex[s[0]&0xF])
|
||||
case r > utf8.MaxRune:
|
||||
r = 0xFFFD
|
||||
fallthrough
|
||||
case r < 0x10000:
|
||||
buf = append(buf, `\u`...)
|
||||
for s := 12; s >= 0; s -= 4 {
|
||||
buf = append(buf, lowerhex[r>>uint(s)&0xF])
|
||||
}
|
||||
default:
|
||||
buf = append(buf, `\U`...)
|
||||
for s := 28; s >= 0; s -= 4 {
|
||||
buf = append(buf, lowerhex[r>>uint(s)&0xF])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf
|
||||
|
||||
}
|
||||
|
||||
// buildCommonLogLine builds a log entry for req in Apache Common Log Format.
|
||||
// ts is the timestamp with which the entry should be logged.
|
||||
// status and size are used to provide the response HTTP status and size.
|
||||
func buildCommonLogLine(req *http.Request, url url.URL, ts time.Time, status int, size int) []byte {
|
||||
username := "-"
|
||||
if url.User != nil {
|
||||
if name := url.User.Username(); name != "" {
|
||||
username = name
|
||||
}
|
||||
}
|
||||
|
||||
host, _, err := net.SplitHostPort(req.RemoteAddr)
|
||||
|
||||
if err != nil {
|
||||
host = req.RemoteAddr
|
||||
}
|
||||
|
||||
uri := url.RequestURI()
|
||||
|
||||
buf := make([]byte, 0, 3*(len(host)+len(username)+len(req.Method)+len(uri)+len(req.Proto)+50)/2)
|
||||
buf = append(buf, host...)
|
||||
buf = append(buf, " - "...)
|
||||
buf = append(buf, username...)
|
||||
buf = append(buf, " ["...)
|
||||
buf = append(buf, ts.Format("02/Jan/2006:15:04:05 -0700")...)
|
||||
buf = append(buf, `] "`...)
|
||||
buf = append(buf, req.Method...)
|
||||
buf = append(buf, " "...)
|
||||
buf = appendQuoted(buf, uri)
|
||||
buf = append(buf, " "...)
|
||||
buf = append(buf, req.Proto...)
|
||||
buf = append(buf, `" `...)
|
||||
buf = append(buf, strconv.Itoa(status)...)
|
||||
buf = append(buf, " "...)
|
||||
buf = append(buf, strconv.Itoa(size)...)
|
||||
return buf
|
||||
}
|
||||
|
||||
// writeLog writes a log entry for req to w in Apache Common Log Format.
|
||||
// ts is the timestamp with which the entry should be logged.
|
||||
// status and size are used to provide the response HTTP status and size.
|
||||
func writeLog(w io.Writer, req *http.Request, url url.URL, ts time.Time, status, size int) {
|
||||
buf := buildCommonLogLine(req, url, ts, status, size)
|
||||
buf = append(buf, '\n')
|
||||
w.Write(buf)
|
||||
}
|
||||
|
||||
// writeCombinedLog writes a log entry for req to w in Apache Combined Log Format.
|
||||
// ts is the timestamp with which the entry should be logged.
|
||||
// status and size are used to provide the response HTTP status and size.
|
||||
func writeCombinedLog(w io.Writer, req *http.Request, url url.URL, ts time.Time, status, size int) {
|
||||
buf := buildCommonLogLine(req, url, ts, status, size)
|
||||
buf = append(buf, ` "`...)
|
||||
buf = appendQuoted(buf, req.Referer())
|
||||
buf = append(buf, `" "`...)
|
||||
buf = appendQuoted(buf, req.UserAgent())
|
||||
buf = append(buf, '"', '\n')
|
||||
w.Write(buf)
|
||||
}
|
||||
|
||||
// CombinedLoggingHandler return a http.Handler that wraps h and logs requests to out in
|
||||
// Apache Combined Log Format.
|
||||
//
|
||||
// See http://httpd.apache.org/docs/2.2/logs.html#combined for a description of this format.
|
||||
//
|
||||
// LoggingHandler always sets the ident field of the log to -
|
||||
func CombinedLoggingHandler(out io.Writer, h http.Handler) http.Handler {
|
||||
return combinedLoggingHandler{out, h}
|
||||
}
|
||||
|
||||
// LoggingHandler return a http.Handler that wraps h and logs requests to out in
|
||||
// Apache Common Log Format (CLF).
|
||||
//
|
||||
// See http://httpd.apache.org/docs/2.2/logs.html#common for a description of this format.
|
||||
//
|
||||
// LoggingHandler always sets the ident field of the log to -
|
||||
func LoggingHandler(out io.Writer, h http.Handler) http.Handler {
|
||||
return loggingHandler{out, h}
|
||||
}
|
||||
|
||||
// isContentType validates the Content-Type header
|
||||
// is contentType. That is, its type and subtype match.
|
||||
func isContentType(h http.Header, contentType string) bool {
|
||||
ct := h.Get("Content-Type")
|
||||
if i := strings.IndexRune(ct, ';'); i != -1 {
|
||||
ct = ct[0:i]
|
||||
}
|
||||
return ct == contentType
|
||||
}
|
||||
|
||||
// ContentTypeHandler wraps and returns a http.Handler, validating the request content type
|
||||
// is acompatible with the contentTypes list.
|
||||
// It writes a HTTP 415 error if that fails.
|
||||
//
|
||||
// Only PUT, POST, and PATCH requests are considered.
|
||||
func ContentTypeHandler(h http.Handler, contentTypes ...string) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if !(r.Method == "PUT" || r.Method == "POST" || r.Method == "PATCH") {
|
||||
h.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
for _, ct := range contentTypes {
|
||||
if isContentType(r.Header, ct) {
|
||||
h.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
http.Error(w, fmt.Sprintf("Unsupported content type %q; expected one of %q", r.Header.Get("Content-Type"), contentTypes), http.StatusUnsupportedMediaType)
|
||||
})
|
||||
}
|
||||
|
||||
const (
|
||||
// HTTPMethodOverrideHeader is a commonly used
|
||||
// http header to override a request method.
|
||||
HTTPMethodOverrideHeader = "X-HTTP-Method-Override"
|
||||
// HTTPMethodOverrideFormKey is a commonly used
|
||||
// HTML form key to override a request method.
|
||||
HTTPMethodOverrideFormKey = "_method"
|
||||
)
|
||||
|
||||
// HTTPMethodOverrideHandler wraps and returns a http.Handler which checks for the X-HTTP-Method-Override header
|
||||
// or the _method form key, and overrides (if valid) request.Method with its value.
|
||||
//
|
||||
// This is especially useful for http clients that don't support many http verbs.
|
||||
// It isn't secure to override e.g a GET to a POST, so only POST requests are considered.
|
||||
// Likewise, the override method can only be a "write" method: PUT, PATCH or DELETE.
|
||||
//
|
||||
// Form method takes precedence over header method.
|
||||
func HTTPMethodOverrideHandler(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "POST" {
|
||||
om := r.FormValue(HTTPMethodOverrideFormKey)
|
||||
if om == "" {
|
||||
om = r.Header.Get(HTTPMethodOverrideHeader)
|
||||
}
|
||||
if om == "PUT" || om == "PATCH" || om == "DELETE" {
|
||||
r.Method = om
|
||||
}
|
||||
}
|
||||
h.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
-305
@@ -1,305 +0,0 @@
|
||||
// Copyright 2013 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
ok = "ok\n"
|
||||
notAllowed = "Method not allowed\n"
|
||||
)
|
||||
|
||||
var okHandler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
w.Write([]byte(ok))
|
||||
})
|
||||
|
||||
func newRequest(method, url string) *http.Request {
|
||||
req, err := http.NewRequest(method, url, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
func TestMethodHandler(t *testing.T) {
|
||||
tests := []struct {
|
||||
req *http.Request
|
||||
handler http.Handler
|
||||
code int
|
||||
allow string // Contents of the Allow header
|
||||
body string
|
||||
}{
|
||||
// No handlers
|
||||
{newRequest("GET", "/foo"), MethodHandler{}, http.StatusMethodNotAllowed, "", notAllowed},
|
||||
{newRequest("OPTIONS", "/foo"), MethodHandler{}, http.StatusOK, "", ""},
|
||||
|
||||
// A single handler
|
||||
{newRequest("GET", "/foo"), MethodHandler{"GET": okHandler}, http.StatusOK, "", ok},
|
||||
{newRequest("POST", "/foo"), MethodHandler{"GET": okHandler}, http.StatusMethodNotAllowed, "GET", notAllowed},
|
||||
|
||||
// Multiple handlers
|
||||
{newRequest("GET", "/foo"), MethodHandler{"GET": okHandler, "POST": okHandler}, http.StatusOK, "", ok},
|
||||
{newRequest("POST", "/foo"), MethodHandler{"GET": okHandler, "POST": okHandler}, http.StatusOK, "", ok},
|
||||
{newRequest("DELETE", "/foo"), MethodHandler{"GET": okHandler, "POST": okHandler}, http.StatusMethodNotAllowed, "GET, POST", notAllowed},
|
||||
{newRequest("OPTIONS", "/foo"), MethodHandler{"GET": okHandler, "POST": okHandler}, http.StatusOK, "GET, POST", ""},
|
||||
|
||||
// Override OPTIONS
|
||||
{newRequest("OPTIONS", "/foo"), MethodHandler{"OPTIONS": okHandler}, http.StatusOK, "", ok},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
rec := httptest.NewRecorder()
|
||||
test.handler.ServeHTTP(rec, test.req)
|
||||
if rec.Code != test.code {
|
||||
t.Fatalf("%d: wrong code, got %d want %d", i, rec.Code, test.code)
|
||||
}
|
||||
if allow := rec.HeaderMap.Get("Allow"); allow != test.allow {
|
||||
t.Fatalf("%d: wrong Allow, got %s want %s", i, allow, test.allow)
|
||||
}
|
||||
if body := rec.Body.String(); body != test.body {
|
||||
t.Fatalf("%d: wrong body, got %q want %q", i, body, test.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteLog(t *testing.T) {
|
||||
loc, err := time.LoadLocation("Europe/Warsaw")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ts := time.Date(1983, 05, 26, 3, 30, 45, 0, loc)
|
||||
|
||||
// A typical request with an OK response
|
||||
req := newRequest("GET", "http://example.com")
|
||||
req.RemoteAddr = "192.168.100.5"
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
writeLog(buf, req, *req.URL, ts, http.StatusOK, 100)
|
||||
log := buf.String()
|
||||
|
||||
expected := "192.168.100.5 - - [26/May/1983:03:30:45 +0200] \"GET / HTTP/1.1\" 200 100\n"
|
||||
if log != expected {
|
||||
t.Fatalf("wrong log, got %q want %q", log, expected)
|
||||
}
|
||||
|
||||
// Request with an unauthorized user
|
||||
req = newRequest("GET", "http://example.com")
|
||||
req.RemoteAddr = "192.168.100.5"
|
||||
req.URL.User = url.User("kamil")
|
||||
|
||||
buf.Reset()
|
||||
writeLog(buf, req, *req.URL, ts, http.StatusUnauthorized, 500)
|
||||
log = buf.String()
|
||||
|
||||
expected = "192.168.100.5 - kamil [26/May/1983:03:30:45 +0200] \"GET / HTTP/1.1\" 401 500\n"
|
||||
if log != expected {
|
||||
t.Fatalf("wrong log, got %q want %q", log, expected)
|
||||
}
|
||||
|
||||
// Request with url encoded parameters
|
||||
req = newRequest("GET", "http://example.com/test?abc=hello%20world&a=b%3F")
|
||||
req.RemoteAddr = "192.168.100.5"
|
||||
|
||||
buf.Reset()
|
||||
writeLog(buf, req, *req.URL, ts, http.StatusOK, 100)
|
||||
log = buf.String()
|
||||
|
||||
expected = "192.168.100.5 - - [26/May/1983:03:30:45 +0200] \"GET /test?abc=hello%20world&a=b%3F HTTP/1.1\" 200 100\n"
|
||||
if log != expected {
|
||||
t.Fatalf("wrong log, got %q want %q", log, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteCombinedLog(t *testing.T) {
|
||||
loc, err := time.LoadLocation("Europe/Warsaw")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ts := time.Date(1983, 05, 26, 3, 30, 45, 0, loc)
|
||||
|
||||
// A typical request with an OK response
|
||||
req := newRequest("GET", "http://example.com")
|
||||
req.RemoteAddr = "192.168.100.5"
|
||||
req.Header.Set("Referer", "http://example.com")
|
||||
req.Header.Set(
|
||||
"User-Agent",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.33 "+
|
||||
"(KHTML, like Gecko) Chrome/27.0.1430.0 Safari/537.33",
|
||||
)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
writeCombinedLog(buf, req, *req.URL, ts, http.StatusOK, 100)
|
||||
log := buf.String()
|
||||
|
||||
expected := "192.168.100.5 - - [26/May/1983:03:30:45 +0200] \"GET / HTTP/1.1\" 200 100 \"http://example.com\" " +
|
||||
"\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) " +
|
||||
"AppleWebKit/537.33 (KHTML, like Gecko) Chrome/27.0.1430.0 Safari/537.33\"\n"
|
||||
if log != expected {
|
||||
t.Fatalf("wrong log, got %q want %q", log, expected)
|
||||
}
|
||||
|
||||
// Request with an unauthorized user
|
||||
req.URL.User = url.User("kamil")
|
||||
|
||||
buf.Reset()
|
||||
writeCombinedLog(buf, req, *req.URL, ts, http.StatusUnauthorized, 500)
|
||||
log = buf.String()
|
||||
|
||||
expected = "192.168.100.5 - kamil [26/May/1983:03:30:45 +0200] \"GET / HTTP/1.1\" 401 500 \"http://example.com\" " +
|
||||
"\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) " +
|
||||
"AppleWebKit/537.33 (KHTML, like Gecko) Chrome/27.0.1430.0 Safari/537.33\"\n"
|
||||
if log != expected {
|
||||
t.Fatalf("wrong log, got %q want %q", log, expected)
|
||||
}
|
||||
|
||||
// Test with remote ipv6 address
|
||||
req.RemoteAddr = "::1"
|
||||
|
||||
buf.Reset()
|
||||
writeCombinedLog(buf, req, *req.URL, ts, http.StatusOK, 100)
|
||||
log = buf.String()
|
||||
|
||||
expected = "::1 - kamil [26/May/1983:03:30:45 +0200] \"GET / HTTP/1.1\" 200 100 \"http://example.com\" " +
|
||||
"\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) " +
|
||||
"AppleWebKit/537.33 (KHTML, like Gecko) Chrome/27.0.1430.0 Safari/537.33\"\n"
|
||||
if log != expected {
|
||||
t.Fatalf("wrong log, got %q want %q", log, expected)
|
||||
}
|
||||
|
||||
// Test remote ipv6 addr, with port
|
||||
req.RemoteAddr = net.JoinHostPort("::1", "65000")
|
||||
|
||||
buf.Reset()
|
||||
writeCombinedLog(buf, req, *req.URL, ts, http.StatusOK, 100)
|
||||
log = buf.String()
|
||||
|
||||
expected = "::1 - kamil [26/May/1983:03:30:45 +0200] \"GET / HTTP/1.1\" 200 100 \"http://example.com\" " +
|
||||
"\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) " +
|
||||
"AppleWebKit/537.33 (KHTML, like Gecko) Chrome/27.0.1430.0 Safari/537.33\"\n"
|
||||
if log != expected {
|
||||
t.Fatalf("wrong log, got %q want %q", log, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogPathRewrites(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
req.URL.Path = "/" // simulate http.StripPrefix and friends
|
||||
w.WriteHeader(200)
|
||||
})
|
||||
logger := LoggingHandler(&buf, handler)
|
||||
|
||||
logger.ServeHTTP(httptest.NewRecorder(), newRequest("GET", "/subdir/asdf"))
|
||||
|
||||
if !strings.Contains(buf.String(), "GET /subdir/asdf HTTP") {
|
||||
t.Fatalf("Got log %#v, wanted substring %#v", buf.String(), "GET /subdir/asdf HTTP")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkWriteLog(b *testing.B) {
|
||||
loc, err := time.LoadLocation("Europe/Warsaw")
|
||||
if err != nil {
|
||||
b.Fatalf(err.Error())
|
||||
}
|
||||
ts := time.Date(1983, 05, 26, 3, 30, 45, 0, loc)
|
||||
|
||||
req := newRequest("GET", "http://example.com")
|
||||
req.RemoteAddr = "192.168.100.5"
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
for i := 0; i < b.N; i++ {
|
||||
buf.Reset()
|
||||
writeLog(buf, req, *req.URL, ts, http.StatusUnauthorized, 500)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContentTypeHandler(t *testing.T) {
|
||||
tests := []struct {
|
||||
Method string
|
||||
AllowContentTypes []string
|
||||
ContentType string
|
||||
Code int
|
||||
}{
|
||||
{"POST", []string{"application/json"}, "application/json", http.StatusOK},
|
||||
{"POST", []string{"application/json", "application/xml"}, "application/json", http.StatusOK},
|
||||
{"POST", []string{"application/json"}, "application/json; charset=utf-8", http.StatusOK},
|
||||
{"POST", []string{"application/json"}, "application/json+xxx", http.StatusUnsupportedMediaType},
|
||||
{"POST", []string{"application/json"}, "text/plain", http.StatusUnsupportedMediaType},
|
||||
{"GET", []string{"application/json"}, "", http.StatusOK},
|
||||
{"GET", []string{}, "", http.StatusOK},
|
||||
}
|
||||
for _, test := range tests {
|
||||
r, err := http.NewRequest(test.Method, "/", nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
h := ContentTypeHandler(okHandler, test.AllowContentTypes...)
|
||||
r.Header.Set("Content-Type", test.ContentType)
|
||||
w := httptest.NewRecorder()
|
||||
h.ServeHTTP(w, r)
|
||||
if w.Code != test.Code {
|
||||
t.Errorf("expected %d, got %d", test.Code, w.Code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPMethodOverride(t *testing.T) {
|
||||
var tests = []struct {
|
||||
Method string
|
||||
OverrideMethod string
|
||||
ExpectedMethod string
|
||||
}{
|
||||
{"POST", "PUT", "PUT"},
|
||||
{"POST", "PATCH", "PATCH"},
|
||||
{"POST", "DELETE", "DELETE"},
|
||||
{"PUT", "DELETE", "PUT"},
|
||||
{"GET", "GET", "GET"},
|
||||
{"HEAD", "HEAD", "HEAD"},
|
||||
{"GET", "PUT", "GET"},
|
||||
{"HEAD", "DELETE", "HEAD"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
h := HTTPMethodOverrideHandler(okHandler)
|
||||
reqs := make([]*http.Request, 0, 2)
|
||||
|
||||
rHeader, err := http.NewRequest(test.Method, "/", nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
rHeader.Header.Set(HTTPMethodOverrideHeader, test.OverrideMethod)
|
||||
reqs = append(reqs, rHeader)
|
||||
|
||||
f := url.Values{HTTPMethodOverrideFormKey: []string{test.OverrideMethod}}
|
||||
rForm, err := http.NewRequest(test.Method, "/", strings.NewReader(f.Encode()))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
rForm.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
reqs = append(reqs, rForm)
|
||||
|
||||
for _, r := range reqs {
|
||||
w := httptest.NewRecorder()
|
||||
h.ServeHTTP(w, r)
|
||||
if r.Method != test.ExpectedMethod {
|
||||
t.Errorf("Expected %s, got %s", test.ExpectedMethod, r.Method)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user