3f77baf2ce
Signed-off-by: allencloud <allen.sun@daocloud.io>
201 lines
4.7 KiB
Go
201 lines
4.7 KiB
Go
package authorization
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
)
|
|
|
|
// ResponseModifier allows authorization plugins to read and modify the content of the http.response
|
|
type ResponseModifier interface {
|
|
http.ResponseWriter
|
|
http.Flusher
|
|
http.CloseNotifier
|
|
|
|
// RawBody returns the current http content
|
|
RawBody() []byte
|
|
|
|
// RawHeaders returns the current content of the http headers
|
|
RawHeaders() ([]byte, error)
|
|
|
|
// StatusCode returns the current status code
|
|
StatusCode() int
|
|
|
|
// OverrideBody replaces the body of the HTTP reply
|
|
OverrideBody(b []byte)
|
|
|
|
// OverrideHeader replaces the headers of the HTTP reply
|
|
OverrideHeader(b []byte) error
|
|
|
|
// OverrideStatusCode replaces the status code of the HTTP reply
|
|
OverrideStatusCode(statusCode int)
|
|
|
|
// Flush flushes all data to the HTTP response
|
|
FlushAll() error
|
|
|
|
// Hijacked indicates the response has been hijacked by the Docker daemon
|
|
Hijacked() bool
|
|
}
|
|
|
|
// NewResponseModifier creates a wrapper to an http.ResponseWriter to allow inspecting and modifying the content
|
|
func NewResponseModifier(rw http.ResponseWriter) ResponseModifier {
|
|
return &responseModifier{rw: rw, header: make(http.Header)}
|
|
}
|
|
|
|
// responseModifier is used as an adapter to http.ResponseWriter in order to manipulate and explore
|
|
// the http request/response from docker daemon
|
|
type responseModifier struct {
|
|
// The original response writer
|
|
rw http.ResponseWriter
|
|
// body holds the response body
|
|
body []byte
|
|
// header holds the response header
|
|
header http.Header
|
|
// statusCode holds the response status code
|
|
statusCode int
|
|
// hijacked indicates the request has been hijacked
|
|
hijacked bool
|
|
}
|
|
|
|
func (rm *responseModifier) Hijacked() bool {
|
|
return rm.hijacked
|
|
}
|
|
|
|
// WriterHeader stores the http status code
|
|
func (rm *responseModifier) WriteHeader(s int) {
|
|
|
|
// Use original request if hijacked
|
|
if rm.hijacked {
|
|
rm.rw.WriteHeader(s)
|
|
return
|
|
}
|
|
|
|
rm.statusCode = s
|
|
}
|
|
|
|
// Header returns the internal http header
|
|
func (rm *responseModifier) Header() http.Header {
|
|
|
|
// Use original header if hijacked
|
|
if rm.hijacked {
|
|
return rm.rw.Header()
|
|
}
|
|
|
|
return rm.header
|
|
}
|
|
|
|
// StatusCode returns the http status code
|
|
func (rm *responseModifier) StatusCode() int {
|
|
return rm.statusCode
|
|
}
|
|
|
|
// OverrideBody replaces the body of the HTTP response
|
|
func (rm *responseModifier) OverrideBody(b []byte) {
|
|
rm.body = b
|
|
}
|
|
|
|
// OverrideStatusCode replaces the status code of the HTTP response
|
|
func (rm *responseModifier) OverrideStatusCode(statusCode int) {
|
|
rm.statusCode = statusCode
|
|
}
|
|
|
|
// OverrideHeader replaces the headers of the HTTP response
|
|
func (rm *responseModifier) OverrideHeader(b []byte) error {
|
|
header := http.Header{}
|
|
if err := json.Unmarshal(b, &header); err != nil {
|
|
return err
|
|
}
|
|
rm.header = header
|
|
return nil
|
|
}
|
|
|
|
// Write stores the byte array inside content
|
|
func (rm *responseModifier) Write(b []byte) (int, error) {
|
|
|
|
if rm.hijacked {
|
|
return rm.rw.Write(b)
|
|
}
|
|
|
|
rm.body = append(rm.body, b...)
|
|
return len(b), nil
|
|
}
|
|
|
|
// Body returns the response body
|
|
func (rm *responseModifier) RawBody() []byte {
|
|
return rm.body
|
|
}
|
|
|
|
func (rm *responseModifier) RawHeaders() ([]byte, error) {
|
|
var b bytes.Buffer
|
|
if err := rm.header.Write(&b); err != nil {
|
|
return nil, err
|
|
}
|
|
return b.Bytes(), nil
|
|
}
|
|
|
|
// Hijack returns the internal connection of the wrapped http.ResponseWriter
|
|
func (rm *responseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
|
|
|
rm.hijacked = true
|
|
rm.FlushAll()
|
|
|
|
hijacker, ok := rm.rw.(http.Hijacker)
|
|
if !ok {
|
|
return nil, nil, fmt.Errorf("Internal response writer doesn't support the Hijacker interface")
|
|
}
|
|
return hijacker.Hijack()
|
|
}
|
|
|
|
// CloseNotify uses the internal close notify API of the wrapped http.ResponseWriter
|
|
func (rm *responseModifier) CloseNotify() <-chan bool {
|
|
closeNotifier, ok := rm.rw.(http.CloseNotifier)
|
|
if !ok {
|
|
logrus.Errorf("Internal response writer doesn't support the CloseNotifier interface")
|
|
return nil
|
|
}
|
|
return closeNotifier.CloseNotify()
|
|
}
|
|
|
|
// Flush uses the internal flush API of the wrapped http.ResponseWriter
|
|
func (rm *responseModifier) Flush() {
|
|
flusher, ok := rm.rw.(http.Flusher)
|
|
if !ok {
|
|
logrus.Errorf("Internal response writer doesn't support the Flusher interface")
|
|
return
|
|
}
|
|
|
|
rm.FlushAll()
|
|
flusher.Flush()
|
|
}
|
|
|
|
// FlushAll flushes all data to the HTTP response
|
|
func (rm *responseModifier) FlushAll() error {
|
|
// Copy the status code
|
|
if rm.statusCode > 0 {
|
|
rm.rw.WriteHeader(rm.statusCode)
|
|
}
|
|
|
|
// Copy the header
|
|
for k, vv := range rm.header {
|
|
for _, v := range vv {
|
|
rm.rw.Header().Add(k, v)
|
|
}
|
|
}
|
|
|
|
var err error
|
|
if len(rm.body) > 0 {
|
|
// Write body
|
|
_, err = rm.rw.Write(rm.body)
|
|
}
|
|
|
|
// Clean previous data
|
|
rm.body = nil
|
|
rm.statusCode = 0
|
|
rm.header = http.Header{}
|
|
return err
|
|
}
|