github.com/sijibomii/docker@v0.0.0-20231230191044-5cf6ca554647/pkg/authorization/response.go (about)

     1  package 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 replace the body of the HTTP reply
    30  	OverrideBody(b []byte)
    31  
    32  	// OverrideHeader replace 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  	// Flush 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  // responseModifier is used as an adapter to http.ResponseWriter in order to manipulate and explore
    51  // the http request/response from docker daemon
    52  type responseModifier struct {
    53  	// The original response writer
    54  	rw http.ResponseWriter
    55  	// body holds the response body
    56  	body []byte
    57  	// header holds the response header
    58  	header http.Header
    59  	// statusCode holds the response status code
    60  	statusCode int
    61  	// hijacked indicates the request has been hijacked
    62  	hijacked bool
    63  }
    64  
    65  func (rm *responseModifier) Hijacked() bool {
    66  	return rm.hijacked
    67  }
    68  
    69  // WriterHeader stores the http status code
    70  func (rm *responseModifier) WriteHeader(s int) {
    71  
    72  	// Use original request if hijacked
    73  	if rm.hijacked {
    74  		rm.rw.WriteHeader(s)
    75  		return
    76  	}
    77  
    78  	rm.statusCode = s
    79  }
    80  
    81  // Header returns the internal http header
    82  func (rm *responseModifier) Header() http.Header {
    83  
    84  	// Use original header if hijacked
    85  	if rm.hijacked {
    86  		return rm.rw.Header()
    87  	}
    88  
    89  	return rm.header
    90  }
    91  
    92  // Header returns the internal http header
    93  func (rm *responseModifier) StatusCode() int {
    94  	return rm.statusCode
    95  }
    96  
    97  // Override replace the body of the HTTP reply
    98  func (rm *responseModifier) OverrideBody(b []byte) {
    99  	rm.body = b
   100  }
   101  
   102  func (rm *responseModifier) OverrideStatusCode(statusCode int) {
   103  	rm.statusCode = statusCode
   104  }
   105  
   106  // Override replace the headers of the HTTP reply
   107  func (rm *responseModifier) OverrideHeader(b []byte) error {
   108  	header := http.Header{}
   109  	if err := json.Unmarshal(b, &header); err != nil {
   110  		return err
   111  	}
   112  	rm.header = header
   113  	return nil
   114  }
   115  
   116  // Write stores the byte array inside content
   117  func (rm *responseModifier) Write(b []byte) (int, error) {
   118  
   119  	if rm.hijacked {
   120  		return rm.rw.Write(b)
   121  	}
   122  
   123  	rm.body = append(rm.body, b...)
   124  	return len(b), nil
   125  }
   126  
   127  // Body returns the response body
   128  func (rm *responseModifier) RawBody() []byte {
   129  	return rm.body
   130  }
   131  
   132  func (rm *responseModifier) RawHeaders() ([]byte, error) {
   133  	var b bytes.Buffer
   134  	if err := rm.header.Write(&b); err != nil {
   135  		return nil, err
   136  	}
   137  	return b.Bytes(), nil
   138  }
   139  
   140  // Hijack returns the internal connection of the wrapped http.ResponseWriter
   141  func (rm *responseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) {
   142  
   143  	rm.hijacked = true
   144  	rm.FlushAll()
   145  
   146  	hijacker, ok := rm.rw.(http.Hijacker)
   147  	if !ok {
   148  		return nil, nil, fmt.Errorf("Internal response writer doesn't support the Hijacker interface")
   149  	}
   150  	return hijacker.Hijack()
   151  }
   152  
   153  // CloseNotify uses the internal close notify API of the wrapped http.ResponseWriter
   154  func (rm *responseModifier) CloseNotify() <-chan bool {
   155  	closeNotifier, ok := rm.rw.(http.CloseNotifier)
   156  	if !ok {
   157  		logrus.Errorf("Internal response writer doesn't support the CloseNotifier interface")
   158  		return nil
   159  	}
   160  	return closeNotifier.CloseNotify()
   161  }
   162  
   163  // Flush uses the internal flush API of the wrapped http.ResponseWriter
   164  func (rm *responseModifier) Flush() {
   165  	flusher, ok := rm.rw.(http.Flusher)
   166  	if !ok {
   167  		logrus.Errorf("Internal response writer doesn't support the Flusher interface")
   168  		return
   169  	}
   170  
   171  	rm.FlushAll()
   172  	flusher.Flush()
   173  }
   174  
   175  // FlushAll flushes all data to the HTTP response
   176  func (rm *responseModifier) FlushAll() error {
   177  	// Copy the status code
   178  	if rm.statusCode > 0 {
   179  		rm.rw.WriteHeader(rm.statusCode)
   180  	}
   181  
   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  	var err error
   190  	if len(rm.body) > 0 {
   191  		// Write body
   192  		_, err = rm.rw.Write(rm.body)
   193  	}
   194  
   195  	// Clean previous data
   196  	rm.body = nil
   197  	rm.statusCode = 0
   198  	rm.header = http.Header{}
   199  	return err
   200  }