Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions lib/httplib/httplib.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,51 @@ func SafeRedirect(w http.ResponseWriter, r *http.Request, redirectURL string) er
http.Redirect(w, r, parsedURL.RequestURI(), http.StatusFound)
return nil
}

// ResponseStatusRecorder is an http.ResponseWriter that records the response status code.
type ResponseStatusRecorder struct {
http.ResponseWriter
flusher http.Flusher
status int
}

// NewResponseStatusRecorder makes and returns a ResponseStatusRecorder.
func NewResponseStatusRecorder(w http.ResponseWriter) *ResponseStatusRecorder {
rec := &ResponseStatusRecorder{ResponseWriter: w}
if flusher, ok := w.(http.Flusher); ok {
rec.flusher = flusher
}
return rec
}

// WriteHeader sends an HTTP response header with the provided
// status code and save the status code in the recorder.
func (r *ResponseStatusRecorder) WriteHeader(status int) {
r.status = status
r.ResponseWriter.WriteHeader(status)
}

// Flush optionally flushes the inner ResponseWriter if it supports that.
// Otherwise, Flush is a noop.
//
// Flush is optionally used by github.com/gravitational/oxy/forward to flush
// pending data on streaming HTTP responses (like streaming pod logs).
//
// Without this, oxy/forward will handle streaming responses by accumulating
// ~32kb of response in a buffer before flushing it.
func (r *ResponseStatusRecorder) Flush() {
if r.flusher != nil {
r.flusher.Flush()
}
}

// Status returns the recorded status after WriteHeader is called, or StatusOK if WriteHeader hasn't been called
// explicitly.
func (r *ResponseStatusRecorder) Status() int {
// http.ResponseWriter implicitly sets StatusOK, if WriteHeader hasn't been
// explicitly called.
if r.status == 0 {
return http.StatusOK
}
return r.status
}
46 changes: 2 additions & 44 deletions lib/kube/proxy/forwarder.go
Original file line number Diff line number Diff line change
Expand Up @@ -1552,7 +1552,7 @@ func (f *Forwarder) catchAll(ctx *authContext, w http.ResponseWriter, req *http.
f.log.Errorf("Failed to set up forwarding headers: %v.", err)
return nil, trace.Wrap(err)
}
rw := newResponseStatusRecorder(w)
rw := httplib.NewResponseStatusRecorder(w)
sess.forwarder.ServeHTTP(rw, req)

if sess.noAuditEvents {
Expand All @@ -1576,7 +1576,7 @@ func (f *Forwarder) catchAll(ctx *authContext, w http.ResponseWriter, req *http.
},
RequestPath: req.URL.Path,
Verb: req.Method,
ResponseCode: int32(rw.getStatus()),
ResponseCode: int32(rw.Status()),
KubernetesClusterMetadata: ctx.eventClusterMeta(),
}
r := parseResourcePath(req.URL.Path)
Expand Down Expand Up @@ -2108,45 +2108,3 @@ func (f *Forwarder) removeKubeDetails(name string) {
}
delete(f.clusterDetails, name)
}

type responseStatusRecorder struct {
http.ResponseWriter
flusher http.Flusher
status int
}

func newResponseStatusRecorder(w http.ResponseWriter) *responseStatusRecorder {
rec := &responseStatusRecorder{ResponseWriter: w}
if flusher, ok := w.(http.Flusher); ok {
rec.flusher = flusher
}
return rec
}

func (r *responseStatusRecorder) WriteHeader(status int) {
r.status = status
r.ResponseWriter.WriteHeader(status)
}

// Flush optionally flushes the inner ResponseWriter if it supports that.
// Otherwise, Flush is a noop.
//
// Flush is optionally used by github.com/gravitational/oxy/forward to flush
// pending data on streaming HTTP responses (like streaming pod logs).
//
// Without this, oxy/forward will handle streaming responses by accumulating
// ~32kb of response in a buffer before flushing it.
func (r *responseStatusRecorder) Flush() {
if r.flusher != nil {
r.flusher.Flush()
}
}

func (r *responseStatusRecorder) getStatus() int {
// http.ResponseWriter implicitly sets StatusOK, if WriteHeader hasn't been
// explicitly called.
if r.status == 0 {
return http.StatusOK
}
return r.status
}
Loading