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