2017-02-07 21:33:23 +00:00
package utils
import (
"bufio"
2017-11-22 17:20:03 +00:00
"fmt"
2017-02-07 21:33:23 +00:00
"io"
"net"
"net/http"
"net/url"
2017-11-22 17:20:03 +00:00
"reflect"
2018-01-22 11:16:03 +00:00
log "github.com/sirupsen/logrus"
2017-02-07 21:33:23 +00:00
)
2018-07-11 08:08:03 +00:00
// ProxyWriter calls recorder, used to debug logs
2018-06-20 07:12:03 +00:00
type ProxyWriter struct {
2018-07-11 08:08:03 +00:00
w http . ResponseWriter
2018-06-20 07:12:03 +00:00
code int
length int64
2018-07-11 08:08:03 +00:00
log * log . Logger
}
// NewProxyWriter creates a new ProxyWriter
func NewProxyWriter ( w http . ResponseWriter ) * ProxyWriter {
return NewProxyWriterWithLogger ( w , log . StandardLogger ( ) )
2017-02-07 21:33:23 +00:00
}
2018-07-11 08:08:03 +00:00
// NewProxyWriterWithLogger creates a new ProxyWriter
func NewProxyWriterWithLogger ( w http . ResponseWriter , l * log . Logger ) * ProxyWriter {
2018-06-20 07:12:03 +00:00
return & ProxyWriter {
2018-07-11 08:08:03 +00:00
w : w ,
log : l ,
2018-04-10 15:24:04 +00:00
}
}
2018-07-11 08:08:03 +00:00
// StatusCode gets status code
2018-06-20 07:12:03 +00:00
func ( p * ProxyWriter ) StatusCode ( ) int {
if p . code == 0 {
2017-02-07 21:33:23 +00:00
// per contract standard lib will set this to http.StatusOK if not set
// by user, here we avoid the confusion by mirroring this logic
return http . StatusOK
}
2018-06-20 07:12:03 +00:00
return p . code
}
2018-07-11 08:08:03 +00:00
// GetLength gets content length
2018-06-20 07:12:03 +00:00
func ( p * ProxyWriter ) GetLength ( ) int64 {
return p . length
2017-02-07 21:33:23 +00:00
}
2018-07-11 08:08:03 +00:00
// Header gets response header
2018-06-20 07:12:03 +00:00
func ( p * ProxyWriter ) Header ( ) http . Header {
2018-07-11 08:08:03 +00:00
return p . w . Header ( )
2017-02-07 21:33:23 +00:00
}
2018-06-20 07:12:03 +00:00
func ( p * ProxyWriter ) Write ( buf [ ] byte ) ( int , error ) {
p . length = p . length + int64 ( len ( buf ) )
2018-07-11 08:08:03 +00:00
return p . w . Write ( buf )
2017-02-07 21:33:23 +00:00
}
2018-07-11 08:08:03 +00:00
// WriteHeader writes status code
2018-06-20 07:12:03 +00:00
func ( p * ProxyWriter ) WriteHeader ( code int ) {
p . code = code
2018-07-11 08:08:03 +00:00
p . w . WriteHeader ( code )
2017-02-07 21:33:23 +00:00
}
2018-07-11 08:08:03 +00:00
// Flush flush the writer
2018-06-20 07:12:03 +00:00
func ( p * ProxyWriter ) Flush ( ) {
2018-07-11 08:08:03 +00:00
if f , ok := p . w . ( http . Flusher ) ; ok {
2017-02-07 21:33:23 +00:00
f . Flush ( )
}
}
2018-07-11 08:08:03 +00:00
// CloseNotify returns a channel that receives at most a single value (true)
// when the client connection has gone away.
2018-06-20 07:12:03 +00:00
func ( p * ProxyWriter ) CloseNotify ( ) <- chan bool {
2018-07-11 08:08:03 +00:00
if cn , ok := p . w . ( http . CloseNotifier ) ; ok {
2017-11-22 17:20:03 +00:00
return cn . CloseNotify ( )
}
2018-07-11 08:08:03 +00:00
p . log . Debugf ( "Upstream ResponseWriter of type %v does not implement http.CloseNotifier. Returning dummy channel." , reflect . TypeOf ( p . w ) )
2017-11-22 17:20:03 +00:00
return make ( <- chan bool )
}
2018-07-11 08:08:03 +00:00
// Hijack lets the caller take over the connection.
2018-06-20 07:12:03 +00:00
func ( p * ProxyWriter ) Hijack ( ) ( net . Conn , * bufio . ReadWriter , error ) {
2018-07-11 08:08:03 +00:00
if hi , ok := p . w . ( http . Hijacker ) ; ok {
2017-11-22 17:20:03 +00:00
return hi . Hijack ( )
}
2018-07-11 08:08:03 +00:00
p . log . Debugf ( "Upstream ResponseWriter of type %v does not implement http.Hijacker. Returning dummy channel." , reflect . TypeOf ( p . w ) )
return nil , nil , fmt . Errorf ( "the response writer that was wrapped in this proxy, does not implement http.Hijacker. It is of type: %v" , reflect . TypeOf ( p . w ) )
2017-02-07 21:33:23 +00:00
}
2018-07-11 08:08:03 +00:00
// NewBufferWriter creates a new BufferWriter
2017-02-07 21:33:23 +00:00
func NewBufferWriter ( w io . WriteCloser ) * BufferWriter {
return & BufferWriter {
W : w ,
H : make ( http . Header ) ,
}
}
2018-07-11 08:08:03 +00:00
// BufferWriter buffer writer
2017-02-07 21:33:23 +00:00
type BufferWriter struct {
H http . Header
Code int
W io . WriteCloser
}
2018-07-11 08:08:03 +00:00
// Close close the writer
2017-02-07 21:33:23 +00:00
func ( b * BufferWriter ) Close ( ) error {
return b . W . Close ( )
}
2018-07-11 08:08:03 +00:00
// Header gets response header
2017-02-07 21:33:23 +00:00
func ( b * BufferWriter ) Header ( ) http . Header {
return b . H
}
func ( b * BufferWriter ) Write ( buf [ ] byte ) ( int , error ) {
return b . W . Write ( buf )
}
2018-07-11 08:08:03 +00:00
// WriteHeader writes status code
2017-02-07 21:33:23 +00:00
func ( b * BufferWriter ) WriteHeader ( code int ) {
b . Code = code
}
2018-07-11 08:08:03 +00:00
// CloseNotify returns a channel that receives at most a single value (true)
// when the client connection has gone away.
2017-11-22 17:20:03 +00:00
func ( b * BufferWriter ) CloseNotify ( ) <- chan bool {
if cn , ok := b . W . ( http . CloseNotifier ) ; ok {
return cn . CloseNotify ( )
}
log . Warningf ( "Upstream ResponseWriter of type %v does not implement http.CloseNotifier. Returning dummy channel." , reflect . TypeOf ( b . W ) )
return make ( <- chan bool )
}
2018-07-11 08:08:03 +00:00
// Hijack lets the caller take over the connection.
2017-02-07 21:33:23 +00:00
func ( b * BufferWriter ) Hijack ( ) ( net . Conn , * bufio . ReadWriter , error ) {
2017-11-22 17:20:03 +00:00
if hi , ok := b . W . ( http . Hijacker ) ; ok {
return hi . Hijack ( )
}
2018-06-20 07:12:03 +00:00
log . Debugf ( "Upstream ResponseWriter of type %v does not implement http.Hijacker. Returning dummy channel." , reflect . TypeOf ( b . W ) )
return nil , nil , fmt . Errorf ( "the response writer that was wrapped in this proxy, does not implement http.Hijacker. It is of type: %v" , reflect . TypeOf ( b . W ) )
2017-02-07 21:33:23 +00:00
}
type nopWriteCloser struct {
io . Writer
}
func ( * nopWriteCloser ) Close ( ) error { return nil }
2018-07-11 08:08:03 +00:00
// NopWriteCloser returns a WriteCloser with a no-op Close method wrapping
2017-02-07 21:33:23 +00:00
// the provided Writer w.
func NopWriteCloser ( w io . Writer ) io . WriteCloser {
2018-07-11 08:08:03 +00:00
return & nopWriteCloser { Writer : w }
2017-02-07 21:33:23 +00:00
}
// CopyURL provides update safe copy by avoiding shallow copying User field
func CopyURL ( i * url . URL ) * url . URL {
out := * i
if i . User != nil {
out . User = & ( * i . User )
}
return & out
}
// CopyHeaders copies http headers from source to destination, it
// does not overide, but adds multiple headers
2017-11-22 17:20:03 +00:00
func CopyHeaders ( dst http . Header , src http . Header ) {
2017-02-07 21:33:23 +00:00
for k , vv := range src {
2017-11-22 17:20:03 +00:00
dst [ k ] = append ( dst [ k ] , vv ... )
2017-02-07 21:33:23 +00:00
}
}
// HasHeaders determines whether any of the header names is present in the http headers
func HasHeaders ( names [ ] string , headers http . Header ) bool {
for _ , h := range names {
if headers . Get ( h ) != "" {
return true
}
}
return false
}
// RemoveHeaders removes the header with the given names from the headers map
func RemoveHeaders ( headers http . Header , names ... string ) {
for _ , h := range names {
headers . Del ( h )
}
}