summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/caddyhttp/matchers.go16
-rw-r--r--modules/caddyhttp/push/handler.go20
-rw-r--r--modules/caddyhttp/responsewriter.go52
-rw-r--r--modules/caddyhttp/staticresp.go15
4 files changed, 77 insertions, 26 deletions
diff --git a/modules/caddyhttp/matchers.go b/modules/caddyhttp/matchers.go
index 5056c9a..f86ce0a 100644
--- a/modules/caddyhttp/matchers.go
+++ b/modules/caddyhttp/matchers.go
@@ -1121,6 +1121,22 @@ func (m MatchProtocol) Match(r *http.Request) bool {
return r.TLS != nil
case "http":
return r.TLS == nil
+ case "http/1.0":
+ return r.ProtoMajor == 1 && r.ProtoMinor == 0
+ case "http/1.0+":
+ return r.ProtoAtLeast(1, 0)
+ case "http/1.1":
+ return r.ProtoMajor == 1 && r.ProtoMinor == 1
+ case "http/1.1+":
+ return r.ProtoAtLeast(1, 1)
+ case "http/2":
+ return r.ProtoMajor == 2
+ case "http/2+":
+ return r.ProtoAtLeast(2, 0)
+ case "http/3":
+ return r.ProtoMajor == 3
+ case "http/3+":
+ return r.ProtoAtLeast(3, 0)
}
return false
}
diff --git a/modules/caddyhttp/push/handler.go b/modules/caddyhttp/push/handler.go
index 75442be..27652ef 100644
--- a/modules/caddyhttp/push/handler.go
+++ b/modules/caddyhttp/push/handler.go
@@ -29,10 +29,24 @@ func init() {
caddy.RegisterModule(Handler{})
}
-// Handler is a middleware for manipulating the request body.
+// Handler is a middleware for HTTP/2 server push. Note that
+// HTTP/2 server push has been deprecated by some clients and
+// its use is discouraged unless you can accurately predict
+// which resources actually need to be pushed to the client;
+// it can be difficult to know what the client already has
+// cached. Pushing unnecessary resources results in worse
+// performance. Consider using HTTP 103 Early Hints instead.
+//
+// This handler supports pushing from Link headers; in other
+// words, if the eventual response has Link headers, this
+// handler will push the resources indicated by those headers,
+// even without specifying any resources in its config.
type Handler struct {
- Resources []Resource `json:"resources,omitempty"`
- Headers *HeaderConfig `json:"headers,omitempty"`
+ // The resources to push.
+ Resources []Resource `json:"resources,omitempty"`
+
+ // Headers to modify for the push requests.
+ Headers *HeaderConfig `json:"headers,omitempty"`
logger *zap.Logger
}
diff --git a/modules/caddyhttp/responsewriter.go b/modules/caddyhttp/responsewriter.go
index 0ffb932..374bbfb 100644
--- a/modules/caddyhttp/responsewriter.go
+++ b/modules/caddyhttp/responsewriter.go
@@ -111,15 +111,15 @@ type responseRecorder struct {
//
// Proper usage of a recorder looks like this:
//
-// rec := caddyhttp.NewResponseRecorder(w, buf, shouldBuffer)
-// err := next.ServeHTTP(rec, req)
-// if err != nil {
-// return err
-// }
-// if !rec.Buffered() {
-// return nil
-// }
-// // process the buffered response here
+// rec := caddyhttp.NewResponseRecorder(w, buf, shouldBuffer)
+// err := next.ServeHTTP(rec, req)
+// if err != nil {
+// return err
+// }
+// if !rec.Buffered() {
+// return nil
+// }
+// // process the buffered response here
//
// The header map is not buffered; i.e. the ResponseRecorder's Header()
// method returns the same header map of the underlying ResponseWriter.
@@ -129,7 +129,7 @@ type responseRecorder struct {
// Once you are ready to write the response, there are two ways you can
// do it. The easier way is to have the recorder do it:
//
-// rec.WriteResponse()
+// rec.WriteResponse()
//
// This writes the recorded response headers as well as the buffered body.
// Or, you may wish to do it yourself, especially if you manipulated the
@@ -138,9 +138,12 @@ type responseRecorder struct {
// recorder's body buffer, but you might have your own body to write
// instead):
//
-// w.WriteHeader(rec.Status())
-// io.Copy(w, rec.Buffer())
+// w.WriteHeader(rec.Status())
+// io.Copy(w, rec.Buffer())
//
+// As a special case, 1xx responses are not buffered nor recorded
+// because they are not the final response; they are passed through
+// directly to the underlying ResponseWriter.
func NewResponseRecorder(w http.ResponseWriter, buf *bytes.Buffer, shouldBuffer ShouldBufferFunc) ResponseRecorder {
return &responseRecorder{
ResponseWriterWrapper: &ResponseWriterWrapper{ResponseWriter: w},
@@ -149,22 +152,29 @@ func NewResponseRecorder(w http.ResponseWriter, buf *bytes.Buffer, shouldBuffer
}
}
+// WriteHeader writes the headers with statusCode to the wrapped
+// ResponseWriter unless the response is to be buffered instead.
+// 1xx responses are never buffered.
func (rr *responseRecorder) WriteHeader(statusCode int) {
if rr.wroteHeader {
return
}
- rr.statusCode = statusCode
- rr.wroteHeader = true
- // decide whether we should buffer the response
- if rr.shouldBuffer == nil {
- rr.stream = true
- } else {
- rr.stream = !rr.shouldBuffer(rr.statusCode, rr.ResponseWriterWrapper.Header())
+ // 1xx responses aren't final; just informational
+ if statusCode < 100 || statusCode > 199 {
+ rr.statusCode = statusCode
+ rr.wroteHeader = true
+
+ // decide whether we should buffer the response
+ if rr.shouldBuffer == nil {
+ rr.stream = true
+ } else {
+ rr.stream = !rr.shouldBuffer(rr.statusCode, rr.ResponseWriterWrapper.Header())
+ }
}
- // if not buffered, immediately write header
- if rr.stream {
+ // if informational or not buffered, immediately write header
+ if rr.stream || (100 <= statusCode && statusCode <= 199) {
rr.ResponseWriterWrapper.WriteHeader(rr.statusCode)
}
}
diff --git a/modules/caddyhttp/staticresp.go b/modules/caddyhttp/staticresp.go
index f429692..ccc70e2 100644
--- a/modules/caddyhttp/staticresp.go
+++ b/modules/caddyhttp/staticresp.go
@@ -86,6 +86,12 @@ Response headers may be added using the --header flag for each header field.
type StaticResponse struct {
// The HTTP status code to respond with. Can be an integer or,
// if needing to use a placeholder, a string.
+ //
+ // If the status code is 103 (Early Hints), the response headers
+ // will be written to the client immediately, the body will be
+ // ignored, and the next handler will be invoked. This behavior
+ // is EXPERIMENTAL while RFC 8297 is a draft, and may be changed
+ // or removed.
StatusCode WeakString `json:"status_code,omitempty"`
// Header fields to set on the response; overwrites any existing
@@ -170,7 +176,7 @@ func (s *StaticResponse) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
return nil
}
-func (s StaticResponse) ServeHTTP(w http.ResponseWriter, r *http.Request, _ Handler) error {
+func (s StaticResponse) ServeHTTP(w http.ResponseWriter, r *http.Request, next Handler) error {
// close the connection immediately
if s.Abort {
panic(http.ErrAbortHandler)
@@ -237,10 +243,15 @@ func (s StaticResponse) ServeHTTP(w http.ResponseWriter, r *http.Request, _ Hand
w.WriteHeader(statusCode)
// write response body
- if body != "" {
+ if statusCode != http.StatusEarlyHints && body != "" {
fmt.Fprint(w, body)
}
+ // continue handling after Early Hints as they are not the final response
+ if statusCode == http.StatusEarlyHints {
+ return next.ServeHTTP(w, r)
+ }
+
return nil
}