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 }