Skip to content

Commit 697e66d

Browse files
authored
Merge pull request #1931 from k8s-infra-cherrypick-robot/cherry-pick-1930-to-release-0.12
🐛 Fix webhook write response error for broken HTTP connection
2 parents d15de97 + 0d4500b commit 697e66d

File tree

2 files changed

+37
-2
lines changed

2 files changed

+37
-2
lines changed

pkg/webhook/admission/http.go

+10-2
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,16 @@ func (wh *Webhook) writeResponseTyped(w io.Writer, response Response, admRevGVK
124124
// writeAdmissionResponse writes ar to w.
125125
func (wh *Webhook) writeAdmissionResponse(w io.Writer, ar v1.AdmissionReview) {
126126
if err := json.NewEncoder(w).Encode(ar); err != nil {
127-
wh.log.Error(err, "unable to encode the response")
128-
wh.writeResponse(w, Errored(http.StatusInternalServerError, err))
127+
wh.log.Error(err, "unable to encode and write the response")
128+
// Since the `ar v1.AdmissionReview` is a clear and legal object,
129+
// it should not have problem to be marshalled into bytes.
130+
// The error here is probably caused by the abnormal HTTP connection,
131+
// e.g., broken pipe, so we can only write the error response once,
132+
// to avoid endless circular calling.
133+
serverError := Errored(http.StatusInternalServerError, err)
134+
if err = json.NewEncoder(w).Encode(v1.AdmissionReview{Response: &serverError.AdmissionResponse}); err != nil {
135+
wh.log.Error(err, "still unable to encode and write the InternalServerError response")
136+
}
129137
} else {
130138
res := ar.Response
131139
if log := wh.log; log.V(1).Enabled() {

pkg/webhook/admission/http_test.go

+27
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"io"
2424
"net/http"
2525
"net/http/httptest"
26+
"time"
2627

2728
. "github.com/onsi/ginkgo"
2829
. "github.com/onsi/gomega"
@@ -190,6 +191,24 @@ var _ = Describe("Admission Webhooks", func() {
190191
webhook.ServeHTTP(respRecorder, req.WithContext(ctx))
191192
Expect(respRecorder.Body.String()).To(Equal(expected))
192193
})
194+
195+
It("should never run into circular calling if the writer has broken", func() {
196+
req := &http.Request{
197+
Header: http.Header{"Content-Type": []string{"application/json"}},
198+
Body: nopCloser{Reader: bytes.NewBufferString(fmt.Sprintf(`{%s,"request":{}}`, gvkJSONv1))},
199+
}
200+
webhook := &Webhook{
201+
Handler: &fakeHandler{},
202+
log: logf.RuntimeLog.WithName("webhook"),
203+
}
204+
205+
bw := &brokenWriter{ResponseWriter: respRecorder}
206+
Eventually(func() int {
207+
// This should not be blocked by the circular calling of writeResponse and writeAdmissionResponse
208+
webhook.ServeHTTP(bw, req)
209+
return respRecorder.Body.Len()
210+
}, time.Second*3).Should(Equal(0))
211+
})
193212
})
194213
})
195214

@@ -225,3 +244,11 @@ func (h *fakeHandler) Handle(ctx context.Context, req Request) Response {
225244
Allowed: true,
226245
}}
227246
}
247+
248+
type brokenWriter struct {
249+
http.ResponseWriter
250+
}
251+
252+
func (bw *brokenWriter) Write(buf []byte) (int, error) {
253+
return 0, fmt.Errorf("mock: write: broken pipe")
254+
}

0 commit comments

Comments
 (0)