2019-10-08 00:05:46 +00:00
|
|
|
package http
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"errors"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
"path"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2020-04-03 17:39:20 +00:00
|
|
|
kithttp "github.com/influxdata/influxdb/v2/kit/transport/http"
|
2019-10-08 00:05:46 +00:00
|
|
|
"go.uber.org/zap"
|
|
|
|
)
|
|
|
|
|
2019-10-21 21:37:30 +00:00
|
|
|
// LoggingMW middleware for logging inflight http requests.
|
2020-01-08 19:19:18 +00:00
|
|
|
func LoggingMW(log *zap.Logger) kithttp.Middleware {
|
2019-10-08 00:05:46 +00:00
|
|
|
return func(next http.Handler) http.Handler {
|
|
|
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
2020-01-08 19:19:18 +00:00
|
|
|
srw := kithttp.NewStatusResponseWriter(w)
|
2019-10-08 00:05:46 +00:00
|
|
|
|
|
|
|
var buf bytes.Buffer
|
|
|
|
r.Body = &bodyEchoer{
|
|
|
|
rc: r.Body,
|
|
|
|
teedR: io.TeeReader(r.Body, &buf),
|
|
|
|
}
|
|
|
|
|
|
|
|
defer func(start time.Time) {
|
|
|
|
errField := zap.Skip()
|
2020-02-03 19:07:43 +00:00
|
|
|
if errStr := w.Header().Get(kithttp.PlatformErrorCodeHeader); errStr != "" {
|
2019-10-08 00:05:46 +00:00
|
|
|
errField = zap.Error(errors.New(errStr))
|
|
|
|
}
|
|
|
|
|
|
|
|
errReferenceField := zap.Skip()
|
2020-02-03 19:07:43 +00:00
|
|
|
if errReference := w.Header().Get(kithttp.PlatformErrorCodeHeader); errReference != "" {
|
2020-03-17 17:35:24 +00:00
|
|
|
errReferenceField = zap.String("error_code", errReference)
|
2019-10-08 00:05:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fields := []zap.Field{
|
|
|
|
zap.String("method", r.Method),
|
|
|
|
zap.String("host", r.Host),
|
|
|
|
zap.String("path", r.URL.Path),
|
|
|
|
zap.String("query", r.URL.Query().Encode()),
|
|
|
|
zap.String("proto", r.Proto),
|
2020-01-08 19:19:18 +00:00
|
|
|
zap.Int("status_code", srw.Code()),
|
|
|
|
zap.Int("response_size", srw.ResponseBytes()),
|
2019-10-08 00:05:46 +00:00
|
|
|
zap.Int64("content_length", r.ContentLength),
|
|
|
|
zap.String("referrer", r.Referer()),
|
|
|
|
zap.String("remote", r.RemoteAddr),
|
2020-01-08 19:19:18 +00:00
|
|
|
zap.String("user_agent", kithttp.UserAgent(r)),
|
2019-10-08 00:05:46 +00:00
|
|
|
zap.Duration("took", time.Since(start)),
|
|
|
|
errField,
|
|
|
|
errReferenceField,
|
|
|
|
}
|
|
|
|
|
|
|
|
invalidMethodFn, ok := mapURLPath(r.URL.Path)
|
|
|
|
if !ok || !invalidMethodFn(r.Method) {
|
|
|
|
fields = append(fields, zap.ByteString("body", buf.Bytes()))
|
|
|
|
}
|
|
|
|
|
2019-12-04 23:10:23 +00:00
|
|
|
log.Debug("Request", fields...)
|
2019-10-08 00:05:46 +00:00
|
|
|
}(time.Now())
|
|
|
|
|
|
|
|
next.ServeHTTP(srw, r)
|
|
|
|
}
|
|
|
|
return http.HandlerFunc(fn)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type isValidMethodFn func(method string) bool
|
|
|
|
|
|
|
|
func mapURLPath(rawPath string) (isValidMethodFn, bool) {
|
|
|
|
if fn, ok := blacklistEndpoints[rawPath]; ok {
|
|
|
|
return fn, true
|
|
|
|
}
|
|
|
|
|
|
|
|
shiftPath := func(p string) (head, tail string) {
|
|
|
|
p = path.Clean("/" + p)
|
|
|
|
i := strings.Index(p[1:], "/") + 1
|
|
|
|
if i <= 0 {
|
|
|
|
return p[1:], "/"
|
|
|
|
}
|
|
|
|
return p[1:i], p[i:]
|
|
|
|
}
|
|
|
|
|
|
|
|
// ugh, should probably make this whole operation use a trie
|
|
|
|
partsMatch := func(raw, source string) bool {
|
|
|
|
return raw == source || (strings.HasPrefix(source, ":") && raw != "")
|
|
|
|
}
|
|
|
|
|
|
|
|
compareRawSourceURLs := func(raw, source string) bool {
|
|
|
|
sourceHead, sourceTail := shiftPath(source)
|
|
|
|
for rawHead, rawTail := shiftPath(rawPath); rawHead != ""; {
|
|
|
|
if !partsMatch(rawHead, sourceHead) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
rawHead, rawTail = shiftPath(rawTail)
|
|
|
|
sourceHead, sourceTail = shiftPath(sourceTail)
|
|
|
|
}
|
|
|
|
return sourceHead == ""
|
|
|
|
}
|
|
|
|
|
|
|
|
for sourcePath, fn := range blacklistEndpoints {
|
|
|
|
match := compareRawSourceURLs(rawPath, sourcePath)
|
|
|
|
if match {
|
|
|
|
return fn, true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
|
|
|
func ignoreMethod(ignoredMethods ...string) isValidMethodFn {
|
|
|
|
if len(ignoredMethods) == 0 {
|
|
|
|
return func(string) bool { return true }
|
|
|
|
}
|
|
|
|
|
|
|
|
ignoreMap := make(map[string]bool)
|
|
|
|
for _, method := range ignoredMethods {
|
|
|
|
ignoreMap[method] = true
|
|
|
|
}
|
|
|
|
|
|
|
|
return func(method string) bool {
|
|
|
|
return ignoreMap[method]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-03 19:07:43 +00:00
|
|
|
// TODO(@jsteenb2): make this a stronger type that handlers can register routes that should not be logged.
|
2019-10-08 00:05:46 +00:00
|
|
|
var blacklistEndpoints = map[string]isValidMethodFn{
|
2019-12-09 23:54:16 +00:00
|
|
|
prefixSignIn: ignoreMethod(),
|
|
|
|
prefixSignOut: ignoreMethod(),
|
|
|
|
prefixMe: ignoreMethod(),
|
2019-10-08 00:05:46 +00:00
|
|
|
mePasswordPath: ignoreMethod(),
|
|
|
|
usersPasswordPath: ignoreMethod(),
|
2020-02-03 19:07:43 +00:00
|
|
|
"/api/v2/packages/apply": ignoreMethod(),
|
2019-12-09 23:54:16 +00:00
|
|
|
prefixWrite: ignoreMethod("POST"),
|
2019-10-08 00:05:46 +00:00
|
|
|
organizationsIDSecretsPath: ignoreMethod("PATCH"),
|
|
|
|
organizationsIDSecretsDeletePath: ignoreMethod("POST"),
|
2019-12-09 23:54:16 +00:00
|
|
|
prefixSetup: ignoreMethod("POST"),
|
|
|
|
prefixNotificationEndpoints: ignoreMethod("POST"),
|
2019-10-08 00:05:46 +00:00
|
|
|
notificationEndpointsIDPath: ignoreMethod("PUT"),
|
|
|
|
}
|
|
|
|
|
|
|
|
type bodyEchoer struct {
|
|
|
|
rc io.ReadCloser
|
|
|
|
teedR io.Reader
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *bodyEchoer) Read(p []byte) (int, error) {
|
|
|
|
return b.teedR.Read(p)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *bodyEchoer) Close() error {
|
|
|
|
return b.rc.Close()
|
|
|
|
}
|
2019-10-21 21:37:30 +00:00
|
|
|
|
2020-01-08 19:19:18 +00:00
|
|
|
func applyMW(h http.Handler, m ...kithttp.Middleware) http.Handler {
|
2019-10-21 21:37:30 +00:00
|
|
|
if len(m) < 1 {
|
|
|
|
return h
|
|
|
|
}
|
|
|
|
wrapped := h
|
|
|
|
|
|
|
|
for i := len(m) - 1; i >= 0; i-- {
|
|
|
|
wrapped = m[i](wrapped)
|
|
|
|
}
|
|
|
|
return wrapped
|
|
|
|
}
|