0
Fork 0
mirror of https://github.com/caddyserver/caddy.git synced 2024-12-23 22:27:38 -05:00

add ability to do meta redirects

Proper Location redirects are disadvantageous in some situations.
For example, you may want a developer to know that a resource is available
via https, but you don't want an insecure call to the API to succeed.
This commit is contained in:
AJ ONeal 2015-06-10 05:44:40 +00:00
parent 3a795de828
commit d6a7dfc1a5
3 changed files with 61 additions and 5 deletions

View file

@ -26,17 +26,21 @@ func redirParse(c *Controller) ([]redirect.Rule, error) {
var rule redirect.Rule var rule redirect.Rule
args := c.RemainingArgs() args := c.RemainingArgs()
// Always set the default Code, then overwrite
rule.Code = http.StatusMovedPermanently
switch len(args) { switch len(args) {
case 1: case 1:
// To specified // To specified
rule.From = "/" rule.From = "/"
rule.To = args[0] rule.To = args[0]
rule.Code = http.StatusMovedPermanently
case 2: case 2:
// To and Code specified // To and Code specified
rule.From = "/" rule.From = "/"
rule.To = args[0] rule.To = args[0]
if code, ok := httpRedirs[args[1]]; !ok { if "meta" == args[1] {
rule.Meta = true
} else if code, ok := httpRedirs[args[1]]; !ok {
return redirects, c.Err("Invalid redirect code '" + args[1] + "'") return redirects, c.Err("Invalid redirect code '" + args[1] + "'")
} else { } else {
rule.Code = code rule.Code = code
@ -45,7 +49,9 @@ func redirParse(c *Controller) ([]redirect.Rule, error) {
// From, To, and Code specified // From, To, and Code specified
rule.From = args[0] rule.From = args[0]
rule.To = args[1] rule.To = args[1]
if code, ok := httpRedirs[args[2]]; !ok { if "meta" == args[2] {
rule.Meta = true
} else if code, ok := httpRedirs[args[2]]; !ok {
return redirects, c.Err("Invalid redirect code '" + args[2] + "'") return redirects, c.Err("Invalid redirect code '" + args[2] + "'")
} else { } else {
rule.Code = code rule.Code = code

View file

@ -3,6 +3,8 @@
package redirect package redirect
import ( import (
"fmt"
"html"
"net/http" "net/http"
"strings" "strings"
@ -20,11 +22,20 @@ func (rd Redirect) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error
for _, rule := range rd.Rules { for _, rule := range rd.Rules {
if rule.From == "/" { if rule.From == "/" {
// Catchall redirect preserves path (TODO: Standardize/formalize this behavior) // Catchall redirect preserves path (TODO: Standardize/formalize this behavior)
http.Redirect(w, r, strings.TrimSuffix(rule.To, "/")+r.URL.Path, rule.Code) newPath := strings.TrimSuffix(rule.To, "/") + r.URL.Path
if rule.Meta {
fmt.Fprintf(w, metaRedir, html.EscapeString(newPath))
} else {
http.Redirect(w, r, newPath, rule.Code)
}
return 0, nil return 0, nil
} }
if r.URL.Path == rule.From { if r.URL.Path == rule.From {
if rule.Meta {
fmt.Fprintf(w, metaRedir, html.EscapeString(rule.To))
} else {
http.Redirect(w, r, rule.To, rule.Code) http.Redirect(w, r, rule.To, rule.Code)
}
return 0, nil return 0, nil
} }
} }
@ -35,4 +46,12 @@ func (rd Redirect) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error
type Rule struct { type Rule struct {
From, To string From, To string
Code int Code int
Meta bool
} }
var metaRedir = `<html>
<head>
<meta http-equiv="refresh" content="0;URL='%s'">
</head>
<body>redirecting...</body>
</html>`

View file

@ -1,6 +1,8 @@
package redirect package redirect
import ( import (
"bytes"
"io/ioutil"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
@ -8,6 +10,35 @@ import (
"github.com/mholt/caddy/middleware" "github.com/mholt/caddy/middleware"
) )
func TestMetaRedirect(t *testing.T) {
re := Redirect{
Rules: []Rule{
{From: "/", Meta: true, To: "https://example.com/"},
{From: "/whatever", Meta: true, To: "https://example.com/whatever"},
},
}
for i, test := range re.Rules {
req, err := http.NewRequest("GET", test.From, nil)
if err != nil {
t.Fatalf("Test %d: Could not create HTTP request: %v", i, err)
}
rec := httptest.NewRecorder()
re.ServeHTTP(rec, req)
body, err := ioutil.ReadAll(rec.Body)
if err != nil {
t.Fatalf("Test %d: Could not read HTTP response body: %v", i, err)
}
expectedSnippet := `<meta http-equiv="refresh" content="0;URL='` + test.To + `'">`
if !bytes.Contains(body, []byte(expectedSnippet)) {
t.Errorf("Test %d: Expected Response Body to contain %q but was %q",
i, expectedSnippet, body)
}
}
}
func TestRedirect(t *testing.T) { func TestRedirect(t *testing.T) {
for i, test := range []struct { for i, test := range []struct {
from string from string