123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- package authorization
- import (
- "bufio"
- "bytes"
- "encoding/json"
- "fmt"
- "net"
- "net/http"
- "github.com/Sirupsen/logrus"
- )
- // ResponseModifier allows authorization plugins to read and modify the content of the http.response
- type ResponseModifier interface {
- http.ResponseWriter
- http.Flusher
- http.CloseNotifier
- // RawBody returns the current http content
- RawBody() []byte
- // RawHeaders returns the current content of the http headers
- RawHeaders() ([]byte, error)
- // StatusCode returns the current status code
- StatusCode() int
- // OverrideBody replaces the body of the HTTP reply
- OverrideBody(b []byte)
- // OverrideHeader replaces the headers of the HTTP reply
- OverrideHeader(b []byte) error
- // OverrideStatusCode replaces the status code of the HTTP reply
- OverrideStatusCode(statusCode int)
- // FlushAll flushes all data to the HTTP response
- FlushAll() error
- // Hijacked indicates the response has been hijacked by the Docker daemon
- Hijacked() bool
- }
- // NewResponseModifier creates a wrapper to an http.ResponseWriter to allow inspecting and modifying the content
- func NewResponseModifier(rw http.ResponseWriter) ResponseModifier {
- return &responseModifier{rw: rw, header: make(http.Header)}
- }
- // responseModifier is used as an adapter to http.ResponseWriter in order to manipulate and explore
- // the http request/response from docker daemon
- type responseModifier struct {
- // The original response writer
- rw http.ResponseWriter
- // body holds the response body
- body []byte
- // header holds the response header
- header http.Header
- // statusCode holds the response status code
- statusCode int
- // hijacked indicates the request has been hijacked
- hijacked bool
- }
- func (rm *responseModifier) Hijacked() bool {
- return rm.hijacked
- }
- // WriterHeader stores the http status code
- func (rm *responseModifier) WriteHeader(s int) {
- // Use original request if hijacked
- if rm.hijacked {
- rm.rw.WriteHeader(s)
- return
- }
- rm.statusCode = s
- }
- // Header returns the internal http header
- func (rm *responseModifier) Header() http.Header {
- // Use original header if hijacked
- if rm.hijacked {
- return rm.rw.Header()
- }
- return rm.header
- }
- // StatusCode returns the http status code
- func (rm *responseModifier) StatusCode() int {
- return rm.statusCode
- }
- // OverrideBody replaces the body of the HTTP response
- func (rm *responseModifier) OverrideBody(b []byte) {
- rm.body = b
- }
- // OverrideStatusCode replaces the status code of the HTTP response
- func (rm *responseModifier) OverrideStatusCode(statusCode int) {
- rm.statusCode = statusCode
- }
- // OverrideHeader replaces the headers of the HTTP response
- func (rm *responseModifier) OverrideHeader(b []byte) error {
- header := http.Header{}
- if err := json.Unmarshal(b, &header); err != nil {
- return err
- }
- rm.header = header
- return nil
- }
- // Write stores the byte array inside content
- func (rm *responseModifier) Write(b []byte) (int, error) {
- if rm.hijacked {
- return rm.rw.Write(b)
- }
- rm.body = append(rm.body, b...)
- return len(b), nil
- }
- // Body returns the response body
- func (rm *responseModifier) RawBody() []byte {
- return rm.body
- }
- func (rm *responseModifier) RawHeaders() ([]byte, error) {
- var b bytes.Buffer
- if err := rm.header.Write(&b); err != nil {
- return nil, err
- }
- return b.Bytes(), nil
- }
- // Hijack returns the internal connection of the wrapped http.ResponseWriter
- func (rm *responseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) {
- rm.hijacked = true
- rm.FlushAll()
- hijacker, ok := rm.rw.(http.Hijacker)
- if !ok {
- return nil, nil, fmt.Errorf("Internal response writer doesn't support the Hijacker interface")
- }
- return hijacker.Hijack()
- }
- // CloseNotify uses the internal close notify API of the wrapped http.ResponseWriter
- func (rm *responseModifier) CloseNotify() <-chan bool {
- closeNotifier, ok := rm.rw.(http.CloseNotifier)
- if !ok {
- logrus.Error("Internal response writer doesn't support the CloseNotifier interface")
- return nil
- }
- return closeNotifier.CloseNotify()
- }
- // Flush uses the internal flush API of the wrapped http.ResponseWriter
- func (rm *responseModifier) Flush() {
- flusher, ok := rm.rw.(http.Flusher)
- if !ok {
- logrus.Error("Internal response writer doesn't support the Flusher interface")
- return
- }
- rm.FlushAll()
- flusher.Flush()
- }
- // FlushAll flushes all data to the HTTP response
- func (rm *responseModifier) FlushAll() error {
- // Copy the header
- for k, vv := range rm.header {
- for _, v := range vv {
- rm.rw.Header().Add(k, v)
- }
- }
- // Copy the status code
- // Also WriteHeader needs to be done after all the headers
- // have been copied (above).
- if rm.statusCode > 0 {
- rm.rw.WriteHeader(rm.statusCode)
- }
- var err error
- if len(rm.body) > 0 {
- // Write body
- _, err = rm.rw.Write(rm.body)
- }
- // Clean previous data
- rm.body = nil
- rm.statusCode = 0
- rm.header = http.Header{}
- return err
- }
|