From fe394c81ceed7c3776a5c6de929c0885c9f01574 Mon Sep 17 00:00:00 2001 From: Blake Williams Date: Mon, 13 Apr 2020 15:02:06 +1000 Subject: [PATCH] Remove dependency on Goji (#223) --- go.mod | 1 - go.sum | 2 - hlog/hlog.go | 2 +- hlog/internal/mutil/LICENSE | 20 ++++ hlog/internal/mutil/mutil.go | 6 ++ hlog/internal/mutil/writer_proxy.go | 149 ++++++++++++++++++++++++++++ 6 files changed, 176 insertions(+), 4 deletions(-) create mode 100644 hlog/internal/mutil/LICENSE create mode 100644 hlog/internal/mutil/mutil.go create mode 100644 hlog/internal/mutil/writer_proxy.go diff --git a/go.mod b/go.mod index 8c42ba88..340ed40e 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,5 @@ require ( github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e github.com/pkg/errors v0.8.1 github.com/rs/xid v1.2.1 - github.com/zenazn/goji v0.9.0 golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74 ) diff --git a/go.sum b/go.sum index b14fd2b6..13b9b2c3 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,6 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/zenazn/goji v0.9.0 h1:RSQQAbXGArQ0dIDEq+PI6WqN6if+5KHu6x2Cx/GXLTQ= -github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/hlog/hlog.go b/hlog/hlog.go index 37f43363..8748b25f 100644 --- a/hlog/hlog.go +++ b/hlog/hlog.go @@ -9,8 +9,8 @@ import ( "github.com/rs/xid" "github.com/rs/zerolog" + "github.com/rs/zerolog/hlog/internal/mutil" "github.com/rs/zerolog/log" - "github.com/zenazn/goji/web/mutil" ) // FromRequest gets the logger in the request's context. diff --git a/hlog/internal/mutil/LICENSE b/hlog/internal/mutil/LICENSE new file mode 100644 index 00000000..446aba06 --- /dev/null +++ b/hlog/internal/mutil/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2014, 2015, 2016 Carl Jackson (carl@avtok.com) + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/hlog/internal/mutil/mutil.go b/hlog/internal/mutil/mutil.go new file mode 100644 index 00000000..80d6ac66 --- /dev/null +++ b/hlog/internal/mutil/mutil.go @@ -0,0 +1,6 @@ +// Package mutil contains various functions that are helpful when writing http +// middleware. +// +// It has been vendored from Goji v1.0, with the exception of the code for Go 1.8: +// https://github.com/zenazn/goji/ +package mutil diff --git a/hlog/internal/mutil/writer_proxy.go b/hlog/internal/mutil/writer_proxy.go new file mode 100644 index 00000000..598a86fb --- /dev/null +++ b/hlog/internal/mutil/writer_proxy.go @@ -0,0 +1,149 @@ +package mutil + +import ( + "bufio" + "io" + "net" + "net/http" +) + +// WriterProxy is a proxy around an http.ResponseWriter that allows you to hook +// into various parts of the response process. +type WriterProxy interface { + http.ResponseWriter + // Status returns the HTTP status of the request, or 0 if one has not + // yet been sent. + Status() int + // BytesWritten returns the total number of bytes sent to the client. + BytesWritten() int + // Tee causes the response body to be written to the given io.Writer in + // addition to proxying the writes through. Only one io.Writer can be + // tee'd to at once: setting a second one will overwrite the first. + // Writes will be sent to the proxy before being written to this + // io.Writer. It is illegal for the tee'd writer to be modified + // concurrently with writes. + Tee(io.Writer) + // Unwrap returns the original proxied target. + Unwrap() http.ResponseWriter +} + +// WrapWriter wraps an http.ResponseWriter, returning a proxy that allows you to +// hook into various parts of the response process. +func WrapWriter(w http.ResponseWriter) WriterProxy { + _, cn := w.(http.CloseNotifier) + _, fl := w.(http.Flusher) + _, hj := w.(http.Hijacker) + _, rf := w.(io.ReaderFrom) + + bw := basicWriter{ResponseWriter: w} + if cn && fl && hj && rf { + return &fancyWriter{bw} + } + if fl { + return &flushWriter{bw} + } + return &bw +} + +// basicWriter wraps a http.ResponseWriter that implements the minimal +// http.ResponseWriter interface. +type basicWriter struct { + http.ResponseWriter + wroteHeader bool + code int + bytes int + tee io.Writer +} + +func (b *basicWriter) WriteHeader(code int) { + if !b.wroteHeader { + b.code = code + b.wroteHeader = true + b.ResponseWriter.WriteHeader(code) + } +} + +func (b *basicWriter) Write(buf []byte) (int, error) { + b.WriteHeader(http.StatusOK) + n, err := b.ResponseWriter.Write(buf) + if b.tee != nil { + _, err2 := b.tee.Write(buf[:n]) + // Prefer errors generated by the proxied writer. + if err == nil { + err = err2 + } + } + b.bytes += n + return n, err +} + +func (b *basicWriter) maybeWriteHeader() { + if !b.wroteHeader { + b.WriteHeader(http.StatusOK) + } +} + +func (b *basicWriter) Status() int { + return b.code +} + +func (b *basicWriter) BytesWritten() int { + return b.bytes +} + +func (b *basicWriter) Tee(w io.Writer) { + b.tee = w +} + +func (b *basicWriter) Unwrap() http.ResponseWriter { + return b.ResponseWriter +} + +// fancyWriter is a writer that additionally satisfies http.CloseNotifier, +// http.Flusher, http.Hijacker, and io.ReaderFrom. It exists for the common case +// of wrapping the http.ResponseWriter that package http gives you, in order to +// make the proxied object support the full method set of the proxied object. +type fancyWriter struct { + basicWriter +} + +func (f *fancyWriter) CloseNotify() <-chan bool { + cn := f.basicWriter.ResponseWriter.(http.CloseNotifier) + return cn.CloseNotify() +} + +func (f *fancyWriter) Flush() { + fl := f.basicWriter.ResponseWriter.(http.Flusher) + fl.Flush() +} + +func (f *fancyWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { + hj := f.basicWriter.ResponseWriter.(http.Hijacker) + return hj.Hijack() +} + +func (f *fancyWriter) ReadFrom(r io.Reader) (int64, error) { + if f.basicWriter.tee != nil { + return io.Copy(&f.basicWriter, r) + } + rf := f.basicWriter.ResponseWriter.(io.ReaderFrom) + f.basicWriter.maybeWriteHeader() + return rf.ReadFrom(r) +} + +type flushWriter struct { + basicWriter +} + +func (f *flushWriter) Flush() { + fl := f.basicWriter.ResponseWriter.(http.Flusher) + fl.Flush() +} + +var ( + _ http.CloseNotifier = &fancyWriter{} + _ http.Flusher = &fancyWriter{} + _ http.Hijacker = &fancyWriter{} + _ io.ReaderFrom = &fancyWriter{} + _ http.Flusher = &flushWriter{} +)