@marcel91200 / caddy Mirror · read-only / commits / bcaa8aaf
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 12, 04:30 UTC

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

WeidiDeng committed Dec 12, 2024 at 02:15 UTC bcaa8aaf114629031d10dcef0ca7680ae8163e32
2 files changed +27 -5
modules/caddyhttp/encode/encode.go
+26 -4
@@ -156,7 +156,7 @@ func (enc *Encode) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyh
156 if _, ok := enc.writerPools[encName]; !ok {
157 continue // encoding not offered
158 }
159 - w = enc.openResponseWriter(encName, w)
159 + w = enc.openResponseWriter(encName, w, r.Method == http.MethodConnect)
160 defer w.(*responseWriter).Close()
161
162 // to comply with RFC 9110 section 8.8.3(.3), we modify the Etag when encoding
@@ -201,14 +201,14 @@ func (enc *Encode) addEncoding(e Encoding) error {
201 // openResponseWriter creates a new response writer that may (or may not)
202 // encode the response with encodingName. The returned response writer MUST
203 // 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 {
205 var rw responseWriter
206 - return enc.initResponseWriter(&rw, encodingName, w)
206 + return enc.initResponseWriter(&rw, encodingName, w, isConnect)
207 }
208
209 // initResponseWriter initializes the responseWriter instance
210 // 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 {
212 if rww, ok := wrappedRW.(*caddyhttp.ResponseWriterWrapper); ok {
213 rw.ResponseWriter = rww
214 } else {
@@ -216,6 +216,7 @@ func (enc *Encode) initResponseWriter(rw *responseWriter, encodingName string, w
216 }
217 rw.encodingName = encodingName
218 rw.config = enc
219 + rw.isConnect = isConnect
220
221 return rw
222 }
@@ -230,6 +231,7 @@ type responseWriter struct {
231 config *Encode
232 statusCode int
233 wroteHeader bool
234 + isConnect bool
235 }
236
237 // WriteHeader stores the status to write when the time comes
@@ -245,6 +247,14 @@ func (rw *responseWriter) WriteHeader(status int) {
247 rw.Header().Add("Vary", "Accept-Encoding")
248 }
249
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 +
258 // write status immediately when status code is informational
259 // see: https://caddy.community/t/disappear-103-early-hints-response-with-encode-enable-caddy-v2-7-6/23081/5
260 if 100 <= status && status <= 199 {
@@ -260,6 +270,12 @@ func (enc *Encode) Match(rw *responseWriter) bool {
270 // FlushError is an alternative Flush returning an error. It delays the actual Flush of the underlying
271 // ResponseWriterWrapper until headers were written.
272 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 +
279 if !rw.wroteHeader {
280 // flushing the underlying ResponseWriter will write header and status code,
281 // 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) {
304 return 0, nil
305 }
306
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 +
313 // sniff content-type and determine content-length
314 if !rw.wroteHeader && rw.config.MinLength > 0 {
315 var gtMinLength bool
modules/caddyhttp/encode/encode_test.go
+1 -1
@@ -9,7 +9,7 @@ import (
9 func BenchmarkOpenResponseWriter(b *testing.B) {
10 enc := new(Encode)
11 for n := 0; n < b.N; n++ {
12 - enc.openResponseWriter("test", nil)
12 + enc.openResponseWriter("test", nil, false)
13 }
14 }
15