Esempio n. 1
0
func rewriteParse(c *caddy.Controller) ([]httpserver.HandlerConfig, error) {
	var rules []httpserver.HandlerConfig

	for c.Next() {
		var rule Rule
		var err error
		var base = "/"
		var pattern, to string
		var ext []string

		args := c.RemainingArgs()

		var matcher httpserver.RequestMatcher

		switch len(args) {
		case 1:
			base = args[0]
			fallthrough
		case 0:
			// Integrate request matcher for 'if' conditions.
			matcher, err = httpserver.SetupIfMatcher(c)
			if err != nil {
				return nil, err
			}

			for c.NextBlock() {
				if httpserver.IfMatcherKeyword(c) {
					continue
				}
				switch c.Val() {
				case "r", "regexp":
					if !c.NextArg() {
						return nil, c.ArgErr()
					}
					pattern = c.Val()
				case "to":
					args1 := c.RemainingArgs()
					if len(args1) == 0 {
						return nil, c.ArgErr()
					}
					to = strings.Join(args1, " ")
				case "ext":
					args1 := c.RemainingArgs()
					if len(args1) == 0 {
						return nil, c.ArgErr()
					}
					ext = args1
				default:
					return nil, c.ArgErr()
				}
			}
			// ensure to is specified
			if to == "" {
				return nil, c.ArgErr()
			}
			if rule, err = NewComplexRule(base, pattern, to, ext, matcher); err != nil {
				return nil, err
			}
			rules = append(rules, rule)

		// the only unhandled case is 2 and above
		default:
			rule = NewSimpleRule(args[0], strings.Join(args[1:], " "))
			rules = append(rules, rule)
		}

	}

	return rules, nil
}
Esempio n. 2
0
func TestSetup(t *testing.T) {

	for j, test := range []struct {
		input         string
		shouldErr     bool
		expectedRules []Rule
	}{
		// test case #0 tests the recognition of a valid HTTP status code defined outside of block statement
		{"redir 300 {\n/ /foo\n}", false, []Rule{{FromPath: "/", To: "/foo", Code: 300, RequestMatcher: httpserver.IfMatcher{}}}},

		// test case #1 tests the recognition of an invalid HTTP status code defined outside of block statement
		{"redir 9000 {\n/ /foo\n}", true, []Rule{{}}},

		// test case #2 tests the detection of a valid HTTP status code outside of a block statement being overridden by an invalid HTTP status code inside statement of a block statement
		{"redir 300 {\n/ /foo 9000\n}", true, []Rule{{}}},

		// test case #3 tests the detection of an invalid HTTP status code outside of a block statement being overridden by a valid HTTP status code inside statement of a block statement
		{"redir 9000 {\n/ /foo 300\n}", true, []Rule{{}}},

		// test case #4 tests the recognition of a TO redirection in a block statement.The HTTP status code is set to the default of 301 - MovedPermanently
		{"redir 302 {\n/foo\n}", false, []Rule{{FromPath: "/", To: "/foo", Code: 302, RequestMatcher: httpserver.IfMatcher{}}}},

		// test case #5 tests the recognition of a TO and From redirection in a block statement
		{"redir {\n/bar /foo 303\n}", false, []Rule{{FromPath: "/bar", To: "/foo", Code: 303, RequestMatcher: httpserver.IfMatcher{}}}},

		// test case #6 tests the recognition of a TO redirection in a non-block statement. The HTTP status code is set to the default of 301 - MovedPermanently
		{"redir /foo", false, []Rule{{FromPath: "/", To: "/foo", Code: 301, RequestMatcher: httpserver.IfMatcher{}}}},

		// test case #7 tests the recognition of a TO and From redirection in a non-block statement
		{"redir /bar /foo 303", false, []Rule{{FromPath: "/bar", To: "/foo", Code: 303, RequestMatcher: httpserver.IfMatcher{}}}},

		// test case #8 tests the recognition of multiple redirections
		{"redir {\n / /foo 304 \n} \n redir {\n /bar /foobar 305 \n}", false,
			[]Rule{{FromPath: "/", To: "/foo", Code: 304, RequestMatcher: httpserver.IfMatcher{}},
				{FromPath: "/bar", To: "/foobar", Code: 305, RequestMatcher: httpserver.IfMatcher{}}}},

		// test case #9 tests the detection of duplicate redirections
		{"redir {\n /bar /foo 304 \n} redir {\n /bar /foo 304 \n}", true, []Rule{{}}},

		// test case #10 tests the detection of a valid HTTP status code outside of a block statement being overridden by an valid HTTP status code inside statement of a block statement
		{"redir 300 {\n/ /foo 301\n}", false, []Rule{{FromPath: "/", To: "/foo", Code: 301, RequestMatcher: httpserver.IfMatcher{}}}},

		// test case #11 tests the recognition of a matcher
		{"redir {\n if {port} is 80\n/ /foo\n}", false, []Rule{{FromPath: "/", To: "/foo", Code: 301,
			RequestMatcher: func() httpserver.IfMatcher {
				c := caddy.NewTestController("http", "{\n if {port} is 80\n}")
				matcher, _ := httpserver.SetupIfMatcher(c)
				return matcher.(httpserver.IfMatcher)
			}()}}},

		// test case #12 tests the detection of a valid HTTP status code outside of a block statement with a matcher
		{"redir 300 {\n if {port} is 80\n/ /foo\n}", false, []Rule{{FromPath: "/", To: "/foo", Code: 300,
			RequestMatcher: func() httpserver.IfMatcher {
				c := caddy.NewTestController("http", "{\n if {port} is 80\n}")
				matcher, _ := httpserver.SetupIfMatcher(c)
				return matcher.(httpserver.IfMatcher)
			}()}}},
	} {
		c := caddy.NewTestController("http", test.input)
		err := setup(c)
		if err != nil && !test.shouldErr {
			t.Errorf("Test case #%d recieved an error of %v", j, err)
		} else if test.shouldErr {
			continue
		}
		mids := httpserver.GetConfig(c).Middleware()
		recievedRules := mids[len(mids)-1](nil).(Redirect).Rules

		for i, recievedRule := range recievedRules {
			if recievedRule.FromPath != test.expectedRules[i].FromPath {
				t.Errorf("Test case #%d.%d expected a from path of %s, but recieved a from path of %s", j, i, test.expectedRules[i].FromPath, recievedRule.FromPath)
			}
			if recievedRule.To != test.expectedRules[i].To {
				t.Errorf("Test case #%d.%d expected a TO path of %s, but recieved a TO path of %s", j, i, test.expectedRules[i].To, recievedRule.To)
			}
			if recievedRule.Code != test.expectedRules[i].Code {
				t.Errorf("Test case #%d.%d expected a HTTP status code of %d, but recieved a code of %d", j, i, test.expectedRules[i].Code, recievedRule.Code)
			}
			if gotMatcher, expectMatcher := fmt.Sprint(recievedRule.RequestMatcher), fmt.Sprint(test.expectedRules[i].RequestMatcher); gotMatcher != expectMatcher {
				t.Errorf("Test case #%d.%d expected a Matcher %s, but recieved a Matcher %s", j, i, expectMatcher, gotMatcher)
			}
		}
	}

}
Esempio n. 3
0
func redirParse(c *caddy.Controller) ([]Rule, error) {
	var redirects []Rule

	cfg := httpserver.GetConfig(c)

	initRule := func(rule *Rule, defaultCode string, args []string) error {
		if cfg.TLS.Enabled {
			rule.FromScheme = "https"
		} else {
			rule.FromScheme = "http"
		}

		var (
			from = "/"
			to   string
			code = defaultCode
		)
		switch len(args) {
		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.
			to = args[0]
		case 2:
			// From and To specified
			from = args[0]
			to = args[1]
		case 3:
			// From, To, and Code specified
			from = args[0]
			to = args[1]
			code = args[2]
		default:
			return c.ArgErr()
		}

		rule.FromPath = from
		rule.To = to
		if code == "meta" {
			rule.Meta = true
			code = defaultCode
		}
		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
	}

	const initDefaultCode = "301"

	for c.Next() {
		args := c.RemainingArgs()
		matcher, err := httpserver.SetupIfMatcher(c)
		if err != nil {
			return nil, err
		}

		var hadOptionalBlock bool
		for c.NextBlock() {
			if httpserver.IfMatcherKeyword(c) {
				continue
			}

			hadOptionalBlock = true

			rule := Rule{
				RequestMatcher: matcher,
			}

			defaultCode := initDefaultCode
			// Set initial redirect code
			if len(args) == 1 {
				defaultCode = args[0]
			}

			// 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()...)
			err := initRule(&rule, defaultCode, insideArgs)
			if err != nil {
				return redirects, err
			}

			err = checkAndSaveRule(rule)
			if err != nil {
				return redirects, err
			}
		}

		if !hadOptionalBlock {
			rule := Rule{
				RequestMatcher: matcher,
			}
			err := initRule(&rule, initDefaultCode, args)
			if err != nil {
				return redirects, err
			}

			err = checkAndSaveRule(rule)
			if err != nil {
				return redirects, err
			}
		}
	}

	return redirects, nil
}
Esempio n. 4
0
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() {
		matcher, err := httpserver.SetupIfMatcher(c.Dispenser)
		if err != nil {
			return nil, err
		}
		args := c.RemainingArgs()

		var hadOptionalBlock bool
		for c.NextBlock() {
			if httpserver.IfMatcherKeyword(c.Val()) {
				continue
			}

			hadOptionalBlock = true

			var rule = Rule{
				RequestMatcher: matcher,
			}

			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{
				RequestMatcher: matcher,
			}

			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
}
Esempio n. 5
0
func rewriteParse(c *caddy.Controller) ([]Rule, error) {
	var simpleRules []Rule
	var regexpRules []Rule

	for c.Next() {
		var rule Rule
		var err error
		var base = "/"
		var pattern, to string
		var status int
		var ext []string

		args := c.RemainingArgs()

		var matcher httpserver.RequestMatcher

		switch len(args) {
		case 1:
			base = args[0]
			fallthrough
		case 0:
			// Integrate request matcher for 'if' conditions.
			matcher, err = httpserver.SetupIfMatcher(c.Dispenser)
			if err != nil {
				return nil, err
			}
		block:
			for c.NextBlock() {
				switch c.Val() {
				case "r", "regexp":
					if !c.NextArg() {
						return nil, c.ArgErr()
					}
					pattern = c.Val()
				case "to":
					args1 := c.RemainingArgs()
					if len(args1) == 0 {
						return nil, c.ArgErr()
					}
					to = strings.Join(args1, " ")
				case "ext":
					args1 := c.RemainingArgs()
					if len(args1) == 0 {
						return nil, c.ArgErr()
					}
					ext = args1
				case "status":
					if !c.NextArg() {
						return nil, c.ArgErr()
					}
					status, _ = strconv.Atoi(c.Val())
					if status < 200 || (status > 299 && status < 400) || status > 499 {
						return nil, c.Err("status must be 2xx or 4xx")
					}
				default:
					if httpserver.IfMatcherKeyword(c.Val()) {
						continue block
					}
					return nil, c.ArgErr()
				}
			}
			// ensure to or status is specified
			if to == "" && status == 0 {
				return nil, c.ArgErr()
			}
			if rule, err = NewComplexRule(base, pattern, to, status, ext, matcher); err != nil {
				return nil, err
			}
			regexpRules = append(regexpRules, rule)

		// the only unhandled case is 2 and above
		default:
			rule = NewSimpleRule(args[0], strings.Join(args[1:], " "))
			simpleRules = append(simpleRules, rule)
		}

	}

	// put simple rules in front to avoid regexp computation for them
	return append(simpleRules, regexpRules...), nil
}