From 484cee1ac12ee590128d85da22b6b9df2efe02d4 Mon Sep 17 00:00:00 2001 From: Matt Holt Date: Tue, 17 Sep 2019 15:16:17 -0600 Subject: fastcgi: Implement / redirect for index.php with php_fastcgi directive (#2754) * fastcgi: Implement / redirect for index.php with php_fastcgi directive See #2752 and https://caddy.community/t/v2-redirect-path-to-path-index-php-with-assets/6196?u=matt * caddyhttp: MatchNegate implements json.Marshaler * fastcgi: Add /index.php element to try_files matcher * fastcgi: Make /index.php redirect permanent --- modules/caddyhttp/matchers.go | 22 ++++++++++------ .../caddyhttp/reverseproxy/fastcgi/caddyfile.go | 29 ++++++++++++++++++++-- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/modules/caddyhttp/matchers.go b/modules/caddyhttp/matchers.go index 94d051ecc..9cf52dcc3 100644 --- a/modules/caddyhttp/matchers.go +++ b/modules/caddyhttp/matchers.go @@ -66,9 +66,9 @@ type ( // MatchNegate matches requests by negating its matchers' results. MatchNegate struct { - matchersRaw map[string]json.RawMessage + MatchersRaw map[string]json.RawMessage `json:"-"` - matchers MatcherSet + Matchers MatcherSet `json:"-"` } // MatchStarlarkExpr matches requests by evaluating a Starlark expression. @@ -400,7 +400,12 @@ func (MatchNegate) CaddyModule() caddy.ModuleInfo { // the struct, but we need a struct because we need another // field just for the provisioned modules. func (m *MatchNegate) UnmarshalJSON(data []byte) error { - return json.Unmarshal(data, &m.matchersRaw) + return json.Unmarshal(data, &m.MatchersRaw) +} + +// MarshalJSON marshals m's matchers. +func (m MatchNegate) MarshalJSON() ([]byte, error) { + return json.Marshal(m.MatchersRaw) } // UnmarshalCaddyfile implements caddyfile.Unmarshaler. @@ -411,21 +416,21 @@ func (m *MatchNegate) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { // Provision loads the matcher modules to be negated. func (m *MatchNegate) Provision(ctx caddy.Context) error { - for modName, rawMsg := range m.matchersRaw { + for modName, rawMsg := range m.MatchersRaw { val, err := ctx.LoadModule("http.matchers."+modName, rawMsg) if err != nil { return fmt.Errorf("loading matcher module '%s': %v", modName, err) } - m.matchers = append(m.matchers, val.(RequestMatcher)) + m.Matchers = append(m.Matchers, val.(RequestMatcher)) } - m.matchersRaw = nil // allow GC to deallocate + m.MatchersRaw = nil // allow GC to deallocate return nil } // Match returns true if r matches m. Since this matcher negates the // embedded matchers, false is returned if any of its matchers match. func (m MatchNegate) Match(r *http.Request) bool { - return !m.matchers.Match(r) + return !m.Matchers.Match(r) } // CaddyModule returns the Caddy module information. @@ -686,4 +691,7 @@ var ( _ caddyfile.Unmarshaler = (*MatchHeaderRE)(nil) _ caddyfile.Unmarshaler = (*MatchProtocol)(nil) _ caddyfile.Unmarshaler = (*MatchRemoteIP)(nil) + + _ json.Marshaler = (*MatchNegate)(nil) + _ json.Unmarshaler = (*MatchNegate)(nil) ) diff --git a/modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go b/modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go index fd82c5a86..b7326afa4 100644 --- a/modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go +++ b/modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go @@ -16,6 +16,7 @@ package fastcgi import ( "encoding/json" + "net/http" "github.com/caddyserver/caddy/v2/caddyconfig" "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" @@ -114,10 +115,30 @@ func parsePHPFastCGI(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error return nil, h.ArgErr() } + // route to redirect to canonical path if index PHP file + redirMatcherSet := map[string]json.RawMessage{ + "file": h.JSON(fileserver.MatchFile{ + TryFiles: []string{"{http.request.uri.path}/index.php"}, + }, nil), + "not": h.JSON(caddyhttp.MatchNegate{ + MatchersRaw: map[string]json.RawMessage{ + "path": h.JSON(caddyhttp.MatchPath{"*/"}, nil), + }, + }, nil), + } + redirHandler := caddyhttp.StaticResponse{ + StatusCode: caddyhttp.WeakString("308"), + Headers: http.Header{"Location": []string{"{http.request.uri.path}/"}}, + } + redirRoute := caddyhttp.Route{ + MatcherSetsRaw: []map[string]json.RawMessage{redirMatcherSet}, + HandlersRaw: []json.RawMessage{caddyconfig.JSONModuleObject(redirHandler, "handler", "static_response", nil)}, + } + // route to rewrite to PHP index file rewriteMatcherSet := map[string]json.RawMessage{ "file": h.JSON(fileserver.MatchFile{ - TryFiles: []string{"{http.request.uri.path}", "index.php"}, + TryFiles: []string{"{http.request.uri.path}", "{http.request.uri.path}/index.php", "index.php"}, }, nil), } rewriteHandler := rewrite.Rewrite{ @@ -175,7 +196,7 @@ func parsePHPFastCGI(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error // wrap ours in a subroute and return that if hasUserMatcher { subroute := caddyhttp.Subroute{ - Routes: caddyhttp.RouteList{rewriteRoute, rpRoute}, + Routes: caddyhttp.RouteList{redirRoute, rewriteRoute, rpRoute}, } return []httpcaddyfile.ConfigValue{ { @@ -191,6 +212,10 @@ func parsePHPFastCGI(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error // if the user did not specify a matcher, then // we can just use our own matchers return []httpcaddyfile.ConfigValue{ + { + Class: "route", + Value: redirRoute, + }, { Class: "route", Value: rewriteRoute, -- cgit v1.2.3