Loading Gopkg.lock +3 −3 Original line number Diff line number Diff line Loading @@ -250,12 +250,12 @@ version = "v1.1.1" [[projects]] digest = "1:a1a1522a8c1fad5675ce8ec9de96f30b898df7c8c229928c35dbd8cf277e6d62" digest = "1:66d63ec7b6035984dced87ad37066e5f4ad277c60234a9d9925537248e983200" name = "github.com/gorilla/handlers" packages = ["."] pruneopts = "" revision = "90663712d74cb411cbef281bc1e08c19d1a76145" version = "v1.3.0" revision = "7e0847f9db758cdebd26c149d0ae9d5d0b9c98ce" version = "v1.4.0" [[projects]] digest = "1:c2c8666b4836c81a1d247bdf21c6a6fc1ab586538ab56f74437c2e0df5c375e1" Loading Gopkg.toml +1 −1 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ [[constraint]] name = "github.com/gorilla/handlers" version = "~1.3.0" version = "~1.4.0" [[constraint]] name = "github.com/gorilla/mux" Loading vendor/github.com/gorilla/handlers/cors.go +20 −9 Original line number Diff line number Diff line Loading @@ -48,7 +48,10 @@ const ( func (ch *cors) ServeHTTP(w http.ResponseWriter, r *http.Request) { origin := r.Header.Get(corsOriginHeader) if !ch.isOriginAllowed(origin) { if r.Method != corsOptionMethod || ch.ignoreOptions { ch.h.ServeHTTP(w, r) } return } Loading Loading @@ -111,15 +114,19 @@ func (ch *cors) ServeHTTP(w http.ResponseWriter, r *http.Request) { } returnOrigin := origin if ch.allowedOriginValidator == nil && len(ch.allowedOrigins) == 0 { returnOrigin = "*" } else { for _, o := range ch.allowedOrigins { // A configuration of * is different than explicitly setting an allowed // origin. Returning arbitrary origin headers an an access control allow // origin. Returning arbitrary origin headers in an access control allow // origin header is unsafe and is not required by any use case. if o == corsOriginMatchAll { returnOrigin = "*" break } } } w.Header().Set(corsAllowOriginHeader, returnOrigin) if r.Method == corsOptionMethod { Loading Loading @@ -159,7 +166,7 @@ func parseCORSOptions(opts ...CORSOption) *cors { ch := &cors{ allowedMethods: defaultCorsMethods, allowedHeaders: defaultCorsHeaders, allowedOrigins: []string{corsOriginMatchAll}, allowedOrigins: []string{}, } for _, option := range opts { Loading Loading @@ -307,6 +314,10 @@ func (ch *cors) isOriginAllowed(origin string) bool { return ch.allowedOriginValidator(origin) } if len(ch.allowedOrigins) == 0 { return true } for _, allowedOrigin := range ch.allowedOrigins { if allowedOrigin == origin || allowedOrigin == corsOriginMatchAll { return true Loading vendor/github.com/gorilla/handlers/cors_test.go +40 −20 Original line number Diff line number Diff line Loading @@ -122,6 +122,24 @@ func TestCORSHandlerOptionsRequestMustNotBePassedToNextHandler(t *testing.T) { } } func TestCORSHandlerOptionsRequestMustNotBePassedToNextHandlerWhenOriginNotAllowed(t *testing.T) { r := newRequest("OPTIONS", "http://www.example.com/") r.Header.Set("Origin", r.URL.String()) r.Header.Set(corsRequestMethodHeader, "GET") rr := httptest.NewRecorder() testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { t.Fatal("Options request must not be passed to next handler") }) CORS(AllowedOrigins([]string{}))(testHandler).ServeHTTP(rr, r) if status := rr.Code; status != http.StatusOK { t.Fatalf("bad status: got %v want %v", status, http.StatusOK) } } func TestCORSHandlerAllowedMethodForPreflight(t *testing.T) { r := newRequest("OPTIONS", "http://www.example.com/") r.Header.Set("Origin", r.URL.String()) Loading Loading @@ -313,7 +331,7 @@ func TestCORSWithMultipleHandlers(t *testing.T) { } } func TestCORSHandlerWithCustomValidator(t *testing.T) { func TestCORSOriginValidatorWithImplicitStar(t *testing.T) { r := newRequest("GET", "http://a.example.com") r.Header.Set("Origin", r.URL.String()) rr := httptest.NewRecorder() Loading @@ -327,32 +345,20 @@ func TestCORSHandlerWithCustomValidator(t *testing.T) { return false } // Specially craft a CORS object. handleFunc := func(h http.Handler) http.Handler { c := &cors{ allowedMethods: defaultCorsMethods, allowedHeaders: defaultCorsHeaders, allowedOrigins: []string{"http://a.example.com"}, h: h, } AllowedOriginValidator(originValidator)(c) return c } handleFunc(testHandler).ServeHTTP(rr, r) CORS(AllowedOriginValidator(originValidator))(testHandler).ServeHTTP(rr, r) header := rr.HeaderMap.Get(corsAllowOriginHeader) if header != r.URL.String() { t.Fatalf("bad header: expected %s to be %s, got %s.", corsAllowOriginHeader, r.URL.String(), header) } } func TestCORSAllowStar(t *testing.T) { func TestCORSOriginValidatorWithExplicitStar(t *testing.T) { r := newRequest("GET", "http://a.example.com") r.Header.Set("Origin", r.URL.String()) rr := httptest.NewRecorder() testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) originValidator := func(origin string) bool { if strings.HasSuffix(origin, ".example.com") { return true Loading @@ -360,12 +366,26 @@ func TestCORSAllowStar(t *testing.T) { return false } CORS(AllowedOriginValidator(originValidator))(testHandler).ServeHTTP(rr, r) CORS( AllowedOriginValidator(originValidator), AllowedOrigins([]string{"*"}), )(testHandler).ServeHTTP(rr, r) header := rr.HeaderMap.Get(corsAllowOriginHeader) // Because * is the default CORS policy (which is safe), we should be // expect a * returned here as the Access Control Allow Origin header if header != "*" { t.Fatalf("bad header: expected %s to be %s, got %s.", corsAllowOriginHeader, r.URL.String(), header) t.Fatalf("bad header: expected %s to be %s, got %s.", corsAllowOriginHeader, "*", header) } } func TestCORSAllowStar(t *testing.T) { r := newRequest("GET", "http://a.example.com") r.Header.Set("Origin", r.URL.String()) rr := httptest.NewRecorder() testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) CORS()(testHandler).ServeHTTP(rr, r) header := rr.HeaderMap.Get(corsAllowOriginHeader) if header != "*" { t.Fatalf("bad header: expected %s to be %s, got %s.", corsAllowOriginHeader, "*", header) } } vendor/github.com/gorilla/handlers/handlers.go +0 −225 Original line number Diff line number Diff line Loading @@ -7,15 +7,10 @@ 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 Loading Loading @@ -48,59 +43,6 @@ func (h MethodHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { } } // 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() logger := makeLogger(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() logger := makeLogger(w) url := *req.URL h.handler.ServeHTTP(logger, req) writeCombinedLog(h.writer, req, url, t, logger.Status(), logger.Size()) } func makeLogger(w http.ResponseWriter) loggingResponseWriter { var logger loggingResponseWriter = &responseLogger{w: w, status: http.StatusOK} if _, ok := w.(http.Hijacker); ok { logger = &hijackLogger{responseLogger{w: w, status: http.StatusOK}} } h, ok1 := logger.(http.Hijacker) c, ok2 := w.(http.CloseNotifier) if ok1 && ok2 { return hijackCloseNotifier{logger, h, c} } if ok2 { return &closeNotifyWriter{logger, c} } return logger } type commonLoggingResponseWriter interface { http.ResponseWriter http.Flusher Status() int Size() int } // responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP // status code and body size type responseLogger struct { Loading Loading @@ -165,173 +107,6 @@ type hijackCloseNotifier struct { http.CloseNotifier } 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 := req.RequestURI // Requests using the CONNECT method over HTTP/2.0 must use // the authority field (aka r.Host) to identify the target. // Refer: https://httpwg.github.io/specs/rfc7540.html#CONNECT if req.ProtoMajor == 2 && req.Method == "CONNECT" { uri = req.Host } if uri == "" { 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 - // // Example: // // r := mux.NewRouter() // r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { // w.Write([]byte("This is a catch-all route")) // }) // loggedRouter := handlers.LoggingHandler(os.Stdout, r) // http.ListenAndServe(":1123", loggedRouter) // func LoggingHandler(out io.Writer, h http.Handler) http.Handler { return loggingHandler{out, h} } // isContentType validates the Content-Type header matches the supplied // contentType. That is, its type and subtype match. func isContentType(h http.Header, contentType string) bool { Loading Loading
Gopkg.lock +3 −3 Original line number Diff line number Diff line Loading @@ -250,12 +250,12 @@ version = "v1.1.1" [[projects]] digest = "1:a1a1522a8c1fad5675ce8ec9de96f30b898df7c8c229928c35dbd8cf277e6d62" digest = "1:66d63ec7b6035984dced87ad37066e5f4ad277c60234a9d9925537248e983200" name = "github.com/gorilla/handlers" packages = ["."] pruneopts = "" revision = "90663712d74cb411cbef281bc1e08c19d1a76145" version = "v1.3.0" revision = "7e0847f9db758cdebd26c149d0ae9d5d0b9c98ce" version = "v1.4.0" [[projects]] digest = "1:c2c8666b4836c81a1d247bdf21c6a6fc1ab586538ab56f74437c2e0df5c375e1" Loading
Gopkg.toml +1 −1 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ [[constraint]] name = "github.com/gorilla/handlers" version = "~1.3.0" version = "~1.4.0" [[constraint]] name = "github.com/gorilla/mux" Loading
vendor/github.com/gorilla/handlers/cors.go +20 −9 Original line number Diff line number Diff line Loading @@ -48,7 +48,10 @@ const ( func (ch *cors) ServeHTTP(w http.ResponseWriter, r *http.Request) { origin := r.Header.Get(corsOriginHeader) if !ch.isOriginAllowed(origin) { if r.Method != corsOptionMethod || ch.ignoreOptions { ch.h.ServeHTTP(w, r) } return } Loading Loading @@ -111,15 +114,19 @@ func (ch *cors) ServeHTTP(w http.ResponseWriter, r *http.Request) { } returnOrigin := origin if ch.allowedOriginValidator == nil && len(ch.allowedOrigins) == 0 { returnOrigin = "*" } else { for _, o := range ch.allowedOrigins { // A configuration of * is different than explicitly setting an allowed // origin. Returning arbitrary origin headers an an access control allow // origin. Returning arbitrary origin headers in an access control allow // origin header is unsafe and is not required by any use case. if o == corsOriginMatchAll { returnOrigin = "*" break } } } w.Header().Set(corsAllowOriginHeader, returnOrigin) if r.Method == corsOptionMethod { Loading Loading @@ -159,7 +166,7 @@ func parseCORSOptions(opts ...CORSOption) *cors { ch := &cors{ allowedMethods: defaultCorsMethods, allowedHeaders: defaultCorsHeaders, allowedOrigins: []string{corsOriginMatchAll}, allowedOrigins: []string{}, } for _, option := range opts { Loading Loading @@ -307,6 +314,10 @@ func (ch *cors) isOriginAllowed(origin string) bool { return ch.allowedOriginValidator(origin) } if len(ch.allowedOrigins) == 0 { return true } for _, allowedOrigin := range ch.allowedOrigins { if allowedOrigin == origin || allowedOrigin == corsOriginMatchAll { return true Loading
vendor/github.com/gorilla/handlers/cors_test.go +40 −20 Original line number Diff line number Diff line Loading @@ -122,6 +122,24 @@ func TestCORSHandlerOptionsRequestMustNotBePassedToNextHandler(t *testing.T) { } } func TestCORSHandlerOptionsRequestMustNotBePassedToNextHandlerWhenOriginNotAllowed(t *testing.T) { r := newRequest("OPTIONS", "http://www.example.com/") r.Header.Set("Origin", r.URL.String()) r.Header.Set(corsRequestMethodHeader, "GET") rr := httptest.NewRecorder() testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { t.Fatal("Options request must not be passed to next handler") }) CORS(AllowedOrigins([]string{}))(testHandler).ServeHTTP(rr, r) if status := rr.Code; status != http.StatusOK { t.Fatalf("bad status: got %v want %v", status, http.StatusOK) } } func TestCORSHandlerAllowedMethodForPreflight(t *testing.T) { r := newRequest("OPTIONS", "http://www.example.com/") r.Header.Set("Origin", r.URL.String()) Loading Loading @@ -313,7 +331,7 @@ func TestCORSWithMultipleHandlers(t *testing.T) { } } func TestCORSHandlerWithCustomValidator(t *testing.T) { func TestCORSOriginValidatorWithImplicitStar(t *testing.T) { r := newRequest("GET", "http://a.example.com") r.Header.Set("Origin", r.URL.String()) rr := httptest.NewRecorder() Loading @@ -327,32 +345,20 @@ func TestCORSHandlerWithCustomValidator(t *testing.T) { return false } // Specially craft a CORS object. handleFunc := func(h http.Handler) http.Handler { c := &cors{ allowedMethods: defaultCorsMethods, allowedHeaders: defaultCorsHeaders, allowedOrigins: []string{"http://a.example.com"}, h: h, } AllowedOriginValidator(originValidator)(c) return c } handleFunc(testHandler).ServeHTTP(rr, r) CORS(AllowedOriginValidator(originValidator))(testHandler).ServeHTTP(rr, r) header := rr.HeaderMap.Get(corsAllowOriginHeader) if header != r.URL.String() { t.Fatalf("bad header: expected %s to be %s, got %s.", corsAllowOriginHeader, r.URL.String(), header) } } func TestCORSAllowStar(t *testing.T) { func TestCORSOriginValidatorWithExplicitStar(t *testing.T) { r := newRequest("GET", "http://a.example.com") r.Header.Set("Origin", r.URL.String()) rr := httptest.NewRecorder() testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) originValidator := func(origin string) bool { if strings.HasSuffix(origin, ".example.com") { return true Loading @@ -360,12 +366,26 @@ func TestCORSAllowStar(t *testing.T) { return false } CORS(AllowedOriginValidator(originValidator))(testHandler).ServeHTTP(rr, r) CORS( AllowedOriginValidator(originValidator), AllowedOrigins([]string{"*"}), )(testHandler).ServeHTTP(rr, r) header := rr.HeaderMap.Get(corsAllowOriginHeader) // Because * is the default CORS policy (which is safe), we should be // expect a * returned here as the Access Control Allow Origin header if header != "*" { t.Fatalf("bad header: expected %s to be %s, got %s.", corsAllowOriginHeader, r.URL.String(), header) t.Fatalf("bad header: expected %s to be %s, got %s.", corsAllowOriginHeader, "*", header) } } func TestCORSAllowStar(t *testing.T) { r := newRequest("GET", "http://a.example.com") r.Header.Set("Origin", r.URL.String()) rr := httptest.NewRecorder() testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) CORS()(testHandler).ServeHTTP(rr, r) header := rr.HeaderMap.Get(corsAllowOriginHeader) if header != "*" { t.Fatalf("bad header: expected %s to be %s, got %s.", corsAllowOriginHeader, "*", header) } }
vendor/github.com/gorilla/handlers/handlers.go +0 −225 Original line number Diff line number Diff line Loading @@ -7,15 +7,10 @@ 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 Loading Loading @@ -48,59 +43,6 @@ func (h MethodHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { } } // 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() logger := makeLogger(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() logger := makeLogger(w) url := *req.URL h.handler.ServeHTTP(logger, req) writeCombinedLog(h.writer, req, url, t, logger.Status(), logger.Size()) } func makeLogger(w http.ResponseWriter) loggingResponseWriter { var logger loggingResponseWriter = &responseLogger{w: w, status: http.StatusOK} if _, ok := w.(http.Hijacker); ok { logger = &hijackLogger{responseLogger{w: w, status: http.StatusOK}} } h, ok1 := logger.(http.Hijacker) c, ok2 := w.(http.CloseNotifier) if ok1 && ok2 { return hijackCloseNotifier{logger, h, c} } if ok2 { return &closeNotifyWriter{logger, c} } return logger } type commonLoggingResponseWriter interface { http.ResponseWriter http.Flusher Status() int Size() int } // responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP // status code and body size type responseLogger struct { Loading Loading @@ -165,173 +107,6 @@ type hijackCloseNotifier struct { http.CloseNotifier } 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 := req.RequestURI // Requests using the CONNECT method over HTTP/2.0 must use // the authority field (aka r.Host) to identify the target. // Refer: https://httpwg.github.io/specs/rfc7540.html#CONNECT if req.ProtoMajor == 2 && req.Method == "CONNECT" { uri = req.Host } if uri == "" { 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 - // // Example: // // r := mux.NewRouter() // r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { // w.Write([]byte("This is a catch-all route")) // }) // loggedRouter := handlers.LoggingHandler(os.Stdout, r) // http.ListenAndServe(":1123", loggedRouter) // func LoggingHandler(out io.Writer, h http.Handler) http.Handler { return loggingHandler{out, h} } // isContentType validates the Content-Type header matches the supplied // contentType. That is, its type and subtype match. func isContentType(h http.Header, contentType string) bool { Loading