I want to embed a http.ResponseWriter
in a struct. That's fine, it makes sure my struct also implements http.ResponseWriter
:
type MyWriter struct {
BytesWritten int
http.ResponseWriter
}
However, it no longer implements http.Hijacker
, http.CloseNotifier
or http.Flusher
even though the embedded http.ResponseWriter
usually does.
Is there some way to do this?
If you know that the response writer satisfies all of the interfaces listed in the question, then you can do this:
type allResponseWriterInterfaces {
http.ResponseWriter
http.Hijacker
http.CloseNotifier
http.Flusher
}
type MyWriter struct {
BytesWritten int
allResponseWriterInterfaces
}
...
aw, ok := w.(allResponseWriterInterfaces)
if !ok {
// oops, response writer does not implement the interfaces
// handle the error
}
mw := MyWriter{0, aw}
It get's messy if the response writer does not satisfy all of the interfaces. See the Gorilla logger for an example of handling the case where the response writer satisfies (http.ResponseWriter, http.CloseNotifier) or (http.ResponseWriter, http.CloseNotifier, http.Hijacker).
One solution is the brute-force solution. Actually implement all permutations. I tried this and found out how painful it is. There are 18 permutations!
So here is a general-purpose wrapper. The advantage is that you can re-use this wrapper as many times as you want.
The key is to define an interface like so:
// ResponseWriterTo can proxy requests to an underlying http.ResponseWriter.
// It is used with CustomResponseWriter to wrap an http.ResponseWriter with
// custom behavior.
type ResponseWriterTo interface {
HeaderTo(w http.ResponseWriter) http.Header
WriteHeaderTo(w http.ResponseWriter, s int)
WriteTo(w http.ResponseWriter, b []byte) (n int, err error)
// Additional methods that http.ResponseWriter sometimes implements.
CloseNotifyTo(w http.CloseNotifier) <-chan bool
FlushTo(w http.Flusher)
HijackTo(w http.Hijacker) (net.Conn, *bufio.ReadWriter, error)
// ReaderFrom is used by the http package to optimize reads from TCP
// connections or from files.
ReadFromTo(w io.ReaderFrom, r io.Reader) (n int64, err error)
}
so that we can define a custom wrapper function (this is the verbose part):
// CustomResponseWriter creates a http.ResponseWriter that implements as many
// hidden interfaces from the base http.ResponseWriter as are available.
func CustomResponseWriter(base http.ResponseWriter, custom ResponseWriterTo) http.ResponseWriter {
rw := &customResponseWriter{base: base, custom: custom}
// the base http.ResponseWriter can implement many hidden interfaces,
// so check all permutations
type HCFR interface {
http.ResponseWriter
http.Hijacker
http.CloseNotifier
http.Flusher
io.ReaderFrom
}
if _, ok := base.(HCFR); ok {
return struct {
HCFR
}{rw}
}
type HCF interface {
http.ResponseWriter
http.Hijacker
http.CloseNotifier
http.Flusher
}
if _, ok := base.(HCF); ok {
return struct {
HCF
}{rw}
}
type HCR interface {
http.ResponseWriter
http.Hijacker
http.CloseNotifier
io.ReaderFrom
}
if _, ok := base.(HCR); ok {
return struct {
HCR
}{rw}
}
type HFR interface {
http.ResponseWriter
http.Hijacker
http.Flusher
io.ReaderFrom
}
if _, ok := base.(HFR); ok {
return struct {
HFR
}{rw}
}
type CFR interface {
http.ResponseWriter
http.CloseNotifier
http.Flusher
io.ReaderFrom
}
if _, ok := base.(CFR); ok {
return struct {
CFR
}{rw}
}
type HC interface {
http.ResponseWriter
http.Hijacker
http.CloseNotifier
}
if _, ok := base.(HC); ok {
return struct {
HC
}{rw}
}
type HF interface {
http.ResponseWriter
http.Hijacker
http.Flusher
}
if _, ok := base.(HF); ok {
return struct {
HF
}{rw}
}
type CF interface {
http.ResponseWriter
http.CloseNotifier
http.Flusher
}
if _, ok := base.(CF); ok {
return struct {
CF
}{rw}
}
type HR interface {
http.ResponseWriter
http.Hijacker
io.ReaderFrom
}
if _, ok := base.(HR); ok {
return struct {
HR
}{rw}
}
type CR interface {
http.ResponseWriter
http.CloseNotifier
io.ReaderFrom
}
if _, ok := base.(CR); ok {
return struct {
CR
}{rw}
}
type FR interface {
http.ResponseWriter
http.Flusher
io.ReaderFrom
}
if _, ok := base.(FR); ok {
return struct {
FR
}{rw}
}
type H interface {
http.ResponseWriter
http.Hijacker
}
if _, ok := base.(H); ok {
return struct {
H
}{rw}
}
type C interface {
http.ResponseWriter
http.CloseNotifier
}
if _, ok := base.(C); ok {
return struct {
C
}{rw}
}
type F interface {
http.ResponseWriter
http.Flusher
}
if _, ok := base.(F); ok {
return struct {
F
}{rw}
}
type R interface {
http.ResponseWriter
io.ReaderFrom
}
if _, ok := base.(R); ok {
return struct {
R
}{rw}
}
return struct {
http.ResponseWriter
}{rw}
}
// customResponseWriter allows us to adapt a ResponseWriterTo to a ResponseWriter.
type customResponseWriter struct {
base http.ResponseWriter
custom ResponseWriterTo
}
func (w *customResponseWriter) Header() http.Header { return w.custom.HeaderTo(w.base) }
func (w *customResponseWriter) Write(b []byte) (int, error) { return w.custom.WriteTo(w.base, b) }
func (w *customResponseWriter) WriteHeader(s int) { w.custom.WriteHeaderTo(w.base, s) }
func (w *customResponseWriter) CloseNotify() <-chan bool {
return w.custom.CloseNotifyTo(w.base.(http.CloseNotifier))
}
func (w *customResponseWriter) Flush() { w.custom.FlushTo(w.base.(http.Flusher)) }
func (w *customResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return w.custom.HijackTo(w.base.(http.Hijacker))
}
func (w *customResponseWriter) ReadFrom(r io.Reader) (n int64, err error) {
return w.custom.ReadFromTo(w.base.(io.ReaderFrom), r)
}
The idea is to embed the correct interface into a struct. Then only the "right" methods are exposed.