Merge pull request #20002 from twistlock/19575_authz_plugin_support_events

Fix 19575: Docker events doesn't work with authorization plugin
This commit is contained in:
Alexander Morozov 2016-02-08 09:10:39 -08:00
commit 80e2b1b5e2
3 changed files with 76 additions and 9 deletions

View file

@ -116,7 +116,7 @@ func (ctx *Ctx) AuthZResponse(rm ResponseModifier, r *http.Request) error {
} }
} }
rm.Flush() rm.FlushAll()
return nil return nil
} }

View file

@ -118,7 +118,7 @@ func TestResponseModifier(t *testing.T) {
m.Write([]byte("body")) m.Write([]byte("body"))
m.WriteHeader(500) m.WriteHeader(500)
m.Flush() m.FlushAll()
if r.Header().Get("h1") != "v1" { if r.Header().Get("h1") != "v1" {
t.Fatalf("Header value must exists %s", r.Header().Get("h1")) t.Fatalf("Header value must exists %s", r.Header().Get("h1"))
} }
@ -147,7 +147,7 @@ func TestResponseModifierOverride(t *testing.T) {
m.OverrideHeader(overrideHeaderBytes) m.OverrideHeader(overrideHeaderBytes)
m.OverrideBody([]byte("override body")) m.OverrideBody([]byte("override body"))
m.OverrideStatusCode(404) m.OverrideStatusCode(404)
m.Flush() m.FlushAll()
if r.Header().Get("h1") != "v2" { if r.Header().Get("h1") != "v2" {
t.Fatalf("Header value must exists %s", r.Header().Get("h1")) t.Fatalf("Header value must exists %s", r.Header().Get("h1"))
} }

View file

@ -5,6 +5,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/Sirupsen/logrus"
"net" "net"
"net/http" "net/http"
) )
@ -12,6 +13,8 @@ import (
// ResponseModifier allows authorization plugins to read and modify the content of the http.response // ResponseModifier allows authorization plugins to read and modify the content of the http.response
type ResponseModifier interface { type ResponseModifier interface {
http.ResponseWriter http.ResponseWriter
http.Flusher
http.CloseNotifier
// RawBody returns the current http content // RawBody returns the current http content
RawBody() []byte RawBody() []byte
@ -32,7 +35,10 @@ type ResponseModifier interface {
OverrideStatusCode(statusCode int) OverrideStatusCode(statusCode int)
// Flush flushes all data to the HTTP response // Flush flushes all data to the HTTP response
Flush() error 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 // NewResponseModifier creates a wrapper to an http.ResponseWriter to allow inspecting and modifying the content
@ -44,7 +50,10 @@ func NewResponseModifier(rw http.ResponseWriter) ResponseModifier {
// the http request/response from docker daemon // the http request/response from docker daemon
type responseModifier struct { type responseModifier struct {
// The original response writer // The original response writer
rw http.ResponseWriter rw http.ResponseWriter
r *http.Request
status int status int
// body holds the response body // body holds the response body
body []byte body []byte
@ -52,15 +61,34 @@ type responseModifier struct {
header http.Header header http.Header
// statusCode holds the response status code // statusCode holds the response status code
statusCode int 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 // WriterHeader stores the http status code
func (rm *responseModifier) WriteHeader(s int) { func (rm *responseModifier) WriteHeader(s int) {
// Use original request if hijacked
if rm.hijacked {
rm.rw.WriteHeader(s)
return
}
rm.statusCode = s rm.statusCode = s
} }
// Header returns the internal http header // Header returns the internal http header
func (rm *responseModifier) Header() http.Header { func (rm *responseModifier) Header() http.Header {
// Use original header if hijacked
if rm.hijacked {
return rm.rw.Header()
}
return rm.header return rm.header
} }
@ -90,6 +118,11 @@ func (rm *responseModifier) OverrideHeader(b []byte) error {
// Write stores the byte array inside content // Write stores the byte array inside content
func (rm *responseModifier) Write(b []byte) (int, error) { func (rm *responseModifier) Write(b []byte) (int, error) {
if rm.hijacked {
return rm.rw.Write(b)
}
rm.body = append(rm.body, b...) rm.body = append(rm.body, b...)
return len(b), nil return len(b), nil
} }
@ -109,6 +142,10 @@ func (rm *responseModifier) RawHeaders() ([]byte, error) {
// Hijack returns the internal connection of the wrapped http.ResponseWriter // Hijack returns the internal connection of the wrapped http.ResponseWriter
func (rm *responseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) { func (rm *responseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) {
rm.hijacked = true
rm.FlushAll()
hijacker, ok := rm.rw.(http.Hijacker) hijacker, ok := rm.rw.(http.Hijacker)
if !ok { if !ok {
return nil, nil, fmt.Errorf("Internal reponse writer doesn't support the Hijacker interface") return nil, nil, fmt.Errorf("Internal reponse writer doesn't support the Hijacker interface")
@ -116,8 +153,30 @@ func (rm *responseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return hijacker.Hijack() return hijacker.Hijack()
} }
// Flush flushes all data to the HTTP response // CloseNotify uses the internal close notify API of the wrapped http.ResponseWriter
func (rm *responseModifier) Flush() error { func (rm *responseModifier) CloseNotify() <-chan bool {
closeNotifier, ok := rm.rw.(http.CloseNotifier)
if !ok {
logrus.Errorf("Internal reponse 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 reponse 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 // Copy the status code
if rm.statusCode > 0 { if rm.statusCode > 0 {
rm.rw.WriteHeader(rm.statusCode) rm.rw.WriteHeader(rm.statusCode)
@ -130,7 +189,15 @@ func (rm *responseModifier) Flush() error {
} }
} }
// Write body var err error
_, err := rm.rw.Write(rm.body) 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 return err
} }