github.com/hustcat/docker@v1.3.3-0.20160314103604-901c67a8eeab/pkg/authorization/response.go (about)

     1  package authorization
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"encoding/json"
     7  	"fmt"
     8  	"github.com/Sirupsen/logrus"
     9  	"net"
    10  	"net/http"
    11  )
    12  
    13  // ResponseModifier allows authorization plugins to read and modify the content of the http.response
    14  type ResponseModifier interface {
    15  	http.ResponseWriter
    16  	http.Flusher
    17  	http.CloseNotifier
    18  
    19  	// RawBody returns the current http content
    20  	RawBody() []byte
    21  
    22  	// RawHeaders returns the current content of the http headers
    23  	RawHeaders() ([]byte, error)
    24  
    25  	// StatusCode returns the current status code
    26  	StatusCode() int
    27  
    28  	// OverrideBody replace the body of the HTTP reply
    29  	OverrideBody(b []byte)
    30  
    31  	// OverrideHeader replace the headers of the HTTP reply
    32  	OverrideHeader(b []byte) error
    33  
    34  	// OverrideStatusCode replaces the status code of the HTTP reply
    35  	OverrideStatusCode(statusCode int)
    36  
    37  	// Flush flushes all data to the HTTP response
    38  	FlushAll() error
    39  
    40  	// Hijacked indicates the response has been hijacked by the Docker daemon
    41  	Hijacked() bool
    42  }
    43  
    44  // NewResponseModifier creates a wrapper to an http.ResponseWriter to allow inspecting and modifying the content
    45  func NewResponseModifier(rw http.ResponseWriter) ResponseModifier {
    46  	return &responseModifier{rw: rw, header: make(http.Header)}
    47  }
    48  
    49  // responseModifier is used as an adapter to http.ResponseWriter in order to manipulate and explore
    50  // the http request/response from docker daemon
    51  type responseModifier struct {
    52  	// The original response writer
    53  	rw http.ResponseWriter
    54  
    55  	r *http.Request
    56  
    57  	status int
    58  	// body holds the response body
    59  	body []byte
    60  	// header holds the response header
    61  	header http.Header
    62  	// statusCode holds the response status code
    63  	statusCode int
    64  	// hijacked indicates the request has been hijacked
    65  	hijacked bool
    66  }
    67  
    68  func (rm *responseModifier) Hijacked() bool {
    69  	return rm.hijacked
    70  }
    71  
    72  // WriterHeader stores the http status code
    73  func (rm *responseModifier) WriteHeader(s int) {
    74  
    75  	// Use original request if hijacked
    76  	if rm.hijacked {
    77  		rm.rw.WriteHeader(s)
    78  		return
    79  	}
    80  
    81  	rm.statusCode = s
    82  }
    83  
    84  // Header returns the internal http header
    85  func (rm *responseModifier) Header() http.Header {
    86  
    87  	// Use original header if hijacked
    88  	if rm.hijacked {
    89  		return rm.rw.Header()
    90  	}
    91  
    92  	return rm.header
    93  }
    94  
    95  // Header returns the internal http header
    96  func (rm *responseModifier) StatusCode() int {
    97  	return rm.statusCode
    98  }
    99  
   100  // Override replace the body of the HTTP reply
   101  func (rm *responseModifier) OverrideBody(b []byte) {
   102  	rm.body = b
   103  }
   104  
   105  func (rm *responseModifier) OverrideStatusCode(statusCode int) {
   106  	rm.statusCode = statusCode
   107  }
   108  
   109  // Override replace the headers of the HTTP reply
   110  func (rm *responseModifier) OverrideHeader(b []byte) error {
   111  	header := http.Header{}
   112  	if err := json.Unmarshal(b, &header); err != nil {
   113  		return err
   114  	}
   115  	rm.header = header
   116  	return nil
   117  }
   118  
   119  // Write stores the byte array inside content
   120  func (rm *responseModifier) Write(b []byte) (int, error) {
   121  
   122  	if rm.hijacked {
   123  		return rm.rw.Write(b)
   124  	}
   125  
   126  	rm.body = append(rm.body, b...)
   127  	return len(b), nil
   128  }
   129  
   130  // Body returns the response body
   131  func (rm *responseModifier) RawBody() []byte {
   132  	return rm.body
   133  }
   134  
   135  func (rm *responseModifier) RawHeaders() ([]byte, error) {
   136  	var b bytes.Buffer
   137  	if err := rm.header.Write(&b); err != nil {
   138  		return nil, err
   139  	}
   140  	return b.Bytes(), nil
   141  }
   142  
   143  // Hijack returns the internal connection of the wrapped http.ResponseWriter
   144  func (rm *responseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) {
   145  
   146  	rm.hijacked = true
   147  	rm.FlushAll()
   148  
   149  	hijacker, ok := rm.rw.(http.Hijacker)
   150  	if !ok {
   151  		return nil, nil, fmt.Errorf("Internal response writer doesn't support the Hijacker interface")
   152  	}
   153  	return hijacker.Hijack()
   154  }
   155  
   156  // CloseNotify uses the internal close notify API of the wrapped http.ResponseWriter
   157  func (rm *responseModifier) CloseNotify() <-chan bool {
   158  	closeNotifier, ok := rm.rw.(http.CloseNotifier)
   159  	if !ok {
   160  		logrus.Errorf("Internal response writer doesn't support the CloseNotifier interface")
   161  		return nil
   162  	}
   163  	return closeNotifier.CloseNotify()
   164  }
   165  
   166  // Flush uses the internal flush API of the wrapped http.ResponseWriter
   167  func (rm *responseModifier) Flush() {
   168  	flusher, ok := rm.rw.(http.Flusher)
   169  	if !ok {
   170  		logrus.Errorf("Internal response writer doesn't support the Flusher interface")
   171  		return
   172  	}
   173  
   174  	rm.FlushAll()
   175  	flusher.Flush()
   176  }
   177  
   178  // FlushAll flushes all data to the HTTP response
   179  func (rm *responseModifier) FlushAll() error {
   180  	// Copy the status code
   181  	if rm.statusCode > 0 {
   182  		rm.rw.WriteHeader(rm.statusCode)
   183  	}
   184  
   185  	// Copy the header
   186  	for k, vv := range rm.header {
   187  		for _, v := range vv {
   188  			rm.rw.Header().Add(k, v)
   189  		}
   190  	}
   191  
   192  	var err error
   193  	if len(rm.body) > 0 {
   194  		// Write body
   195  		_, err = rm.rw.Write(rm.body)
   196  	}
   197  
   198  	// Clean previous data
   199  	rm.body = nil
   200  	rm.statusCode = 0
   201  	rm.header = http.Header{}
   202  	return err
   203  }