Commit 2c7de8f3 authored by Abiola Ibrahim's avatar Abiola Ibrahim

Merge remote-tracking branch 'upstream/master'

parents 7443fd09 b2ee6638
...@@ -59,6 +59,7 @@ var directiveOrder = []directive{ ...@@ -59,6 +59,7 @@ var directiveOrder = []directive{
{"redir", setup.Redir}, {"redir", setup.Redir},
{"ext", setup.Ext}, {"ext", setup.Ext},
{"basicauth", setup.BasicAuth}, {"basicauth", setup.BasicAuth},
{"internal", setup.Internal},
{"proxy", setup.Proxy}, {"proxy", setup.Proxy},
{"fastcgi", setup.FastCGI}, {"fastcgi", setup.FastCGI},
{"websocket", setup.WebSocket}, {"websocket", setup.WebSocket},
......
...@@ -109,6 +109,10 @@ func (d *Dispenser) NextBlock() bool { ...@@ -109,6 +109,10 @@ func (d *Dispenser) NextBlock() bool {
return false return false
} }
d.Next() d.Next()
if d.Val() == "}" {
// Open and then closed right away
return false
}
d.nesting++ d.nesting++
return true return true
} }
......
...@@ -150,8 +150,7 @@ func TestDispenser_NextBlock(t *testing.T) { ...@@ -150,8 +150,7 @@ func TestDispenser_NextBlock(t *testing.T) {
assertNextBlock(true, 4, 1) assertNextBlock(true, 4, 1)
assertNextBlock(false, 5, 0) assertNextBlock(false, 5, 0)
d.Next() // foobar2 d.Next() // foobar2
assertNextBlock(true, 8, 1) assertNextBlock(false, 8, 0) // empty block is as if it didn't exist
assertNextBlock(false, 8, 0)
} }
func TestDispenser_Args(t *testing.T) { func TestDispenser_Args(t *testing.T) {
......
package setup
import (
"fmt"
"testing"
"github.com/mholt/caddy/middleware/basicauth"
)
func TestBasicAuth(t *testing.T) {
c := newTestController(`basicauth user pwd`)
mid, err := BasicAuth(c)
if err != nil {
t.Errorf("Expected no errors, but got: %v", err)
}
if mid == nil {
t.Fatal("Expected middleware, was nil instead")
}
handler := mid(emptyNext)
myHandler, ok := handler.(basicauth.BasicAuth)
if !ok {
t.Fatalf("Expected handler to be type BasicAuth, got: %#v", handler)
}
if !sameNext(myHandler.Next, emptyNext) {
t.Error("'Next' field of handler was not set properly")
}
}
func TestBasicAuthParse(t *testing.T) {
tests := []struct {
input string
shouldErr bool
expected []basicauth.Rule
}{
{`basicauth user pwd`, false, []basicauth.Rule{
{Username: "user", Password: "pwd"},
}},
{`basicauth user pwd {
}`, false, []basicauth.Rule{
{Username: "user", Password: "pwd"},
}},
{`basicauth user pwd {
/resource1
/resource2
}`, false, []basicauth.Rule{
{Username: "user", Password: "pwd", Resources: []string{"/resource1", "/resource2"}},
}},
{`basicauth /resource user pwd`, false, []basicauth.Rule{
{Username: "user", Password: "pwd", Resources: []string{"/resource"}},
}},
{`basicauth /res1 user1 pwd1
basicauth /res2 user2 pwd2`, false, []basicauth.Rule{
{Username: "user1", Password: "pwd1", Resources: []string{"/res1"}},
{Username: "user2", Password: "pwd2", Resources: []string{"/res2"}},
}},
{`basicauth user`, true, []basicauth.Rule{}},
{`basicauth`, true, []basicauth.Rule{}},
{`basicauth /resource user pwd asdf`, true, []basicauth.Rule{}},
}
for i, test := range tests {
c := newTestController(test.input)
actual, err := basicAuthParse(c)
if err == nil && test.shouldErr {
t.Errorf("Test %d didn't error, but it should have", i)
} else if err != nil && !test.shouldErr {
t.Errorf("Test %d errored, but it shouldn't have; got '%v'", i, err)
}
if len(actual) != len(test.expected) {
t.Fatalf("Test %d expected %d rules, but got %d",
i, len(test.expected), len(actual))
}
for j, expectedRule := range test.expected {
actualRule := actual[j]
if actualRule.Username != expectedRule.Username {
t.Errorf("Test %d, rule %d: Expected username '%s', got '%s'",
i, j, expectedRule.Username, actualRule.Username)
}
if actualRule.Password != expectedRule.Password {
t.Errorf("Test %d, rule %d: Expected password '%s', got '%s'",
i, j, expectedRule.Password, actualRule.Password)
}
expectedRes := fmt.Sprintf("%v", expectedRule.Resources)
actualRes := fmt.Sprintf("%v", actualRule.Resources)
if actualRes != expectedRes {
t.Errorf("Test %d, rule %d: Expected resource list %s, but got %s",
i, j, expectedRes, actualRes)
}
}
}
}
package setup package setup
import ( import (
"fmt"
"net/http"
"strings" "strings"
"github.com/mholt/caddy/config/parse" "github.com/mholt/caddy/config/parse"
"github.com/mholt/caddy/middleware"
"github.com/mholt/caddy/server" "github.com/mholt/caddy/server"
) )
...@@ -15,3 +18,15 @@ func newTestController(input string) *Controller { ...@@ -15,3 +18,15 @@ func newTestController(input string) *Controller {
Dispenser: parse.NewDispenser("Testfile", strings.NewReader(input)), Dispenser: parse.NewDispenser("Testfile", strings.NewReader(input)),
} }
} }
// emptyNext is a no-op function that can be passed into
// middleware.Middleware functions so that the assignment
// to the Next field of the Handler can be tested.
var emptyNext = middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
return 0, nil
})
// sameNext does a pointer comparison between next1 and next2.
func sameNext(next1, next2 middleware.Handler) bool {
return fmt.Sprintf("%p", next1) == fmt.Sprintf("%p", next2)
}
package setup
import (
"testing"
"github.com/mholt/caddy/middleware/gzip"
)
func TestGzip(t *testing.T) {
c := newTestController(`gzip`)
mid, err := Gzip(c)
if err != nil {
t.Errorf("Expected no errors, but got: %v", err)
}
if mid == nil {
t.Fatal("Expected middleware, was nil instead")
}
handler := mid(emptyNext)
myHandler, ok := handler.(gzip.Gzip)
if !ok {
t.Fatalf("Expected handler to be type Gzip, got: %#v", handler)
}
if !sameNext(myHandler.Next, emptyNext) {
t.Error("'Next' field of handler was not set properly")
}
}
package setup
import (
"github.com/mholt/caddy/middleware"
"github.com/mholt/caddy/middleware/internal"
)
// Internal configures a new Internal middleware instance.
func Internal(c *Controller) (middleware.Middleware, error) {
paths, err := internalParse(c)
if err != nil {
return nil, err
}
return func(next middleware.Handler) middleware.Handler {
return internal.Internal{Next: next, Paths: paths}
}, nil
}
func internalParse(c *Controller) ([]string, error) {
var paths []string
for c.Next() {
if !c.NextArg() {
return paths, c.ArgErr()
}
paths = append(paths, c.Val())
}
return paths, nil
}
package setup
import (
"testing"
"github.com/mholt/caddy/middleware/rewrite"
)
func TestRewrite(t *testing.T) {
c := newTestController(`rewrite /from /to`)
mid, err := Rewrite(c)
if err != nil {
t.Errorf("Expected no errors, but got: %v", err)
}
if mid == nil {
t.Fatal("Expected middleware, was nil instead")
}
handler := mid(emptyNext)
myHandler, ok := handler.(rewrite.Rewrite)
if !ok {
t.Fatalf("Expected handler to be type Rewrite, got: %#v", handler)
}
if !sameNext(myHandler.Next, emptyNext) {
t.Error("'Next' field of handler was not set properly")
}
if len(myHandler.Rules) != 1 {
t.Errorf("Expected handler to have %d rule, has %d instead", 1, len(myHandler.Rules))
}
}
func TestRewriteParse(t *testing.T) {
tests := []struct {
input string
shouldErr bool
expected []rewrite.Rule
}{
{`rewrite /from /to`, false, []rewrite.Rule{
{From: "/from", To: "/to"},
}},
{`rewrite /from /to
rewrite a b`, false, []rewrite.Rule{
{From: "/from", To: "/to"},
{From: "a", To: "b"},
}},
{`rewrite a`, true, []rewrite.Rule{}},
{`rewrite`, true, []rewrite.Rule{}},
{`rewrite a b c`, true, []rewrite.Rule{
{From: "a", To: "b"},
}},
}
for i, test := range tests {
c := newTestController(test.input)
actual, err := rewriteParse(c)
if err == nil && test.shouldErr {
t.Errorf("Test %d didn't error, but it should have", i)
} else if err != nil && !test.shouldErr {
t.Errorf("Test %d errored, but it shouldn't have; got '%v'", i, err)
}
if len(actual) != len(test.expected) {
t.Fatalf("Test %d expected %d rules, but got %d",
i, len(test.expected), len(actual))
}
for j, expectedRule := range test.expected {
actualRule := actual[j]
if actualRule.From != expectedRule.From {
t.Errorf("Test %d, rule %d: Expected From=%s, got %s",
i, j, expectedRule.From, actualRule.From)
}
if actualRule.To != expectedRule.To {
t.Errorf("Test %d, rule %d: Expected To=%s, got %s",
i, j, expectedRule.To, actualRule.To)
}
}
}
}
...@@ -24,6 +24,7 @@ var ( ...@@ -24,6 +24,7 @@ var (
http2 bool // TODO: temporary flag until http2 is standard http2 bool // TODO: temporary flag until http2 is standard
quiet bool quiet bool
cpu string cpu string
version bool
) )
func init() { func init() {
...@@ -34,6 +35,7 @@ func init() { ...@@ -34,6 +35,7 @@ func init() {
flag.StringVar(&config.Root, "root", config.DefaultRoot, "Root path to default site") flag.StringVar(&config.Root, "root", config.DefaultRoot, "Root path to default site")
flag.StringVar(&config.Host, "host", config.DefaultHost, "Default host") flag.StringVar(&config.Host, "host", config.DefaultHost, "Default host")
flag.StringVar(&config.Port, "port", config.DefaultPort, "Default port") flag.StringVar(&config.Port, "port", config.DefaultPort, "Default port")
flag.BoolVar(&version, "version", false, "Show version")
config.AppName = "Caddy" config.AppName = "Caddy"
config.AppVersion = "0.6.0" config.AppVersion = "0.6.0"
...@@ -42,6 +44,11 @@ func init() { ...@@ -42,6 +44,11 @@ func init() {
func main() { func main() {
flag.Parse() flag.Parse()
if version {
fmt.Printf("%s %s\n", config.AppName, config.AppVersion)
os.Exit(0)
}
var wg sync.WaitGroup var wg sync.WaitGroup
// Set CPU cap // Set CPU cap
......
// The package internal provides a simple middleware that (a) prevents access
// to internal locations and (b) allows to return files from internal location
// by setting a special header, e.g. in a proxy response.
package internal
import (
"net/http"
"github.com/mholt/caddy/middleware"
)
// Internal middleware protects internal locations from external requests -
// but allows access from the inside by using a special HTTP header.
type Internal struct {
Next middleware.Handler
Paths []string
}
const (
redirectHeader string = "X-Accel-Redirect"
maxRedirectCount int = 10
)
func isInternalRedirect(w http.ResponseWriter) bool {
return w.Header().Get(redirectHeader) != ""
}
// ServeHTTP implements the middlware.Handler interface.
func (i Internal) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
// Internal location requested? -> Not found.
for _, prefix := range i.Paths {
if middleware.Path(r.URL.Path).Matches(prefix) {
return http.StatusNotFound, nil
}
}
// Use internal response writer to ignore responses that will be
// redirected to internal locations
iw := internalResponseWriter{ResponseWriter: w}
status, err := i.Next.ServeHTTP(iw, r)
for c := 0; c < maxRedirectCount && isInternalRedirect(iw); c++ {
// Redirect - adapt request URL path and send it again
// "down the chain"
r.URL.Path = iw.Header().Get(redirectHeader)
iw.ClearHeader()
status, err = i.Next.ServeHTTP(iw, r)
}
if isInternalRedirect(iw) {
// Too many redirect cycles
iw.ClearHeader()
return http.StatusInternalServerError, nil
}
return status, err
}
// internalResponseWriter wraps the underlying http.ResponseWriter and ignores
// calls to Write and WriteHeader if the response should be redirected to an
// internal location.
type internalResponseWriter struct {
http.ResponseWriter
}
// ClearHeader removes all header fields that are already set.
func (w internalResponseWriter) ClearHeader() {
for k := range w.Header() {
w.Header().Del(k)
}
}
// WriteHeader ignores the call if the response should be redirected to an
// internal location.
func (w internalResponseWriter) WriteHeader(code int) {
if !isInternalRedirect(w) {
w.ResponseWriter.WriteHeader(code)
}
}
// Write ignores the call if the response should be redirected to an internal
// location.
func (w internalResponseWriter) Write(b []byte) (int, error) {
if isInternalRedirect(w) {
return 0, nil
} else {
return w.ResponseWriter.Write(b)
}
}
package rewrite
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
"github.com/mholt/caddy/middleware"
)
func TestRewrite(t *testing.T) {
rw := Rewrite{
Next: middleware.HandlerFunc(urlPrinter),
Rules: []Rule{
{From: "/from", To: "/to"},
{From: "/a", To: "/b"},
},
}
tests := []struct {
from string
expectedTo string
}{
{"/from", "/to"},
{"/a", "/b"},
{"/aa", "/aa"},
{"/", "/"},
{"/a?foo=bar", "/b?foo=bar"},
{"/asdf?foo=bar", "/asdf?foo=bar"},
{"/foo#bar", "/foo#bar"},
{"/a#foo", "/b#foo"},
}
for i, test := range tests {
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()
rw.ServeHTTP(rec, req)
if rec.Body.String() != test.expectedTo {
t.Errorf("Test %d: Expected URL to be '%s' but was '%s'",
i, test.expectedTo, rec.Body.String())
}
}
}
func urlPrinter(w http.ResponseWriter, r *http.Request) (int, error) {
fmt.Fprintf(w, r.URL.String())
return 0, nil
}
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