Commit ce2a9cd8 authored by Matthew Holt's avatar Matthew Holt

push: Reorder before proxy; and allow zero arguments (cf. #1573)

parent 4462e397
......@@ -475,11 +475,11 @@ var directives = []string{
"internal",
"pprof",
"expvar",
"push",
"prometheus", // github.com/miekg/caddy-prometheus
"proxy",
"fastcgi",
"cgi", // github.com/jung-kurt/caddy-cgi
"push",
"websocket",
"filemanager", // github.com/hacdias/caddy-filemanager
"markdown",
......
......@@ -8,22 +8,21 @@ import (
)
func (h Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
pusher, hasPusher := w.(http.Pusher)
// No Pusher, no cry
// no push possible, carry on
if !hasPusher {
return h.Next.ServeHTTP(w, r)
}
// This is request for the pushed resource - it should not be recursive
// check if this is a request for the pushed resource (avoid recursion)
if _, exists := r.Header[pushHeader]; exists {
return h.Next.ServeHTTP(w, r)
}
headers := h.filterProxiedHeaders(r.Header)
// Push first
// push first
outer:
for _, rule := range h.Rules {
if httpserver.Path(r.URL.Path).Matches(rule.Path) {
......@@ -33,16 +32,17 @@ outer:
Header: h.mergeHeaders(headers, resource.Header),
})
if pushErr != nil {
// If we cannot push (either not supported or concurrent streams are full - break)
// if we cannot push (either not supported or concurrent streams are full - break)
break outer
}
}
}
}
// Serve later
// serve later
code, err := h.Next.ServeHTTP(w, r)
// push resources returned in Link headers from upstream middlewares or proxied apps
if links, exists := w.Header()["Link"]; exists {
h.servePreloadLinks(pusher, headers, links)
}
......@@ -51,7 +51,6 @@ outer:
}
func (h Middleware) servePreloadLinks(pusher http.Pusher, headers http.Header, links []string) {
outer:
for _, link := range links {
parts := strings.Split(link, ";")
......@@ -67,13 +66,12 @@ outer:
})
if err != nil {
break outer
break
}
}
}
func (h Middleware) mergeHeaders(l, r http.Header) http.Header {
out := http.Header{}
for k, v := range l {
......@@ -90,7 +88,6 @@ func (h Middleware) mergeHeaders(l, r http.Header) http.Header {
}
func (h Middleware) filterProxiedHeaders(headers http.Header) http.Header {
filter := http.Header{}
for _, header := range proxiedHeaders {
......
......@@ -45,87 +45,94 @@ func parsePushRules(c *caddy.Controller) ([]Rule, error) {
var rules = make(map[string]*Rule)
for c.NextLine() {
if !c.NextArg() {
return emptyRules, c.ArgErr()
}
path := c.Val()
args := c.RemainingArgs()
var rule *Rule
var resources []Resource
var ops []ruleOp
if existingRule, ok := rules[path]; ok {
rule = existingRule
} else {
rule = new(Rule)
rule.Path = path
rules[rule.Path] = rule
}
parseBlock := func() error {
for c.NextBlock() {
val := c.Val()
for i := 0; i < len(args); i++ {
resources = append(resources, Resource{
Path: args[i],
Method: http.MethodGet,
Header: http.Header{pushHeader: []string{}},
})
}
switch val {
case "method":
if !c.NextArg() {
return c.ArgErr()
}
for c.NextBlock() {
val := c.Val()
method := c.Val()
switch val {
case "method":
if !c.NextArg() {
return emptyRules, c.ArgErr()
}
if err := validateMethod(method); err != nil {
return errMethodNotSupported
}
method := c.Val()
ops = append(ops, setMethodOp(method))
if err := validateMethod(method); err != nil {
return emptyRules, errMethodNotSupported
}
case "header":
args := c.RemainingArgs()
ops = append(ops, setMethodOp(method))
if len(args) != 2 {
return errInvalidHeader
}
case "header":
args := c.RemainingArgs()
if err := validateHeader(args[0]); err != nil {
return err
}
if len(args) != 2 {
return emptyRules, errInvalidHeader
ops = append(ops, setHeaderOp(args[0], args[1]))
default:
resources = append(resources, Resource{
Path: val,
Method: http.MethodGet,
Header: http.Header{pushHeader: []string{}},
})
}
}
return nil
}
if err := validateHeader(args[0]); err != nil {
return emptyRules, err
}
args := c.RemainingArgs()
ops = append(ops, setHeaderOp(args[0], args[1]))
if len(args) == 0 {
rule = new(Rule)
rule.Path = "/"
rules["/"] = rule
err := parseBlock()
if err != nil {
return emptyRules, err
}
} else {
path := args[0]
if existingRule, ok := rules[path]; ok {
rule = existingRule
} else {
rule = new(Rule)
rule.Path = path
rules[rule.Path] = rule
}
default:
for i := 1; i < len(args); i++ {
resources = append(resources, Resource{
Path: val,
Path: args[i],
Method: http.MethodGet,
Header: http.Header{pushHeader: []string{}},
})
}
err := parseBlock()
if err != nil {
return emptyRules, err
}
}
for _, op := range ops {
op(resources)
}
rule.Resources = append(rule.Resources, resources...)
}
var returnRules []Rule
for path, rule := range rules {
if len(rule.Resources) == 0 {
return emptyRules, c.Errf("Rule %s has empty push resources list", path)
}
for _, rule := range rules {
returnRules = append(returnRules, *rule)
}
......@@ -141,7 +148,6 @@ func setHeaderOp(key, value string) func(resources []Resource) {
}
func setMethodOp(method string) func(resources []Resource) {
return func(resources []Resource) {
for index := range resources {
resources[index].Method = method
......
......@@ -25,10 +25,10 @@ func TestConfigParse(t *testing.T) {
expected []Rule
}{
{
"ParseInvalidEmptyConfig", `push`, true, []Rule{},
"ParseInvalidEmptyConfig", `push`, false, []Rule{{Path: "/"}},
},
{
"ParseInvalidConfig", `push /index.html`, true, []Rule{},
"ParseInvalidConfig", `push /index.html`, false, []Rule{{Path: "/index.html"}},
},
{
"ParseInvalidConfigBlock", `push /index.html /index.css {
......@@ -255,7 +255,7 @@ func TestSetupInstalledMiddleware(t *testing.T) {
func TestSetupWithError(t *testing.T) {
// given
c := caddy.NewTestController("http", `push /index.html`)
c := caddy.NewTestController("http", "push {\nmethod\n}")
// when
err := setup(c)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment