bump github.com/go-pkgz/rest to v1.9.0

This commit is contained in:
Umputun 2021-04-12 11:38:21 -05:00
parent c4703d3b17
commit bdae15b274
10 changed files with 161 additions and 45 deletions

2
go.mod
View File

@ -5,7 +5,7 @@ go 1.16
require (
github.com/fsouza/go-dockerclient v1.7.2
github.com/go-pkgz/lgr v0.10.4
github.com/go-pkgz/rest v1.8.1
github.com/go-pkgz/rest v1.9.0
github.com/gorilla/handlers v1.5.1
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.7.0

2
go.sum
View File

@ -47,6 +47,8 @@ github.com/go-pkgz/lgr v0.10.4 h1:l7qyFjqEZgwRgaQQSEp6tve4A3OU80VrfzpvtEX8ngw=
github.com/go-pkgz/lgr v0.10.4/go.mod h1:CD0s1z6EFpIUplV067gitF77tn25JItzwHNKAPqeCF0=
github.com/go-pkgz/rest v1.8.1 h1:M0sMbgcWxHpKjXw7Z8uF6uNcsLynaPoR0CHGczjYSw0=
github.com/go-pkgz/rest v1.8.1/go.mod h1:wZ/dGipZUaF9to0vIQl7PwDHgWQDB0jsrFg1xnAKLDw=
github.com/go-pkgz/rest v1.9.0 h1:cbBXd4YH0X6W64zneDGF+Ym3Mgj7Gv54krIEJjbQACs=
github.com/go-pkgz/rest v1.9.0/go.mod h1:wZ/dGipZUaF9to0vIQl7PwDHgWQDB0jsrFg1xnAKLDw=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=

View File

@ -33,3 +33,30 @@ func CacheControl(expiration time.Duration, version string) func(http.Handler) h
return http.HandlerFunc(fn)
}
}
// CacheControlDynamic is a middleware setting cache expiration. Using url+ func(r) for etag
func CacheControlDynamic(expiration time.Duration, versionFn func(r *http.Request) string) func(http.Handler) http.Handler {
etag := func(r *http.Request, version string) string {
s := fmt.Sprintf("%s:%s", version, r.URL.String())
return fmt.Sprintf("%x", sha1.Sum([]byte(s))) //nolint
}
return func(h http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
e := `"` + etag(r, versionFn(r)) + `"`
w.Header().Set("Etag", e)
w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d, no-cache", int(expiration.Seconds())))
if match := r.Header.Get("If-None-Match"); match != "" {
if strings.Contains(match, e) {
w.WriteHeader(http.StatusNotModified)
return
}
}
h.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
}

View File

@ -64,7 +64,7 @@ func Gzip(contentTypes ...string) func(http.Handler) http.Handler {
var gzOk bool
ctype := contentType(r)
for _, c := range gzCts {
if strings.EqualFold(ctype, c) {
if strings.HasPrefix(strings.ToLower(ctype), strings.ToLower(c)) {
gzOk = true
break
}

View File

@ -30,8 +30,7 @@ func (e *ErrorLogger) Log(w http.ResponseWriter, r *http.Request, httpCode int,
if e.l != nil {
e.l.Logf("%s", errDetailsMsg(r, httpCode, err, m))
}
w.WriteHeader(httpCode)
RenderJSON(w, JSON{"error": m})
renderJSONWithStatus(w, JSON{"error": m}, httpCode)
}
// SendErrorJSON sends {error: msg} with error code and logging error and caller
@ -39,8 +38,7 @@ func SendErrorJSON(w http.ResponseWriter, r *http.Request, l logger.Backend, cod
if l != nil {
l.Logf("%s", errDetailsMsg(r, code, err, msg))
}
w.WriteHeader(code)
RenderJSON(w, JSON{"error": msg})
renderJSONWithStatus(w, JSON{"error": msg}, code)
}
func errDetailsMsg(r *http.Request, code int, err error, msg string) string {

View File

@ -12,19 +12,21 @@ import (
"net/http"
"net/url"
"regexp"
"strconv"
"strings"
"time"
)
// Middleware is a logger for rest requests.
type Middleware struct {
prefix string
logBody bool
maxBodySize int
ipFn func(ip string) string
userFn func(r *http.Request) (string, error)
subjFn func(r *http.Request) (string, error)
log Backend
prefix string
logBody bool
maxBodySize int
ipFn func(ip string) string
userFn func(r *http.Request) (string, error)
subjFn func(r *http.Request) (string, error)
log Backend
apacheCombined bool
}
// Backend is logging backend
@ -32,6 +34,19 @@ type Backend interface {
Logf(format string, args ...interface{})
}
type logParts struct {
duration time.Duration
rawURL string
method string
remoteIP string
statusCode int
respSize int
prefix string
user string
body string
}
type stdBackend struct{}
func (s stdBackend) Logf(format string, args ...interface{}) {
@ -61,6 +76,11 @@ func New(options ...Option) *Middleware {
// Handler middleware prints http log
func (l *Middleware) Handler(next http.Handler) http.Handler {
formater := l.formatDefault
if l.apacheCombined {
formater = l.formatApacheCombined
}
fn := func(w http.ResponseWriter, r *http.Request) {
ww := newCustomResponseWriter(w)
@ -88,37 +108,19 @@ func (l *Middleware) Handler(next http.Handler) http.Handler {
remoteIP = l.ipFn(remoteIP)
}
var bld strings.Builder
if l.prefix != "" {
_, _ = bld.WriteString(l.prefix)
_, _ = bld.WriteString(" ")
p := &logParts{
duration: t2.Sub(t1),
rawURL: rawurl,
method: r.Method,
remoteIP: remoteIP,
statusCode: ww.status,
respSize: ww.size,
prefix: l.prefix,
user: user,
body: body,
}
_, _ = bld.WriteString(fmt.Sprintf("%s - %s - %s - %d (%d) - %v", r.Method, rawurl, remoteIP, ww.status, ww.size, t2.Sub(t1)))
if user != "" {
_, _ = bld.WriteString(" - ")
_, _ = bld.WriteString(user)
}
if l.subjFn != nil {
if subj, err := l.subjFn(r); err == nil {
_, _ = bld.WriteString(" - ")
_, _ = bld.WriteString(subj)
}
}
if traceID := r.Header.Get("X-Request-ID"); traceID != "" {
_, _ = bld.WriteString(" - ")
_, _ = bld.WriteString(traceID)
}
if body != "" {
_, _ = bld.WriteString(" - ")
_, _ = bld.WriteString(body)
}
l.log.Logf("%s", bld.String())
l.log.Logf(formater(r, p))
}()
next.ServeHTTP(ww, r)
@ -126,6 +128,73 @@ func (l *Middleware) Handler(next http.Handler) http.Handler {
return http.HandlerFunc(fn)
}
func (l *Middleware) formatDefault(r *http.Request, p *logParts) string {
var bld strings.Builder
if l.prefix != "" {
_, _ = bld.WriteString(l.prefix)
_, _ = bld.WriteString(" ")
}
_, _ = bld.WriteString(fmt.Sprintf("%s - %s - %s - %d (%d) - %v",
p.method, p.rawURL, p.remoteIP, p.statusCode, p.respSize, p.duration))
if p.user != "" {
_, _ = bld.WriteString(" - ")
_, _ = bld.WriteString(p.user)
}
if l.subjFn != nil {
if subj, err := l.subjFn(r); err == nil {
_, _ = bld.WriteString(" - ")
_, _ = bld.WriteString(subj)
}
}
if traceID := r.Header.Get("X-Request-ID"); traceID != "" {
_, _ = bld.WriteString(" - ")
_, _ = bld.WriteString(traceID)
}
if p.body != "" {
_, _ = bld.WriteString(" - ")
_, _ = bld.WriteString(p.body)
}
return bld.String()
}
// 127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 "http://www.example.com/start.html" "Mozilla/4.08 [en] (Win98; I ;Nav)"
//nolint gosec
func (l *Middleware) formatApacheCombined(r *http.Request, p *logParts) string {
username := "-"
if p.user != "" {
username = p.user
}
var bld strings.Builder
bld.WriteString(p.remoteIP)
bld.WriteString(" - ")
bld.WriteString(username)
bld.WriteString(" [")
bld.WriteString(time.Now().Format("02/Jan/2006:15:04:05 -0700"))
bld.WriteString(`] "`)
bld.WriteString(p.method)
bld.WriteString(" ")
bld.WriteString(p.rawURL)
bld.WriteString(`" `)
bld.WriteString(r.Proto)
bld.WriteString(`" `)
bld.WriteString(strconv.Itoa(p.statusCode))
bld.WriteString(" ")
bld.WriteString(strconv.Itoa(p.respSize))
bld.WriteString(` "`)
bld.WriteString(r.Header.Get("Referer"))
bld.WriteString(`" "`)
bld.WriteString(r.Header.Get("User-Agent"))
bld.WriteString(`"`)
return bld.String()
}
var reMultWhtsp = regexp.MustCompile(`[\s\p{Zs}]{2,}`)
func (l *Middleware) getBody(r *http.Request) string {

View File

@ -49,7 +49,13 @@ func SubjFn(subjFn func(r *http.Request) (string, error)) Option {
}
}
// Log sets logging backend.
// ApacheCombined sets format to Apache Combined Log.
// See http://httpd.apache.org/docs/2.2/logs.html#combined
func ApacheCombined(l *Middleware) {
l.apacheCombined = true
}
// Log sets logging backend.
func Log(log Backend) Option {
return func(l *Middleware) {
l.log = log

View File

@ -59,7 +59,7 @@ func Recoverer(l logger.Backend) func(http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
defer func() {
if rvr := recover(); rvr != nil {
l.Logf("request panic, %v", rvr)
l.Logf("request panic for %s from %s, %v", r.URL.String(), r.RemoteAddr, rvr)
l.Logf(string(debug.Stack()))
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}

View File

@ -53,3 +53,17 @@ func RenderJSONWithHTML(w http.ResponseWriter, r *http.Request, v interface{}) e
}
return RenderJSONFromBytes(w, r, data)
}
// renderJSONWithStatus sends data as json and enforces status code
func renderJSONWithStatus(w http.ResponseWriter, data interface{}, code int) {
buf := &bytes.Buffer{}
enc := json.NewEncoder(buf)
enc.SetEscapeHTML(true)
if err := enc.Encode(data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(code)
_, _ = w.Write(buf.Bytes())
}

2
vendor/modules.txt vendored
View File

@ -70,7 +70,7 @@ github.com/fsouza/go-dockerclient
# github.com/go-pkgz/lgr v0.10.4
## explicit
github.com/go-pkgz/lgr
# github.com/go-pkgz/rest v1.8.1
# github.com/go-pkgz/rest v1.9.0
## explicit
github.com/go-pkgz/rest
github.com/go-pkgz/rest/logger