2017-02-18 00:25:22 +08:00
|
|
|
package push
|
|
|
|
|
|
|
|
import (
|
|
|
|
"net/http"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/mholt/caddy/caddyhttp/httpserver"
|
2017-07-06 15:24:01 +08:00
|
|
|
"github.com/mholt/caddy/caddyhttp/staticfiles"
|
2017-02-18 00:25:22 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
func (h Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
|
|
|
pusher, hasPusher := w.(http.Pusher)
|
|
|
|
|
2017-04-18 12:06:17 +08:00
|
|
|
// no push possible, carry on
|
2017-02-18 00:25:22 +08:00
|
|
|
if !hasPusher {
|
|
|
|
return h.Next.ServeHTTP(w, r)
|
|
|
|
}
|
|
|
|
|
2017-04-18 12:06:17 +08:00
|
|
|
// check if this is a request for the pushed resource (avoid recursion)
|
2017-02-18 00:25:22 +08:00
|
|
|
if _, exists := r.Header[pushHeader]; exists {
|
|
|
|
return h.Next.ServeHTTP(w, r)
|
|
|
|
}
|
|
|
|
|
2017-02-19 06:50:36 +08:00
|
|
|
headers := h.filterProxiedHeaders(r.Header)
|
2017-02-18 00:25:22 +08:00
|
|
|
|
2017-04-18 12:06:17 +08:00
|
|
|
// push first
|
2017-02-18 00:25:22 +08:00
|
|
|
outer:
|
|
|
|
for _, rule := range h.Rules {
|
2017-07-06 15:24:01 +08:00
|
|
|
urlPath := r.URL.Path
|
|
|
|
matches := httpserver.Path(urlPath).Matches(rule.Path)
|
|
|
|
// Also check IndexPages when requesting a directory
|
|
|
|
if !matches {
|
|
|
|
_, matches = httpserver.IndexFile(h.Root, urlPath, staticfiles.IndexPages)
|
|
|
|
}
|
|
|
|
if matches {
|
2017-02-18 00:25:22 +08:00
|
|
|
for _, resource := range rule.Resources {
|
|
|
|
pushErr := pusher.Push(resource.Path, &http.PushOptions{
|
|
|
|
Method: resource.Method,
|
2017-02-19 06:50:36 +08:00
|
|
|
Header: h.mergeHeaders(headers, resource.Header),
|
2017-02-18 00:25:22 +08:00
|
|
|
})
|
|
|
|
if pushErr != nil {
|
2017-04-18 12:06:17 +08:00
|
|
|
// if we cannot push (either not supported or concurrent streams are full - break)
|
2017-02-18 00:25:22 +08:00
|
|
|
break outer
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-18 12:06:17 +08:00
|
|
|
// serve later
|
2017-02-19 06:50:36 +08:00
|
|
|
code, err := h.Next.ServeHTTP(w, r)
|
|
|
|
|
2017-04-18 12:06:17 +08:00
|
|
|
// push resources returned in Link headers from upstream middlewares or proxied apps
|
2017-02-18 00:25:22 +08:00
|
|
|
if links, exists := w.Header()["Link"]; exists {
|
2017-02-19 06:50:36 +08:00
|
|
|
h.servePreloadLinks(pusher, headers, links)
|
2017-02-18 00:25:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return code, err
|
|
|
|
}
|
|
|
|
|
2017-02-19 06:50:36 +08:00
|
|
|
func (h Middleware) servePreloadLinks(pusher http.Pusher, headers http.Header, links []string) {
|
2017-02-18 00:25:22 +08:00
|
|
|
for _, link := range links {
|
|
|
|
parts := strings.Split(link, ";")
|
|
|
|
|
|
|
|
if link == "" || strings.HasSuffix(link, "nopush") {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
target := strings.TrimSuffix(strings.TrimPrefix(parts[0], "<"), ">")
|
|
|
|
|
2017-02-19 06:50:36 +08:00
|
|
|
err := pusher.Push(target, &http.PushOptions{
|
|
|
|
Method: http.MethodGet,
|
|
|
|
Header: headers,
|
|
|
|
})
|
|
|
|
|
2017-02-18 00:25:22 +08:00
|
|
|
if err != nil {
|
2017-04-18 12:06:17 +08:00
|
|
|
break
|
2017-02-18 00:25:22 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-02-19 06:50:36 +08:00
|
|
|
|
|
|
|
func (h Middleware) mergeHeaders(l, r http.Header) http.Header {
|
|
|
|
out := http.Header{}
|
|
|
|
|
|
|
|
for k, v := range l {
|
|
|
|
out[k] = v
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, vv := range r {
|
|
|
|
for _, v := range vv {
|
|
|
|
out.Add(k, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h Middleware) filterProxiedHeaders(headers http.Header) http.Header {
|
|
|
|
filter := http.Header{}
|
|
|
|
|
|
|
|
for _, header := range proxiedHeaders {
|
|
|
|
if val, ok := headers[header]; ok {
|
|
|
|
filter[header] = val
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return filter
|
|
|
|
}
|
|
|
|
|
|
|
|
var proxiedHeaders = []string{
|
|
|
|
"Accept-Encoding",
|
|
|
|
"Accept-Language",
|
|
|
|
"Cache-Control",
|
|
|
|
"Host",
|
|
|
|
"User-Agent",
|
|
|
|
}
|