response.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. package authorization // import "github.com/docker/docker/pkg/authorization"
  2. import (
  3. "bufio"
  4. "bytes"
  5. "context"
  6. "encoding/json"
  7. "fmt"
  8. "net"
  9. "net/http"
  10. "github.com/containerd/log"
  11. )
  12. // ResponseModifier allows authorization plugins to read and modify the content of the http.response
  13. type ResponseModifier interface {
  14. http.ResponseWriter
  15. http.Flusher
  16. // RawBody returns the current http content
  17. RawBody() []byte
  18. // RawHeaders returns the current content of the http headers
  19. RawHeaders() ([]byte, error)
  20. // StatusCode returns the current status code
  21. StatusCode() int
  22. // OverrideBody replaces the body of the HTTP reply
  23. OverrideBody(b []byte)
  24. // OverrideHeader replaces the headers of the HTTP reply
  25. OverrideHeader(b []byte) error
  26. // OverrideStatusCode replaces the status code of the HTTP reply
  27. OverrideStatusCode(statusCode int)
  28. // FlushAll flushes all data to the HTTP response
  29. FlushAll() error
  30. // Hijacked indicates the response has been hijacked by the Docker daemon
  31. Hijacked() bool
  32. }
  33. // NewResponseModifier creates a wrapper to an http.ResponseWriter to allow inspecting and modifying the content
  34. func NewResponseModifier(rw http.ResponseWriter) ResponseModifier {
  35. return &responseModifier{rw: rw, header: make(http.Header)}
  36. }
  37. const maxBufferSize = 64 * 1024
  38. // responseModifier is used as an adapter to http.ResponseWriter in order to manipulate and explore
  39. // the http request/response from docker daemon
  40. type responseModifier struct {
  41. // The original response writer
  42. rw http.ResponseWriter
  43. // body holds the response body
  44. body []byte
  45. // header holds the response header
  46. header http.Header
  47. // statusCode holds the response status code
  48. statusCode int
  49. // hijacked indicates the request has been hijacked
  50. hijacked bool
  51. }
  52. func (rm *responseModifier) Hijacked() bool {
  53. return rm.hijacked
  54. }
  55. // WriteHeader stores the http status code
  56. func (rm *responseModifier) WriteHeader(s int) {
  57. // Use original request if hijacked
  58. if rm.hijacked {
  59. rm.rw.WriteHeader(s)
  60. return
  61. }
  62. rm.statusCode = s
  63. }
  64. // Header returns the internal http header
  65. func (rm *responseModifier) Header() http.Header {
  66. // Use original header if hijacked
  67. if rm.hijacked {
  68. return rm.rw.Header()
  69. }
  70. return rm.header
  71. }
  72. // StatusCode returns the http status code
  73. func (rm *responseModifier) StatusCode() int {
  74. return rm.statusCode
  75. }
  76. // OverrideBody replaces the body of the HTTP response
  77. func (rm *responseModifier) OverrideBody(b []byte) {
  78. rm.body = b
  79. }
  80. // OverrideStatusCode replaces the status code of the HTTP response
  81. func (rm *responseModifier) OverrideStatusCode(statusCode int) {
  82. rm.statusCode = statusCode
  83. }
  84. // OverrideHeader replaces the headers of the HTTP response
  85. func (rm *responseModifier) OverrideHeader(b []byte) error {
  86. header := http.Header{}
  87. if err := json.Unmarshal(b, &header); err != nil {
  88. return err
  89. }
  90. rm.header = header
  91. return nil
  92. }
  93. // Write stores the byte array inside content
  94. func (rm *responseModifier) Write(b []byte) (int, error) {
  95. if rm.hijacked {
  96. return rm.rw.Write(b)
  97. }
  98. if len(rm.body)+len(b) > maxBufferSize {
  99. rm.Flush()
  100. }
  101. rm.body = append(rm.body, b...)
  102. return len(b), nil
  103. }
  104. // Body returns the response body
  105. func (rm *responseModifier) RawBody() []byte {
  106. return rm.body
  107. }
  108. func (rm *responseModifier) RawHeaders() ([]byte, error) {
  109. var b bytes.Buffer
  110. if err := rm.header.Write(&b); err != nil {
  111. return nil, err
  112. }
  113. return b.Bytes(), nil
  114. }
  115. // Hijack returns the internal connection of the wrapped http.ResponseWriter
  116. func (rm *responseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) {
  117. rm.hijacked = true
  118. rm.FlushAll()
  119. hijacker, ok := rm.rw.(http.Hijacker)
  120. if !ok {
  121. return nil, nil, fmt.Errorf("Internal response writer doesn't support the Hijacker interface")
  122. }
  123. return hijacker.Hijack()
  124. }
  125. // Flush uses the internal flush API of the wrapped http.ResponseWriter
  126. func (rm *responseModifier) Flush() {
  127. flusher, ok := rm.rw.(http.Flusher)
  128. if !ok {
  129. log.G(context.TODO()).Error("Internal response writer doesn't support the Flusher interface")
  130. return
  131. }
  132. rm.FlushAll()
  133. flusher.Flush()
  134. }
  135. // FlushAll flushes all data to the HTTP response
  136. func (rm *responseModifier) FlushAll() error {
  137. // Copy the header
  138. for k, vv := range rm.header {
  139. for _, v := range vv {
  140. rm.rw.Header().Add(k, v)
  141. }
  142. }
  143. // Copy the status code
  144. // Also WriteHeader needs to be done after all the headers
  145. // have been copied (above).
  146. if rm.statusCode > 0 {
  147. rm.rw.WriteHeader(rm.statusCode)
  148. }
  149. var err error
  150. if len(rm.body) > 0 {
  151. // Write body
  152. var n int
  153. n, err = rm.rw.Write(rm.body)
  154. // TODO(@cpuguy83): there is now a relatively small buffer limit, instead of discarding our buffer here and
  155. // allocating again later this should just keep using the same buffer and track the buffer position (like a bytes.Buffer with a fixed size)
  156. rm.body = rm.body[n:]
  157. }
  158. // Clean previous data
  159. rm.statusCode = 0
  160. rm.header = http.Header{}
  161. return err
  162. }