mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-16 21:56:40 -05:00
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
This commit is contained in:
parent
d0e209e1da
commit
bcaa8aaf11
2 changed files with 27 additions and 5 deletions
|
@ -156,7 +156,7 @@ func (enc *Encode) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyh
|
||||||
if _, ok := enc.writerPools[encName]; !ok {
|
if _, ok := enc.writerPools[encName]; !ok {
|
||||||
continue // encoding not offered
|
continue // encoding not offered
|
||||||
}
|
}
|
||||||
w = enc.openResponseWriter(encName, w)
|
w = enc.openResponseWriter(encName, w, r.Method == http.MethodConnect)
|
||||||
defer w.(*responseWriter).Close()
|
defer w.(*responseWriter).Close()
|
||||||
|
|
||||||
// to comply with RFC 9110 section 8.8.3(.3), we modify the Etag when encoding
|
// 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 {
|
||||||
// openResponseWriter creates a new response writer that may (or may not)
|
// openResponseWriter creates a new response writer that may (or may not)
|
||||||
// encode the response with encodingName. The returned response writer MUST
|
// encode the response with encodingName. The returned response writer MUST
|
||||||
// be closed after the handler completes.
|
// be closed after the handler completes.
|
||||||
func (enc *Encode) openResponseWriter(encodingName string, w http.ResponseWriter) *responseWriter {
|
func (enc *Encode) openResponseWriter(encodingName string, w http.ResponseWriter, isConnect bool) *responseWriter {
|
||||||
var rw responseWriter
|
var rw responseWriter
|
||||||
return enc.initResponseWriter(&rw, encodingName, w)
|
return enc.initResponseWriter(&rw, encodingName, w, isConnect)
|
||||||
}
|
}
|
||||||
|
|
||||||
// initResponseWriter initializes the responseWriter instance
|
// initResponseWriter initializes the responseWriter instance
|
||||||
// allocated in openResponseWriter, enabling mid-stack inlining.
|
// allocated in openResponseWriter, enabling mid-stack inlining.
|
||||||
func (enc *Encode) initResponseWriter(rw *responseWriter, encodingName string, wrappedRW http.ResponseWriter) *responseWriter {
|
func (enc *Encode) initResponseWriter(rw *responseWriter, encodingName string, wrappedRW http.ResponseWriter, isConnect bool) *responseWriter {
|
||||||
if rww, ok := wrappedRW.(*caddyhttp.ResponseWriterWrapper); ok {
|
if rww, ok := wrappedRW.(*caddyhttp.ResponseWriterWrapper); ok {
|
||||||
rw.ResponseWriter = rww
|
rw.ResponseWriter = rww
|
||||||
} else {
|
} else {
|
||||||
|
@ -216,6 +216,7 @@ func (enc *Encode) initResponseWriter(rw *responseWriter, encodingName string, w
|
||||||
}
|
}
|
||||||
rw.encodingName = encodingName
|
rw.encodingName = encodingName
|
||||||
rw.config = enc
|
rw.config = enc
|
||||||
|
rw.isConnect = isConnect
|
||||||
|
|
||||||
return rw
|
return rw
|
||||||
}
|
}
|
||||||
|
@ -230,6 +231,7 @@ type responseWriter struct {
|
||||||
config *Encode
|
config *Encode
|
||||||
statusCode int
|
statusCode int
|
||||||
wroteHeader bool
|
wroteHeader bool
|
||||||
|
isConnect bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteHeader stores the status to write when the time comes
|
// WriteHeader stores the status to write when the time comes
|
||||||
|
@ -245,6 +247,14 @@ func (rw *responseWriter) WriteHeader(status int) {
|
||||||
rw.Header().Add("Vary", "Accept-Encoding")
|
rw.Header().Add("Vary", "Accept-Encoding")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// write status immediately if status is 2xx and the request is CONNECT
|
||||||
|
// since it means the response is successful.
|
||||||
|
// see: https://github.com/caddyserver/caddy/issues/6733#issuecomment-2525058845
|
||||||
|
if rw.isConnect && 200 <= status && status <= 299 {
|
||||||
|
rw.ResponseWriter.WriteHeader(status)
|
||||||
|
rw.wroteHeader = true
|
||||||
|
}
|
||||||
|
|
||||||
// write status immediately when status code is informational
|
// write status immediately when status code is informational
|
||||||
// see: https://caddy.community/t/disappear-103-early-hints-response-with-encode-enable-caddy-v2-7-6/23081/5
|
// see: https://caddy.community/t/disappear-103-early-hints-response-with-encode-enable-caddy-v2-7-6/23081/5
|
||||||
if 100 <= status && status <= 199 {
|
if 100 <= status && status <= 199 {
|
||||||
|
@ -260,6 +270,12 @@ func (enc *Encode) Match(rw *responseWriter) bool {
|
||||||
// FlushError is an alternative Flush returning an error. It delays the actual Flush of the underlying
|
// FlushError is an alternative Flush returning an error. It delays the actual Flush of the underlying
|
||||||
// ResponseWriterWrapper until headers were written.
|
// ResponseWriterWrapper until headers were written.
|
||||||
func (rw *responseWriter) FlushError() error {
|
func (rw *responseWriter) FlushError() error {
|
||||||
|
// WriteHeader wasn't called and is a CONNECT request, treat it as a success.
|
||||||
|
// otherwise, wait until header is written.
|
||||||
|
if rw.isConnect && !rw.wroteHeader && rw.statusCode == 0 {
|
||||||
|
rw.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
if !rw.wroteHeader {
|
if !rw.wroteHeader {
|
||||||
// flushing the underlying ResponseWriter will write header and status code,
|
// flushing the underlying ResponseWriter will write header and status code,
|
||||||
// but we need to delay that until we can determine if we must encode and
|
// 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) {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteHeader wasn't called and is a CONNECT request, treat it as a success.
|
||||||
|
// otherwise, determine if the response should be compressed.
|
||||||
|
if rw.isConnect && !rw.wroteHeader && rw.statusCode == 0 {
|
||||||
|
rw.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
// sniff content-type and determine content-length
|
// sniff content-type and determine content-length
|
||||||
if !rw.wroteHeader && rw.config.MinLength > 0 {
|
if !rw.wroteHeader && rw.config.MinLength > 0 {
|
||||||
var gtMinLength bool
|
var gtMinLength bool
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
func BenchmarkOpenResponseWriter(b *testing.B) {
|
func BenchmarkOpenResponseWriter(b *testing.B) {
|
||||||
enc := new(Encode)
|
enc := new(Encode)
|
||||||
for n := 0; n < b.N; n++ {
|
for n := 0; n < b.N; n++ {
|
||||||
enc.openResponseWriter("test", nil)
|
enc.openResponseWriter("test", nil, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue