mirror of
https://github.com/caddyserver/caddy.git
synced 2025-01-08 11:58:49 +03:00
a798e0c951
- Server types no longer need to store their own contexts; they are stored on the caddy.Instance, which means each context will be properly GC'ed when the instance is stopped. Server types should use type assertions to convert from caddy.Context to their concrete context type when they need to use it. - Pass the entire context into httpserver.GetConfig instead of only the Key field. - caddy.NewTestController now requires a server type string so it can create a controller with the proper concrete context associated with that server type. Tests still need more attention so that we can test the proper creation of startup functions, etc.
184 lines
4.5 KiB
Go
184 lines
4.5 KiB
Go
package redirect
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/mholt/caddy"
|
|
"github.com/mholt/caddy/caddyhttp/httpserver"
|
|
)
|
|
|
|
func init() {
|
|
caddy.RegisterPlugin("redir", caddy.Plugin{
|
|
ServerType: "http",
|
|
Action: setup,
|
|
})
|
|
}
|
|
|
|
// setup configures a new Redirect middleware instance.
|
|
func setup(c *caddy.Controller) error {
|
|
rules, err := redirParse(c)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler {
|
|
return Redirect{Next: next, Rules: rules}
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
func redirParse(c *caddy.Controller) ([]Rule, error) {
|
|
var redirects []Rule
|
|
|
|
cfg := httpserver.GetConfig(c)
|
|
|
|
// setRedirCode sets the redirect code for rule if it can, or returns an error
|
|
setRedirCode := func(code string, rule *Rule) error {
|
|
if code == "meta" {
|
|
rule.Meta = true
|
|
} else if codeNumber, ok := httpRedirs[code]; ok {
|
|
rule.Code = codeNumber
|
|
} else {
|
|
return c.Errf("Invalid redirect code '%v'", code)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// checkAndSaveRule checks the rule for validity (except the redir code)
|
|
// and saves it if it's valid, or returns an error.
|
|
checkAndSaveRule := func(rule Rule) error {
|
|
if rule.FromPath == rule.To {
|
|
return c.Err("'from' and 'to' values of redirect rule cannot be the same")
|
|
}
|
|
|
|
for _, otherRule := range redirects {
|
|
if otherRule.FromPath == rule.FromPath {
|
|
return c.Errf("rule with duplicate 'from' value: %s -> %s", otherRule.FromPath, otherRule.To)
|
|
}
|
|
}
|
|
|
|
redirects = append(redirects, rule)
|
|
return nil
|
|
}
|
|
|
|
for c.Next() {
|
|
args := c.RemainingArgs()
|
|
|
|
var hadOptionalBlock bool
|
|
for c.NextBlock() {
|
|
hadOptionalBlock = true
|
|
|
|
var rule Rule
|
|
|
|
if cfg.TLS.Enabled {
|
|
rule.FromScheme = "https"
|
|
} else {
|
|
rule.FromScheme = "http"
|
|
}
|
|
|
|
// Set initial redirect code
|
|
// BUG: If the code is specified for a whole block and that code is invalid,
|
|
// the line number will appear on the first line inside the block, even if that
|
|
// line overwrites the block-level code with a valid redirect code. The program
|
|
// still functions correctly, but the line number in the error reporting is
|
|
// misleading to the user.
|
|
if len(args) == 1 {
|
|
err := setRedirCode(args[0], &rule)
|
|
if err != nil {
|
|
return redirects, err
|
|
}
|
|
} else {
|
|
rule.Code = http.StatusMovedPermanently // default code
|
|
}
|
|
|
|
// RemainingArgs only gets the values after the current token, but in our
|
|
// case we want to include the current token to get an accurate count.
|
|
insideArgs := append([]string{c.Val()}, c.RemainingArgs()...)
|
|
|
|
switch len(insideArgs) {
|
|
case 1:
|
|
// To specified (catch-all redirect)
|
|
// Not sure why user is doing this in a table, as it causes all other redirects to be ignored.
|
|
// As such, this feature remains undocumented.
|
|
rule.FromPath = "/"
|
|
rule.To = insideArgs[0]
|
|
case 2:
|
|
// From and To specified
|
|
rule.FromPath = insideArgs[0]
|
|
rule.To = insideArgs[1]
|
|
case 3:
|
|
// From, To, and Code specified
|
|
rule.FromPath = insideArgs[0]
|
|
rule.To = insideArgs[1]
|
|
err := setRedirCode(insideArgs[2], &rule)
|
|
if err != nil {
|
|
return redirects, err
|
|
}
|
|
default:
|
|
return redirects, c.ArgErr()
|
|
}
|
|
|
|
err := checkAndSaveRule(rule)
|
|
if err != nil {
|
|
return redirects, err
|
|
}
|
|
}
|
|
|
|
if !hadOptionalBlock {
|
|
var rule Rule
|
|
|
|
if cfg.TLS.Enabled {
|
|
rule.FromScheme = "https"
|
|
} else {
|
|
rule.FromScheme = "http"
|
|
}
|
|
|
|
rule.Code = http.StatusMovedPermanently // default
|
|
|
|
switch len(args) {
|
|
case 1:
|
|
// To specified (catch-all redirect)
|
|
rule.FromPath = "/"
|
|
rule.To = args[0]
|
|
case 2:
|
|
// To and Code specified (catch-all redirect)
|
|
rule.FromPath = "/"
|
|
rule.To = args[0]
|
|
err := setRedirCode(args[1], &rule)
|
|
if err != nil {
|
|
return redirects, err
|
|
}
|
|
case 3:
|
|
// From, To, and Code specified
|
|
rule.FromPath = args[0]
|
|
rule.To = args[1]
|
|
err := setRedirCode(args[2], &rule)
|
|
if err != nil {
|
|
return redirects, err
|
|
}
|
|
default:
|
|
return redirects, c.ArgErr()
|
|
}
|
|
|
|
err := checkAndSaveRule(rule)
|
|
if err != nil {
|
|
return redirects, err
|
|
}
|
|
}
|
|
}
|
|
|
|
return redirects, nil
|
|
}
|
|
|
|
// httpRedirs is a list of supported HTTP redirect codes.
|
|
var httpRedirs = map[string]int{
|
|
"300": http.StatusMultipleChoices,
|
|
"301": http.StatusMovedPermanently,
|
|
"302": http.StatusFound, // (NOT CORRECT for "Temporary Redirect", see 307)
|
|
"303": http.StatusSeeOther,
|
|
"304": http.StatusNotModified,
|
|
"305": http.StatusUseProxy,
|
|
"307": http.StatusTemporaryRedirect,
|
|
"308": 308, // Permanent Redirect
|
|
}
|