github.com/hustcat/docker@v1.3.3-0.20160314103604-901c67a8eeab/pkg/authorization/response.go (about) 1 package authorization 2 3 import ( 4 "bufio" 5 "bytes" 6 "encoding/json" 7 "fmt" 8 "github.com/Sirupsen/logrus" 9 "net" 10 "net/http" 11 ) 12 13 // ResponseModifier allows authorization plugins to read and modify the content of the http.response 14 type ResponseModifier interface { 15 http.ResponseWriter 16 http.Flusher 17 http.CloseNotifier 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 replace the body of the HTTP reply 29 OverrideBody(b []byte) 30 31 // OverrideHeader replace 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 // Flush 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 // responseModifier is used as an adapter to http.ResponseWriter in order to manipulate and explore 50 // the http request/response from docker daemon 51 type responseModifier struct { 52 // The original response writer 53 rw http.ResponseWriter 54 55 r *http.Request 56 57 status int 58 // body holds the response body 59 body []byte 60 // header holds the response header 61 header http.Header 62 // statusCode holds the response status code 63 statusCode int 64 // hijacked indicates the request has been hijacked 65 hijacked bool 66 } 67 68 func (rm *responseModifier) Hijacked() bool { 69 return rm.hijacked 70 } 71 72 // WriterHeader stores the http status code 73 func (rm *responseModifier) WriteHeader(s int) { 74 75 // Use original request if hijacked 76 if rm.hijacked { 77 rm.rw.WriteHeader(s) 78 return 79 } 80 81 rm.statusCode = s 82 } 83 84 // Header returns the internal http header 85 func (rm *responseModifier) Header() http.Header { 86 87 // Use original header if hijacked 88 if rm.hijacked { 89 return rm.rw.Header() 90 } 91 92 return rm.header 93 } 94 95 // Header returns the internal http header 96 func (rm *responseModifier) StatusCode() int { 97 return rm.statusCode 98 } 99 100 // Override replace the body of the HTTP reply 101 func (rm *responseModifier) OverrideBody(b []byte) { 102 rm.body = b 103 } 104 105 func (rm *responseModifier) OverrideStatusCode(statusCode int) { 106 rm.statusCode = statusCode 107 } 108 109 // Override replace the headers of the HTTP reply 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 122 if rm.hijacked { 123 return rm.rw.Write(b) 124 } 125 126 rm.body = append(rm.body, b...) 127 return len(b), nil 128 } 129 130 // Body returns the response body 131 func (rm *responseModifier) RawBody() []byte { 132 return rm.body 133 } 134 135 func (rm *responseModifier) RawHeaders() ([]byte, error) { 136 var b bytes.Buffer 137 if err := rm.header.Write(&b); err != nil { 138 return nil, err 139 } 140 return b.Bytes(), nil 141 } 142 143 // Hijack returns the internal connection of the wrapped http.ResponseWriter 144 func (rm *responseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) { 145 146 rm.hijacked = true 147 rm.FlushAll() 148 149 hijacker, ok := rm.rw.(http.Hijacker) 150 if !ok { 151 return nil, nil, fmt.Errorf("Internal response writer doesn't support the Hijacker interface") 152 } 153 return hijacker.Hijack() 154 } 155 156 // CloseNotify uses the internal close notify API of the wrapped http.ResponseWriter 157 func (rm *responseModifier) CloseNotify() <-chan bool { 158 closeNotifier, ok := rm.rw.(http.CloseNotifier) 159 if !ok { 160 logrus.Errorf("Internal response writer doesn't support the CloseNotifier interface") 161 return nil 162 } 163 return closeNotifier.CloseNotify() 164 } 165 166 // Flush uses the internal flush API of the wrapped http.ResponseWriter 167 func (rm *responseModifier) Flush() { 168 flusher, ok := rm.rw.(http.Flusher) 169 if !ok { 170 logrus.Errorf("Internal response writer doesn't support the Flusher interface") 171 return 172 } 173 174 rm.FlushAll() 175 flusher.Flush() 176 } 177 178 // FlushAll flushes all data to the HTTP response 179 func (rm *responseModifier) FlushAll() error { 180 // Copy the status code 181 if rm.statusCode > 0 { 182 rm.rw.WriteHeader(rm.statusCode) 183 } 184 185 // Copy the header 186 for k, vv := range rm.header { 187 for _, v := range vv { 188 rm.rw.Header().Add(k, v) 189 } 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 }