Automatically handle HEAD requests (#104)
HEAD requests will be routed as GET requests, and Content-Type and Content-Length headers will always be sent for all requests. Co-authored-by: Ben Visness <bvisness@gmail.com> Reviewed-on: hmn/hmn#104
This commit is contained in:
parent
f3453355c4
commit
348feff4cf
|
@ -12,6 +12,7 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -105,9 +106,14 @@ func (rb *RouteBuilder) Group(regex *regexp.Regexp, ms ...Middleware) RouteBuild
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
func (r *Router) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
method := req.Method
|
||||||
|
if method == http.MethodHead {
|
||||||
|
method = http.MethodGet // HEADs map to GETs for the purposes of routing
|
||||||
|
}
|
||||||
|
|
||||||
nextroute:
|
nextroute:
|
||||||
for _, route := range r.Routes {
|
for _, route := range r.Routes {
|
||||||
if route.Method != "" && req.Method != route.Method {
|
if route.Method != "" && method != route.Method {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -465,12 +471,36 @@ func doRequest(rw http.ResponseWriter, c *RequestContext, h Handler) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// Run the chosen handler
|
||||||
res := h(c)
|
res := h(c)
|
||||||
|
|
||||||
if res.StatusCode == 0 {
|
if res.StatusCode == 0 {
|
||||||
res.StatusCode = http.StatusOK
|
res.StatusCode = http.StatusOK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set Content-Type and Content-Length if necessary. This behavior would in
|
||||||
|
// some cases be handled by http.ResponseWriter.Write, but we extract it so
|
||||||
|
// that HEAD requests always return both headers.
|
||||||
|
|
||||||
|
var preamble []byte // Any bytes we read to determine Content-Type
|
||||||
|
if res.Body != nil {
|
||||||
|
bodyLen := res.Body.Len()
|
||||||
|
|
||||||
|
if res.Header().Get("Content-Type") == "" {
|
||||||
|
preamble = res.Body.Next(512)
|
||||||
|
rw.Header().Set("Content-Type", http.DetectContentType(preamble))
|
||||||
|
}
|
||||||
|
if res.Header().Get("Content-Length") == "" {
|
||||||
|
rw.Header().Set("Content-Length", strconv.Itoa(bodyLen))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we send no body for HEAD requests
|
||||||
|
if c.Req.Method == http.MethodHead {
|
||||||
|
res.Body = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send remaining response headers
|
||||||
for name, vals := range res.Header() {
|
for name, vals := range res.Header() {
|
||||||
for _, val := range vals {
|
for _, val := range vals {
|
||||||
rw.Header().Add(name, val)
|
rw.Header().Add(name, val)
|
||||||
|
@ -478,7 +508,18 @@ func doRequest(rw http.ResponseWriter, c *RequestContext, h Handler) {
|
||||||
}
|
}
|
||||||
rw.WriteHeader(res.StatusCode)
|
rw.WriteHeader(res.StatusCode)
|
||||||
|
|
||||||
|
// Send response body
|
||||||
if res.Body != nil {
|
if res.Body != nil {
|
||||||
io.Copy(rw, res.Body)
|
// Write preamble, if any
|
||||||
|
_, err := rw.Write(preamble)
|
||||||
|
if err != nil {
|
||||||
|
logging.Error().Err(err).Msg("Failed to write response preamble")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write remainder of body
|
||||||
|
_, err = io.Copy(rw, res.Body)
|
||||||
|
if err != nil {
|
||||||
|
logging.Error().Err(err).Msg("copied res.Body")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue