github.com/ttys3/engine@v17.12.1-ce-rc2+incompatible/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 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 // 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 // StatusCode returns the http status code 93 func (rm *responseModifier) StatusCode() int { 94 return rm.statusCode 95 } 96 97 // OverrideBody replaces the body of the HTTP response 98 func (rm *responseModifier) OverrideBody(b []byte) { 99 rm.body = b 100 } 101 102 // OverrideStatusCode replaces the status code of the HTTP response 103 func (rm *responseModifier) OverrideStatusCode(statusCode int) { 104 rm.statusCode = statusCode 105 } 106 107 // OverrideHeader replaces the headers of the HTTP response 108 func (rm *responseModifier) OverrideHeader(b []byte) error { 109 header := http.Header{} 110 if err := json.Unmarshal(b, &header); err != nil { 111 return err 112 } 113 rm.header = header 114 return nil 115 } 116 117 // Write stores the byte array inside content 118 func (rm *responseModifier) Write(b []byte) (int, error) { 119 120 if rm.hijacked { 121 return rm.rw.Write(b) 122 } 123 124 rm.body = append(rm.body, b...) 125 return len(b), nil 126 } 127 128 // Body returns the response body 129 func (rm *responseModifier) RawBody() []byte { 130 return rm.body 131 } 132 133 func (rm *responseModifier) RawHeaders() ([]byte, error) { 134 var b bytes.Buffer 135 if err := rm.header.Write(&b); err != nil { 136 return nil, err 137 } 138 return b.Bytes(), nil 139 } 140 141 // Hijack returns the internal connection of the wrapped http.ResponseWriter 142 func (rm *responseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) { 143 144 rm.hijacked = true 145 rm.FlushAll() 146 147 hijacker, ok := rm.rw.(http.Hijacker) 148 if !ok { 149 return nil, nil, fmt.Errorf("Internal response writer doesn't support the Hijacker interface") 150 } 151 return hijacker.Hijack() 152 } 153 154 // CloseNotify uses the internal close notify API of the wrapped http.ResponseWriter 155 func (rm *responseModifier) CloseNotify() <-chan bool { 156 closeNotifier, ok := rm.rw.(http.CloseNotifier) 157 if !ok { 158 logrus.Error("Internal response writer doesn't support the CloseNotifier interface") 159 return nil 160 } 161 return closeNotifier.CloseNotify() 162 } 163 164 // Flush uses the internal flush API of the wrapped http.ResponseWriter 165 func (rm *responseModifier) Flush() { 166 flusher, ok := rm.rw.(http.Flusher) 167 if !ok { 168 logrus.Error("Internal response writer doesn't support the Flusher interface") 169 return 170 } 171 172 rm.FlushAll() 173 flusher.Flush() 174 } 175 176 // FlushAll flushes all data to the HTTP response 177 func (rm *responseModifier) FlushAll() error { 178 // Copy the header 179 for k, vv := range rm.header { 180 for _, v := range vv { 181 rm.rw.Header().Add(k, v) 182 } 183 } 184 185 // Copy the status code 186 // Also WriteHeader needs to be done after all the headers 187 // have been copied (above). 188 if rm.statusCode > 0 { 189 rm.rw.WriteHeader(rm.statusCode) 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 }