github.com/rita33cool1/iot-system-gateway@v0.0.0-20200911033302-e65bde238cc5/docker-engine/pkg/authorization/response.go (about)

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