diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index f11bd7f..c60cf2a 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -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" diff --git a/Godeps/_workspace/src/github.com/gorilla/handlers/.travis.yml b/Godeps/_workspace/src/github.com/gorilla/handlers/.travis.yml deleted file mode 100644 index 825dc3f..0000000 --- a/Godeps/_workspace/src/github.com/gorilla/handlers/.travis.yml +++ /dev/null @@ -1,8 +0,0 @@ -language: go - -go: - - 1.0 - - 1.1 - - 1.2 - - 1.3 - - tip diff --git a/Godeps/_workspace/src/github.com/gorilla/handlers/README.md b/Godeps/_workspace/src/github.com/gorilla/handlers/README.md deleted file mode 100644 index 9be9370..0000000 --- a/Godeps/_workspace/src/github.com/gorilla/handlers/README.md +++ /dev/null @@ -1,6 +0,0 @@ -gorilla/handlers -================ -[![Build Status](https://travis-ci.org/gorilla/handlers.png?branch=master)](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. diff --git a/Godeps/_workspace/src/github.com/gorilla/handlers/compress.go b/Godeps/_workspace/src/github.com/gorilla/handlers/compress.go deleted file mode 100644 index 81202bb..0000000 --- a/Godeps/_workspace/src/github.com/gorilla/handlers/compress.go +++ /dev/null @@ -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) - }) -} diff --git a/Godeps/_workspace/src/github.com/gorilla/handlers/compress_test.go b/Godeps/_workspace/src/github.com/gorilla/handlers/compress_test.go deleted file mode 100644 index 2661b39..0000000 --- a/Godeps/_workspace/src/github.com/gorilla/handlers/compress_test.go +++ /dev/null @@ -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") - } -} diff --git a/Godeps/_workspace/src/github.com/gorilla/handlers/handlers.go b/Godeps/_workspace/src/github.com/gorilla/handlers/handlers.go deleted file mode 100644 index 23a738d..0000000 --- a/Godeps/_workspace/src/github.com/gorilla/handlers/handlers.go +++ /dev/null @@ -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) - }) -} diff --git a/Godeps/_workspace/src/github.com/gorilla/handlers/handlers_test.go b/Godeps/_workspace/src/github.com/gorilla/handlers/handlers_test.go deleted file mode 100644 index 94eeb03..0000000 --- a/Godeps/_workspace/src/github.com/gorilla/handlers/handlers_test.go +++ /dev/null @@ -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) - } - } - } -}