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 }