github.com/rawahars/moby@v24.0.4+incompatible/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 19 // RawBody returns the current http content 20 RawBody() []byte 21 22 // RawHeaders returns the current content of the http headers 23 RawHeaders() ([]byte, error) 24 25 // StatusCode returns the current status code 26 StatusCode() int 27 28 // OverrideBody replaces the body of the HTTP reply 29 OverrideBody(b []byte) 30 31 // OverrideHeader replaces the headers of the HTTP reply 32 OverrideHeader(b []byte) error 33 34 // OverrideStatusCode replaces the status code of the HTTP reply 35 OverrideStatusCode(statusCode int) 36 37 // FlushAll flushes all data to the HTTP response 38 FlushAll() error 39 40 // Hijacked indicates the response has been hijacked by the Docker daemon 41 Hijacked() bool 42 } 43 44 // NewResponseModifier creates a wrapper to an http.ResponseWriter to allow inspecting and modifying the content 45 func NewResponseModifier(rw http.ResponseWriter) ResponseModifier { 46 return &responseModifier{rw: rw, header: make(http.Header)} 47 } 48 49 const maxBufferSize = 64 * 1024 50 51 // responseModifier is used as an adapter to http.ResponseWriter in order to manipulate and explore 52 // the http request/response from docker daemon 53 type responseModifier struct { 54 // The original response writer 55 rw http.ResponseWriter 56 // body holds the response body 57 body []byte 58 // header holds the response header 59 header http.Header 60 // statusCode holds the response status code 61 statusCode int 62 // hijacked indicates the request has been hijacked 63 hijacked bool 64 } 65 66 func (rm *responseModifier) Hijacked() bool { 67 return rm.hijacked 68 } 69 70 // WriteHeader stores the http status code 71 func (rm *responseModifier) WriteHeader(s int) { 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 // Use original header if hijacked 84 if rm.hijacked { 85 return rm.rw.Header() 86 } 87 88 return rm.header 89 } 90 91 // StatusCode returns the http status code 92 func (rm *responseModifier) StatusCode() int { 93 return rm.statusCode 94 } 95 96 // OverrideBody replaces the body of the HTTP response 97 func (rm *responseModifier) OverrideBody(b []byte) { 98 rm.body = b 99 } 100 101 // OverrideStatusCode replaces the status code of the HTTP response 102 func (rm *responseModifier) OverrideStatusCode(statusCode int) { 103 rm.statusCode = statusCode 104 } 105 106 // OverrideHeader replaces the headers of the HTTP response 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 if rm.hijacked { 119 return rm.rw.Write(b) 120 } 121 122 if len(rm.body)+len(b) > maxBufferSize { 123 rm.Flush() 124 } 125 rm.body = append(rm.body, b...) 126 return len(b), nil 127 } 128 129 // Body returns the response body 130 func (rm *responseModifier) RawBody() []byte { 131 return rm.body 132 } 133 134 func (rm *responseModifier) RawHeaders() ([]byte, error) { 135 var b bytes.Buffer 136 if err := rm.header.Write(&b); err != nil { 137 return nil, err 138 } 139 return b.Bytes(), nil 140 } 141 142 // Hijack returns the internal connection of the wrapped http.ResponseWriter 143 func (rm *responseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) { 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 // Flush uses the internal flush API of the wrapped http.ResponseWriter 155 func (rm *responseModifier) Flush() { 156 flusher, ok := rm.rw.(http.Flusher) 157 if !ok { 158 logrus.Error("Internal response writer doesn't support the Flusher interface") 159 return 160 } 161 162 rm.FlushAll() 163 flusher.Flush() 164 } 165 166 // FlushAll flushes all data to the HTTP response 167 func (rm *responseModifier) FlushAll() error { 168 // Copy the header 169 for k, vv := range rm.header { 170 for _, v := range vv { 171 rm.rw.Header().Add(k, v) 172 } 173 } 174 175 // Copy the status code 176 // Also WriteHeader needs to be done after all the headers 177 // have been copied (above). 178 if rm.statusCode > 0 { 179 rm.rw.WriteHeader(rm.statusCode) 180 } 181 182 var err error 183 if len(rm.body) > 0 { 184 // Write body 185 var n int 186 n, err = rm.rw.Write(rm.body) 187 // TODO(@cpuguy83): there is now a relatively small buffer limit, instead of discarding our buffer here and 188 // allocating again later this should just keep using the same buffer and track the buffer position (like a bytes.Buffer with a fixed size) 189 rm.body = rm.body[n:] 190 } 191 192 // Clean previous data 193 rm.statusCode = 0 194 rm.header = http.Header{} 195 return err 196 }