Skip to content

Commit bcaa8aa

Browse files
authored
encode: write status immediate for success response for CONNECT requests (#6738)
* encode: write status immediate for success response for CONNECT requests * fix compile * fix test * fix lint * treat first write and flush for encode response writer to CONNECT request as success if status is not set explicitly
1 parent d0e209e commit bcaa8aa

File tree

2 files changed

+27
-5
lines changed

2 files changed

+27
-5
lines changed

modules/caddyhttp/encode/encode.go

+26-4
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ func (enc *Encode) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyh
156156
if _, ok := enc.writerPools[encName]; !ok {
157157
continue // encoding not offered
158158
}
159-
w = enc.openResponseWriter(encName, w)
159+
w = enc.openResponseWriter(encName, w, r.Method == http.MethodConnect)
160160
defer w.(*responseWriter).Close()
161161

162162
// to comply with RFC 9110 section 8.8.3(.3), we modify the Etag when encoding
@@ -201,21 +201,22 @@ func (enc *Encode) addEncoding(e Encoding) error {
201201
// openResponseWriter creates a new response writer that may (or may not)
202202
// encode the response with encodingName. The returned response writer MUST
203203
// be closed after the handler completes.
204-
func (enc *Encode) openResponseWriter(encodingName string, w http.ResponseWriter) *responseWriter {
204+
func (enc *Encode) openResponseWriter(encodingName string, w http.ResponseWriter, isConnect bool) *responseWriter {
205205
var rw responseWriter
206-
return enc.initResponseWriter(&rw, encodingName, w)
206+
return enc.initResponseWriter(&rw, encodingName, w, isConnect)
207207
}
208208

209209
// initResponseWriter initializes the responseWriter instance
210210
// allocated in openResponseWriter, enabling mid-stack inlining.
211-
func (enc *Encode) initResponseWriter(rw *responseWriter, encodingName string, wrappedRW http.ResponseWriter) *responseWriter {
211+
func (enc *Encode) initResponseWriter(rw *responseWriter, encodingName string, wrappedRW http.ResponseWriter, isConnect bool) *responseWriter {
212212
if rww, ok := wrappedRW.(*caddyhttp.ResponseWriterWrapper); ok {
213213
rw.ResponseWriter = rww
214214
} else {
215215
rw.ResponseWriter = &caddyhttp.ResponseWriterWrapper{ResponseWriter: wrappedRW}
216216
}
217217
rw.encodingName = encodingName
218218
rw.config = enc
219+
rw.isConnect = isConnect
219220

220221
return rw
221222
}
@@ -230,6 +231,7 @@ type responseWriter struct {
230231
config *Encode
231232
statusCode int
232233
wroteHeader bool
234+
isConnect bool
233235
}
234236

235237
// WriteHeader stores the status to write when the time comes
@@ -245,6 +247,14 @@ func (rw *responseWriter) WriteHeader(status int) {
245247
rw.Header().Add("Vary", "Accept-Encoding")
246248
}
247249

250+
// write status immediately if status is 2xx and the request is CONNECT
251+
// since it means the response is successful.
252+
// see: https://github.com/caddyserver/caddy/issues/6733#issuecomment-2525058845
253+
if rw.isConnect && 200 <= status && status <= 299 {
254+
rw.ResponseWriter.WriteHeader(status)
255+
rw.wroteHeader = true
256+
}
257+
248258
// write status immediately when status code is informational
249259
// see: https://caddy.community/t/disappear-103-early-hints-response-with-encode-enable-caddy-v2-7-6/23081/5
250260
if 100 <= status && status <= 199 {
@@ -260,6 +270,12 @@ func (enc *Encode) Match(rw *responseWriter) bool {
260270
// FlushError is an alternative Flush returning an error. It delays the actual Flush of the underlying
261271
// ResponseWriterWrapper until headers were written.
262272
func (rw *responseWriter) FlushError() error {
273+
// WriteHeader wasn't called and is a CONNECT request, treat it as a success.
274+
// otherwise, wait until header is written.
275+
if rw.isConnect && !rw.wroteHeader && rw.statusCode == 0 {
276+
rw.WriteHeader(http.StatusOK)
277+
}
278+
263279
if !rw.wroteHeader {
264280
// flushing the underlying ResponseWriter will write header and status code,
265281
// but we need to delay that until we can determine if we must encode and
@@ -288,6 +304,12 @@ func (rw *responseWriter) Write(p []byte) (int, error) {
288304
return 0, nil
289305
}
290306

307+
// WriteHeader wasn't called and is a CONNECT request, treat it as a success.
308+
// otherwise, determine if the response should be compressed.
309+
if rw.isConnect && !rw.wroteHeader && rw.statusCode == 0 {
310+
rw.WriteHeader(http.StatusOK)
311+
}
312+
291313
// sniff content-type and determine content-length
292314
if !rw.wroteHeader && rw.config.MinLength > 0 {
293315
var gtMinLength bool

modules/caddyhttp/encode/encode_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
func BenchmarkOpenResponseWriter(b *testing.B) {
1010
enc := new(Encode)
1111
for n := 0; n < b.N; n++ {
12-
enc.openResponseWriter("test", nil)
12+
enc.openResponseWriter("test", nil, false)
1313
}
1414
}
1515

0 commit comments

Comments
 (0)