mirror of
https://github.com/caddyserver/caddy.git
synced 2025-07-07 18:24:53 -04:00
Complete test coverage for replacer for Go
This commit is contained in:
commit
ed4148f20e
@ -2,6 +2,7 @@ language: go
|
|||||||
|
|
||||||
go:
|
go:
|
||||||
- 1.4
|
- 1.4
|
||||||
|
- 1.5
|
||||||
- tip
|
- tip
|
||||||
|
|
||||||
script: go test ./...
|
script: go test ./...
|
||||||
|
@ -43,6 +43,7 @@ By default, Caddy serves the current directory at [localhost:2015](http://localh
|
|||||||
|
|
||||||
Caddy accepts some flags from the command line. Run `caddy -h` to view the help for flags. You can also pipe a Caddyfile into the caddy command.
|
Caddy accepts some flags from the command line. Run `caddy -h` to view the help for flags. You can also pipe a Caddyfile into the caddy command.
|
||||||
|
|
||||||
|
**Running as root:** We advise against this; use setcap instead, like so: `setcap cap_net_bind_service=+ep ./caddy` This will allow you to listen on ports below 1024 (like 80 and 443).
|
||||||
|
|
||||||
|
|
||||||
#### Docker Container
|
#### Docker Container
|
||||||
|
@ -119,6 +119,11 @@ func (d *Dispenser) NextBlock() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Dispenser) IncrNest() {
|
||||||
|
d.nesting++
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Val gets the text of the current token. If there is no token
|
// Val gets the text of the current token. If there is no token
|
||||||
// loaded, it returns empty string.
|
// loaded, it returns empty string.
|
||||||
func (d *Dispenser) Val() string {
|
func (d *Dispenser) Val() string {
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
package setup
|
package setup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/mholt/caddy/middleware"
|
"github.com/mholt/caddy/middleware"
|
||||||
"github.com/mholt/caddy/middleware/basicauth"
|
"github.com/mholt/caddy/middleware/basicauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BasicAuth configures a new BasicAuth middleware instance.
|
// BasicAuth configures a new BasicAuth middleware instance.
|
||||||
func BasicAuth(c *Controller) (middleware.Middleware, error) {
|
func BasicAuth(c *Controller) (middleware.Middleware, error) {
|
||||||
|
root := c.Root
|
||||||
|
|
||||||
rules, err := basicAuthParse(c)
|
rules, err := basicAuthParse(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -16,6 +20,7 @@ func BasicAuth(c *Controller) (middleware.Middleware, error) {
|
|||||||
|
|
||||||
return func(next middleware.Handler) middleware.Handler {
|
return func(next middleware.Handler) middleware.Handler {
|
||||||
basic.Next = next
|
basic.Next = next
|
||||||
|
basic.SiteRoot = root
|
||||||
return basic
|
return basic
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -23,6 +28,7 @@ func BasicAuth(c *Controller) (middleware.Middleware, error) {
|
|||||||
func basicAuthParse(c *Controller) ([]basicauth.Rule, error) {
|
func basicAuthParse(c *Controller) ([]basicauth.Rule, error) {
|
||||||
var rules []basicauth.Rule
|
var rules []basicauth.Rule
|
||||||
|
|
||||||
|
var err error
|
||||||
for c.Next() {
|
for c.Next() {
|
||||||
var rule basicauth.Rule
|
var rule basicauth.Rule
|
||||||
|
|
||||||
@ -31,7 +37,10 @@ func basicAuthParse(c *Controller) ([]basicauth.Rule, error) {
|
|||||||
switch len(args) {
|
switch len(args) {
|
||||||
case 2:
|
case 2:
|
||||||
rule.Username = args[0]
|
rule.Username = args[0]
|
||||||
rule.Password = args[1]
|
if rule.Password, err = passwordMatcher(rule.Username, args[1], c.Root); err != nil {
|
||||||
|
return rules, c.Errf("Get password matcher from %s: %v", c.Val(), err)
|
||||||
|
}
|
||||||
|
|
||||||
for c.NextBlock() {
|
for c.NextBlock() {
|
||||||
rule.Resources = append(rule.Resources, c.Val())
|
rule.Resources = append(rule.Resources, c.Val())
|
||||||
if c.NextArg() {
|
if c.NextArg() {
|
||||||
@ -41,7 +50,9 @@ func basicAuthParse(c *Controller) ([]basicauth.Rule, error) {
|
|||||||
case 3:
|
case 3:
|
||||||
rule.Resources = append(rule.Resources, args[0])
|
rule.Resources = append(rule.Resources, args[0])
|
||||||
rule.Username = args[1]
|
rule.Username = args[1]
|
||||||
rule.Password = args[2]
|
if rule.Password, err = passwordMatcher(rule.Username, args[2], c.Root); err != nil {
|
||||||
|
return rules, c.Errf("Get password matcher from %s: %v", c.Val(), err)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return rules, c.ArgErr()
|
return rules, c.ArgErr()
|
||||||
}
|
}
|
||||||
@ -51,3 +62,11 @@ func basicAuthParse(c *Controller) ([]basicauth.Rule, error) {
|
|||||||
|
|
||||||
return rules, nil
|
return rules, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func passwordMatcher(username, passw, siteRoot string) (basicauth.PasswordMatcher, error) {
|
||||||
|
if !strings.HasPrefix(passw, "htpasswd=") {
|
||||||
|
return basicauth.PlainMatcher(passw), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return basicauth.GetHtpasswdMatcher(passw[9:], username, siteRoot)
|
||||||
|
}
|
||||||
|
@ -2,6 +2,9 @@ package setup
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/mholt/caddy/middleware/basicauth"
|
"github.com/mholt/caddy/middleware/basicauth"
|
||||||
@ -30,35 +33,57 @@ func TestBasicAuth(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestBasicAuthParse(t *testing.T) {
|
func TestBasicAuthParse(t *testing.T) {
|
||||||
|
htpasswdPasswd := "IedFOuGmTpT8"
|
||||||
|
htpasswdFile := `sha1:{SHA}dcAUljwz99qFjYR0YLTXx0RqLww=
|
||||||
|
md5:$apr1$l42y8rex$pOA2VJ0x/0TwaFeAF9nX61`
|
||||||
|
|
||||||
|
var skipHtpassword bool
|
||||||
|
htfh, err := ioutil.TempFile("", "basicauth-")
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("Error creating temp file (%v), will skip htpassword test")
|
||||||
|
skipHtpassword = true
|
||||||
|
} else {
|
||||||
|
if _, err = htfh.Write([]byte(htpasswdFile)); err != nil {
|
||||||
|
t.Fatalf("write htpasswd file %q: %v", htfh.Name(), err)
|
||||||
|
}
|
||||||
|
htfh.Close()
|
||||||
|
defer os.Remove(htfh.Name())
|
||||||
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
input string
|
input string
|
||||||
shouldErr bool
|
shouldErr bool
|
||||||
|
password string
|
||||||
expected []basicauth.Rule
|
expected []basicauth.Rule
|
||||||
}{
|
}{
|
||||||
{`basicauth user pwd`, false, []basicauth.Rule{
|
{`basicauth user pwd`, false, "pwd", []basicauth.Rule{
|
||||||
{Username: "user", Password: "pwd"},
|
{Username: "user"},
|
||||||
}},
|
}},
|
||||||
{`basicauth user pwd {
|
{`basicauth user pwd {
|
||||||
}`, false, []basicauth.Rule{
|
}`, false, "pwd", []basicauth.Rule{
|
||||||
{Username: "user", Password: "pwd"},
|
{Username: "user"},
|
||||||
}},
|
}},
|
||||||
{`basicauth user pwd {
|
{`basicauth user pwd {
|
||||||
/resource1
|
/resource1
|
||||||
/resource2
|
/resource2
|
||||||
}`, false, []basicauth.Rule{
|
}`, false, "pwd", []basicauth.Rule{
|
||||||
{Username: "user", Password: "pwd", Resources: []string{"/resource1", "/resource2"}},
|
{Username: "user", Resources: []string{"/resource1", "/resource2"}},
|
||||||
}},
|
}},
|
||||||
{`basicauth /resource user pwd`, false, []basicauth.Rule{
|
{`basicauth /resource user pwd`, false, "pwd", []basicauth.Rule{
|
||||||
{Username: "user", Password: "pwd", Resources: []string{"/resource"}},
|
{Username: "user", Resources: []string{"/resource"}},
|
||||||
}},
|
}},
|
||||||
{`basicauth /res1 user1 pwd1
|
{`basicauth /res1 user1 pwd1
|
||||||
basicauth /res2 user2 pwd2`, false, []basicauth.Rule{
|
basicauth /res2 user2 pwd2`, false, "pwd", []basicauth.Rule{
|
||||||
{Username: "user1", Password: "pwd1", Resources: []string{"/res1"}},
|
{Username: "user1", Resources: []string{"/res1"}},
|
||||||
{Username: "user2", Password: "pwd2", Resources: []string{"/res2"}},
|
{Username: "user2", Resources: []string{"/res2"}},
|
||||||
|
}},
|
||||||
|
{`basicauth user`, true, "", []basicauth.Rule{}},
|
||||||
|
{`basicauth`, true, "", []basicauth.Rule{}},
|
||||||
|
{`basicauth /resource user pwd asdf`, true, "", []basicauth.Rule{}},
|
||||||
|
|
||||||
|
{`basicauth sha1 htpasswd=` + htfh.Name(), false, htpasswdPasswd, []basicauth.Rule{
|
||||||
|
{Username: "sha1"},
|
||||||
}},
|
}},
|
||||||
{`basicauth user`, true, []basicauth.Rule{}},
|
|
||||||
{`basicauth`, true, []basicauth.Rule{}},
|
|
||||||
{`basicauth /resource user pwd asdf`, true, []basicauth.Rule{}},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
@ -84,9 +109,16 @@ func TestBasicAuthParse(t *testing.T) {
|
|||||||
i, j, expectedRule.Username, actualRule.Username)
|
i, j, expectedRule.Username, actualRule.Username)
|
||||||
}
|
}
|
||||||
|
|
||||||
if actualRule.Password != expectedRule.Password {
|
if strings.Contains(test.input, "htpasswd=") && skipHtpassword {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pwd := test.password
|
||||||
|
if len(actual) > 1 {
|
||||||
|
pwd = fmt.Sprintf("%s%d", pwd, j+1)
|
||||||
|
}
|
||||||
|
if !actualRule.Password(pwd) || actualRule.Password(test.password+"!") {
|
||||||
t.Errorf("Test %d, rule %d: Expected password '%s', got '%s'",
|
t.Errorf("Test %d, rule %d: Expected password '%s', got '%s'",
|
||||||
i, j, expectedRule.Password, actualRule.Password)
|
i, j, test.password, actualRule.Password)
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedRes := fmt.Sprintf("%v", expectedRule.Resources)
|
expectedRes := fmt.Sprintf("%v", expectedRule.Resources)
|
||||||
|
@ -23,25 +23,35 @@ func Errors(c *Controller) (middleware.Middleware, error) {
|
|||||||
// Open the log file for writing when the server starts
|
// Open the log file for writing when the server starts
|
||||||
c.Startup = append(c.Startup, func() error {
|
c.Startup = append(c.Startup, func() error {
|
||||||
var err error
|
var err error
|
||||||
var file io.Writer
|
var writer io.Writer
|
||||||
|
|
||||||
if handler.LogFile == "stdout" {
|
if handler.LogFile == "stdout" {
|
||||||
file = os.Stdout
|
writer = os.Stdout
|
||||||
} else if handler.LogFile == "stderr" {
|
} else if handler.LogFile == "stderr" {
|
||||||
file = os.Stderr
|
writer = os.Stderr
|
||||||
} else if handler.LogFile == "syslog" {
|
} else if handler.LogFile == "syslog" {
|
||||||
file, err = gsyslog.NewLogger(gsyslog.LOG_ERR, "LOCAL0", "caddy")
|
writer, err = gsyslog.NewLogger(gsyslog.LOG_ERR, "LOCAL0", "caddy")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else if handler.LogFile != "" {
|
} else if handler.LogFile != "" {
|
||||||
|
var file *os.File
|
||||||
file, err = os.OpenFile(handler.LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
|
file, err = os.OpenFile(handler.LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if handler.LogRoller != nil {
|
||||||
|
file.Close()
|
||||||
|
|
||||||
|
handler.LogRoller.Filename = handler.LogFile
|
||||||
|
|
||||||
|
writer = handler.LogRoller.GetLogWriter()
|
||||||
|
} else {
|
||||||
|
writer = file
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handler.Log = log.New(file, "", 0)
|
handler.Log = log.New(writer, "", 0)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -71,6 +81,16 @@ func errorsParse(c *Controller) (*errors.ErrorHandler, error) {
|
|||||||
|
|
||||||
if what == "log" {
|
if what == "log" {
|
||||||
handler.LogFile = where
|
handler.LogFile = where
|
||||||
|
if c.NextArg() {
|
||||||
|
if c.Val() == "{" {
|
||||||
|
c.IncrNest()
|
||||||
|
logRoller, err := parseRoller(c)
|
||||||
|
if err != nil {
|
||||||
|
return hadBlock, err
|
||||||
|
}
|
||||||
|
handler.LogRoller = logRoller
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Error page; ensure it exists
|
// Error page; ensure it exists
|
||||||
where = path.Join(c.Root, where)
|
where = path.Join(c.Root, where)
|
||||||
@ -91,6 +111,10 @@ func errorsParse(c *Controller) (*errors.ErrorHandler, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for c.Next() {
|
for c.Next() {
|
||||||
|
// weird hack to avoid having the handler values overwritten.
|
||||||
|
if c.Val() == "}" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
// Configuration may be in a block
|
// Configuration may be in a block
|
||||||
hadBlock, err := optionalBlock()
|
hadBlock, err := optionalBlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
139
config/setup/errors_test.go
Normal file
139
config/setup/errors_test.go
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
package setup
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mholt/caddy/middleware"
|
||||||
|
"github.com/mholt/caddy/middleware/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestErrors(t *testing.T) {
|
||||||
|
|
||||||
|
c := NewTestController(`errors`)
|
||||||
|
|
||||||
|
mid, err := Errors(c)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected no errors, got: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if mid == nil {
|
||||||
|
t.Fatal("Expected middleware, was nil instead")
|
||||||
|
}
|
||||||
|
|
||||||
|
handler := mid(EmptyNext)
|
||||||
|
myHandler, ok := handler.(*errors.ErrorHandler)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Expected handler to be type ErrorHandler, got: %#v", handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
if myHandler.LogFile != errors.DefaultLogFilename {
|
||||||
|
t.Errorf("Expected %s as the default LogFile", errors.DefaultLogFilename)
|
||||||
|
}
|
||||||
|
if myHandler.LogRoller != nil {
|
||||||
|
t.Errorf("Expected LogRoller to be nil, got: %v", *myHandler.LogRoller)
|
||||||
|
}
|
||||||
|
if !SameNext(myHandler.Next, EmptyNext) {
|
||||||
|
t.Error("'Next' field of handler was not set properly")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorsParse(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
inputErrorsRules string
|
||||||
|
shouldErr bool
|
||||||
|
expectedErrorHandler errors.ErrorHandler
|
||||||
|
}{
|
||||||
|
{`errors`, false, errors.ErrorHandler{
|
||||||
|
LogFile: errors.DefaultLogFilename,
|
||||||
|
}},
|
||||||
|
{`errors errors.txt`, false, errors.ErrorHandler{
|
||||||
|
LogFile: "errors.txt",
|
||||||
|
}},
|
||||||
|
{`errors { log errors.txt
|
||||||
|
404 404.html
|
||||||
|
500 500.html
|
||||||
|
}`, false, errors.ErrorHandler{
|
||||||
|
LogFile: "errors.txt",
|
||||||
|
ErrorPages: map[int]string{
|
||||||
|
404: "404.html",
|
||||||
|
500: "500.html",
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
{`errors { log errors.txt { size 2 age 10 keep 3 } }`, false, errors.ErrorHandler{
|
||||||
|
LogFile: "errors.txt",
|
||||||
|
LogRoller: &middleware.LogRoller{
|
||||||
|
MaxSize: 2,
|
||||||
|
MaxAge: 10,
|
||||||
|
MaxBackups: 3,
|
||||||
|
LocalTime: true,
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
{`errors { log errors.txt {
|
||||||
|
size 3
|
||||||
|
age 11
|
||||||
|
keep 5
|
||||||
|
}
|
||||||
|
404 404.html
|
||||||
|
503 503.html
|
||||||
|
}`, false, errors.ErrorHandler{
|
||||||
|
LogFile: "errors.txt",
|
||||||
|
ErrorPages: map[int]string{
|
||||||
|
404: "404.html",
|
||||||
|
503: "503.html",
|
||||||
|
},
|
||||||
|
LogRoller: &middleware.LogRoller{
|
||||||
|
MaxSize: 3,
|
||||||
|
MaxAge: 11,
|
||||||
|
MaxBackups: 5,
|
||||||
|
LocalTime: true,
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
for i, test := range tests {
|
||||||
|
c := NewTestController(test.inputErrorsRules)
|
||||||
|
actualErrorsRule, err := errorsParse(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 actualErrorsRule.LogFile != test.expectedErrorHandler.LogFile {
|
||||||
|
t.Errorf("Test %d expected LogFile to be %s , but got %s",
|
||||||
|
i, test.expectedErrorHandler.LogFile, actualErrorsRule.LogFile)
|
||||||
|
}
|
||||||
|
if actualErrorsRule.LogRoller != nil && test.expectedErrorHandler.LogRoller == nil || actualErrorsRule.LogRoller == nil && test.expectedErrorHandler.LogRoller != nil {
|
||||||
|
t.Fatalf("Test %d expected LogRoller to be %v, but got %v",
|
||||||
|
i, test.expectedErrorHandler.LogRoller, actualErrorsRule.LogRoller)
|
||||||
|
}
|
||||||
|
if len(actualErrorsRule.ErrorPages) != len(test.expectedErrorHandler.ErrorPages) {
|
||||||
|
t.Fatalf("Test %d expected %d no of Error pages, but got %d ",
|
||||||
|
i, len(test.expectedErrorHandler.ErrorPages), len(actualErrorsRule.ErrorPages))
|
||||||
|
}
|
||||||
|
if actualErrorsRule.LogRoller != nil && test.expectedErrorHandler.LogRoller != nil {
|
||||||
|
if actualErrorsRule.LogRoller.Filename != test.expectedErrorHandler.LogRoller.Filename {
|
||||||
|
t.Fatalf("Test %d expected LogRoller Filename to be %s, but got %s",
|
||||||
|
i, test.expectedErrorHandler.LogRoller.Filename, actualErrorsRule.LogRoller.Filename)
|
||||||
|
}
|
||||||
|
if actualErrorsRule.LogRoller.MaxAge != test.expectedErrorHandler.LogRoller.MaxAge {
|
||||||
|
t.Fatalf("Test %d expected LogRoller MaxAge to be %d, but got %d",
|
||||||
|
i, test.expectedErrorHandler.LogRoller.MaxAge, actualErrorsRule.LogRoller.MaxAge)
|
||||||
|
}
|
||||||
|
if actualErrorsRule.LogRoller.MaxBackups != test.expectedErrorHandler.LogRoller.MaxBackups {
|
||||||
|
t.Fatalf("Test %d expected LogRoller MaxBackups to be %d, but got %d",
|
||||||
|
i, test.expectedErrorHandler.LogRoller.MaxBackups, actualErrorsRule.LogRoller.MaxBackups)
|
||||||
|
}
|
||||||
|
if actualErrorsRule.LogRoller.MaxSize != test.expectedErrorHandler.LogRoller.MaxSize {
|
||||||
|
t.Fatalf("Test %d expected LogRoller MaxSize to be %d, but got %d",
|
||||||
|
i, test.expectedErrorHandler.LogRoller.MaxSize, actualErrorsRule.LogRoller.MaxSize)
|
||||||
|
}
|
||||||
|
if actualErrorsRule.LogRoller.LocalTime != test.expectedErrorHandler.LogRoller.LocalTime {
|
||||||
|
t.Fatalf("Test %d expected LogRoller LocalTime to be %t, but got %t",
|
||||||
|
i, test.expectedErrorHandler.LogRoller.LocalTime, actualErrorsRule.LogRoller.LocalTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -22,25 +22,33 @@ func Log(c *Controller) (middleware.Middleware, error) {
|
|||||||
c.Startup = append(c.Startup, func() error {
|
c.Startup = append(c.Startup, func() error {
|
||||||
for i := 0; i < len(rules); i++ {
|
for i := 0; i < len(rules); i++ {
|
||||||
var err error
|
var err error
|
||||||
var file io.Writer
|
var writer io.Writer
|
||||||
|
|
||||||
if rules[i].OutputFile == "stdout" {
|
if rules[i].OutputFile == "stdout" {
|
||||||
file = os.Stdout
|
writer = os.Stdout
|
||||||
} else if rules[i].OutputFile == "stderr" {
|
} else if rules[i].OutputFile == "stderr" {
|
||||||
file = os.Stderr
|
writer = os.Stderr
|
||||||
} else if rules[i].OutputFile == "syslog" {
|
} else if rules[i].OutputFile == "syslog" {
|
||||||
file, err = gsyslog.NewLogger(gsyslog.LOG_INFO, "LOCAL0", "caddy")
|
writer, err = gsyslog.NewLogger(gsyslog.LOG_INFO, "LOCAL0", "caddy")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
var file *os.File
|
||||||
file, err = os.OpenFile(rules[i].OutputFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
|
file, err = os.OpenFile(rules[i].OutputFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if rules[i].Roller != nil {
|
||||||
|
file.Close()
|
||||||
|
rules[i].Roller.Filename = rules[i].OutputFile
|
||||||
|
writer = rules[i].Roller.GetLogWriter()
|
||||||
|
} else {
|
||||||
|
writer = file
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rules[i].Log = log.New(file, "", 0)
|
rules[i].Log = log.New(writer, "", 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -57,12 +65,33 @@ func logParse(c *Controller) ([]caddylog.Rule, error) {
|
|||||||
for c.Next() {
|
for c.Next() {
|
||||||
args := c.RemainingArgs()
|
args := c.RemainingArgs()
|
||||||
|
|
||||||
|
var logRoller *middleware.LogRoller
|
||||||
|
if c.NextBlock() {
|
||||||
|
if c.Val() == "rotate" {
|
||||||
|
if c.NextArg() {
|
||||||
|
if c.Val() == "{" {
|
||||||
|
var err error
|
||||||
|
logRoller, err = parseRoller(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// This part doesn't allow having something after the rotate block
|
||||||
|
if c.Next() {
|
||||||
|
if c.Val() != "}" {
|
||||||
|
return nil, c.ArgErr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
// Nothing specified; use defaults
|
// Nothing specified; use defaults
|
||||||
rules = append(rules, caddylog.Rule{
|
rules = append(rules, caddylog.Rule{
|
||||||
PathScope: "/",
|
PathScope: "/",
|
||||||
OutputFile: caddylog.DefaultLogFilename,
|
OutputFile: caddylog.DefaultLogFilename,
|
||||||
Format: caddylog.DefaultLogFormat,
|
Format: caddylog.DefaultLogFormat,
|
||||||
|
Roller: logRoller,
|
||||||
})
|
})
|
||||||
} else if len(args) == 1 {
|
} else if len(args) == 1 {
|
||||||
// Only an output file specified
|
// Only an output file specified
|
||||||
@ -70,6 +99,7 @@ func logParse(c *Controller) ([]caddylog.Rule, error) {
|
|||||||
PathScope: "/",
|
PathScope: "/",
|
||||||
OutputFile: args[0],
|
OutputFile: args[0],
|
||||||
Format: caddylog.DefaultLogFormat,
|
Format: caddylog.DefaultLogFormat,
|
||||||
|
Roller: logRoller,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// Path scope, output file, and maybe a format specified
|
// Path scope, output file, and maybe a format specified
|
||||||
@ -91,6 +121,7 @@ func logParse(c *Controller) ([]caddylog.Rule, error) {
|
|||||||
PathScope: args[0],
|
PathScope: args[0],
|
||||||
OutputFile: args[1],
|
OutputFile: args[1],
|
||||||
Format: format,
|
Format: format,
|
||||||
|
Roller: logRoller,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package setup
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mholt/caddy/middleware"
|
||||||
caddylog "github.com/mholt/caddy/middleware/log"
|
caddylog "github.com/mholt/caddy/middleware/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -36,6 +37,9 @@ func TestLog(t *testing.T) {
|
|||||||
if myHandler.Rules[0].Format != caddylog.DefaultLogFormat {
|
if myHandler.Rules[0].Format != caddylog.DefaultLogFormat {
|
||||||
t.Errorf("Expected %s as the default Log Format", caddylog.DefaultLogFormat)
|
t.Errorf("Expected %s as the default Log Format", caddylog.DefaultLogFormat)
|
||||||
}
|
}
|
||||||
|
if myHandler.Rules[0].Roller != nil {
|
||||||
|
t.Errorf("Expected Roller to be nil, got: %v", *myHandler.Rules[0].Roller)
|
||||||
|
}
|
||||||
if !SameNext(myHandler.Next, EmptyNext) {
|
if !SameNext(myHandler.Next, EmptyNext) {
|
||||||
t.Error("'Next' field of handler was not set properly")
|
t.Error("'Next' field of handler was not set properly")
|
||||||
}
|
}
|
||||||
@ -98,6 +102,17 @@ func TestLogParse(t *testing.T) {
|
|||||||
OutputFile: "log.txt",
|
OutputFile: "log.txt",
|
||||||
Format: "{when}",
|
Format: "{when}",
|
||||||
}}},
|
}}},
|
||||||
|
{`log access.log { rotate { size 2 age 10 keep 3 } }`, false, []caddylog.Rule{{
|
||||||
|
PathScope: "/",
|
||||||
|
OutputFile: "access.log",
|
||||||
|
Format: caddylog.DefaultLogFormat,
|
||||||
|
Roller: &middleware.LogRoller{
|
||||||
|
MaxSize: 2,
|
||||||
|
MaxAge: 10,
|
||||||
|
MaxBackups: 3,
|
||||||
|
LocalTime: true,
|
||||||
|
},
|
||||||
|
}}},
|
||||||
}
|
}
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
c := NewTestController(test.inputLogRules)
|
c := NewTestController(test.inputLogRules)
|
||||||
@ -128,6 +143,32 @@ func TestLogParse(t *testing.T) {
|
|||||||
t.Errorf("Test %d expected %dth LogRule Format to be %s , but got %s",
|
t.Errorf("Test %d expected %dth LogRule Format to be %s , but got %s",
|
||||||
i, j, test.expectedLogRules[j].Format, actualLogRule.Format)
|
i, j, test.expectedLogRules[j].Format, actualLogRule.Format)
|
||||||
}
|
}
|
||||||
|
if actualLogRule.Roller != nil && test.expectedLogRules[j].Roller == nil || actualLogRule.Roller == nil && test.expectedLogRules[j].Roller != nil {
|
||||||
|
t.Fatalf("Test %d expected %dth LogRule Roller to be %v, but got %v",
|
||||||
|
i, j, test.expectedLogRules[j].Roller, actualLogRule.Roller)
|
||||||
|
}
|
||||||
|
if actualLogRule.Roller != nil && test.expectedLogRules[j].Roller != nil {
|
||||||
|
if actualLogRule.Roller.Filename != test.expectedLogRules[j].Roller.Filename {
|
||||||
|
t.Fatalf("Test %d expected %dth LogRule Roller Filename to be %s, but got %s",
|
||||||
|
i, j, test.expectedLogRules[j].Roller.Filename, actualLogRule.Roller.Filename)
|
||||||
|
}
|
||||||
|
if actualLogRule.Roller.MaxAge != test.expectedLogRules[j].Roller.MaxAge {
|
||||||
|
t.Fatalf("Test %d expected %dth LogRule Roller MaxAge to be %d, but got %d",
|
||||||
|
i, j, test.expectedLogRules[j].Roller.MaxAge, actualLogRule.Roller.MaxAge)
|
||||||
|
}
|
||||||
|
if actualLogRule.Roller.MaxBackups != test.expectedLogRules[j].Roller.MaxBackups {
|
||||||
|
t.Fatalf("Test %d expected %dth LogRule Roller MaxBackups to be %d, but got %d",
|
||||||
|
i, j, test.expectedLogRules[j].Roller.MaxBackups, actualLogRule.Roller.MaxBackups)
|
||||||
|
}
|
||||||
|
if actualLogRule.Roller.MaxSize != test.expectedLogRules[j].Roller.MaxSize {
|
||||||
|
t.Fatalf("Test %d expected %dth LogRule Roller MaxSize to be %d, but got %d",
|
||||||
|
i, j, test.expectedLogRules[j].Roller.MaxSize, actualLogRule.Roller.MaxSize)
|
||||||
|
}
|
||||||
|
if actualLogRule.Roller.LocalTime != test.expectedLogRules[j].Roller.LocalTime {
|
||||||
|
t.Fatalf("Test %d expected %dth LogRule Roller LocalTime to be %t, but got %t",
|
||||||
|
i, j, test.expectedLogRules[j].Roller.LocalTime, actualLogRule.Roller.LocalTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ func TestMarkdownStaticGen(t *testing.T) {
|
|||||||
|
|
||||||
fp := filepath.Join(c.Root, markdown.DefaultStaticDir)
|
fp := filepath.Join(c.Root, markdown.DefaultStaticDir)
|
||||||
if err = os.RemoveAll(fp); err != nil {
|
if err = os.RemoveAll(fp); err != nil {
|
||||||
t.Errorf("Error while removing the generated static files: ", err)
|
t.Errorf("Error while removing the generated static files: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
40
config/setup/roller.go
Normal file
40
config/setup/roller.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package setup
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/mholt/caddy/middleware"
|
||||||
|
)
|
||||||
|
|
||||||
|
func parseRoller(c *Controller) (*middleware.LogRoller, error) {
|
||||||
|
var size, age, keep int
|
||||||
|
// This is kind of a hack to support nested blocks:
|
||||||
|
// As we are already in a block: either log or errors,
|
||||||
|
// c.nesting > 0 but, as soon as c meets a }, it thinks
|
||||||
|
// the block is over and return false for c.NextBlock.
|
||||||
|
for c.NextBlock() {
|
||||||
|
what := c.Val()
|
||||||
|
if !c.NextArg() {
|
||||||
|
return nil, c.ArgErr()
|
||||||
|
}
|
||||||
|
value := c.Val()
|
||||||
|
var err error
|
||||||
|
switch what {
|
||||||
|
case "size":
|
||||||
|
size, err = strconv.Atoi(value)
|
||||||
|
case "age":
|
||||||
|
age, err = strconv.Atoi(value)
|
||||||
|
case "keep":
|
||||||
|
keep, err = strconv.Atoi(value)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &middleware.LogRoller{
|
||||||
|
MaxSize: size,
|
||||||
|
MaxAge: age,
|
||||||
|
MaxBackups: keep,
|
||||||
|
LocalTime: true,
|
||||||
|
}, nil
|
||||||
|
}
|
5
dist/CHANGES.txt
vendored
5
dist/CHANGES.txt
vendored
@ -1,5 +1,10 @@
|
|||||||
CHANGES
|
CHANGES
|
||||||
|
|
||||||
|
<master>
|
||||||
|
- basicauth: Support for legacy htpasswd files
|
||||||
|
- browse: JSON response with file listing given Accept header
|
||||||
|
|
||||||
|
|
||||||
0.7.5 (August 5, 2015)
|
0.7.5 (August 5, 2015)
|
||||||
- core: All listeners bind to 0.0.0.0 unless 'bind' directive is used
|
- core: All listeners bind to 0.0.0.0 unless 'bind' directive is used
|
||||||
- fastcgi: Set HTTPS env variable if connection is secure
|
- fastcgi: Set HTTPS env variable if connection is secure
|
||||||
|
8
main.go
8
main.go
@ -124,7 +124,7 @@ func isLocalhost(s string) bool {
|
|||||||
// loadConfigs loads configuration from a file or stdin (piped).
|
// loadConfigs loads configuration from a file or stdin (piped).
|
||||||
// The configurations are grouped by bind address.
|
// The configurations are grouped by bind address.
|
||||||
// Configuration is obtained from one of three sources, tried
|
// Configuration is obtained from one of three sources, tried
|
||||||
// in this order: 1. -conf flag, 2. stdin, 3. Caddyfile.
|
// in this order: 1. -conf flag, 2. stdin, 3. command line argument 4. Caddyfile.
|
||||||
// If none of those are available, a default configuration is
|
// If none of those are available, a default configuration is
|
||||||
// loaded.
|
// loaded.
|
||||||
func loadConfigs() (config.Group, error) {
|
func loadConfigs() (config.Group, error) {
|
||||||
@ -155,6 +155,12 @@ func loadConfigs() (config.Group, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Command line Arg
|
||||||
|
if flag.NArg() > 0 {
|
||||||
|
confBody := ":" + config.DefaultPort + "\n" + strings.Join(flag.Args(), "\n")
|
||||||
|
return config.Load("args", bytes.NewBufferString(confBody))
|
||||||
|
}
|
||||||
|
|
||||||
// Caddyfile
|
// Caddyfile
|
||||||
file, err := os.Open(config.DefaultConfigFile)
|
file, err := os.Open(config.DefaultConfigFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -2,9 +2,17 @@
|
|||||||
package basicauth
|
package basicauth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/jimstudt/http-authentication/basic"
|
||||||
"github.com/mholt/caddy/middleware"
|
"github.com/mholt/caddy/middleware"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,8 +22,9 @@ import (
|
|||||||
// security of HTTP Basic Auth is disputed. Use discretion when deciding
|
// security of HTTP Basic Auth is disputed. Use discretion when deciding
|
||||||
// what to protect with BasicAuth.
|
// what to protect with BasicAuth.
|
||||||
type BasicAuth struct {
|
type BasicAuth struct {
|
||||||
Next middleware.Handler
|
Next middleware.Handler
|
||||||
Rules []Rule
|
SiteRoot string
|
||||||
|
Rules []Rule
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeHTTP implements the middleware.Handler interface.
|
// ServeHTTP implements the middleware.Handler interface.
|
||||||
@ -37,7 +46,8 @@ func (a BasicAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error
|
|||||||
// Check credentials
|
// Check credentials
|
||||||
if !ok ||
|
if !ok ||
|
||||||
username != rule.Username ||
|
username != rule.Username ||
|
||||||
subtle.ConstantTimeCompare([]byte(password), []byte(rule.Password)) != 1 {
|
!rule.Password(password) {
|
||||||
|
//subtle.ConstantTimeCompare([]byte(password), []byte(rule.Password)) != 1 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,6 +74,71 @@ func (a BasicAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error
|
|||||||
// file or directory paths.
|
// file or directory paths.
|
||||||
type Rule struct {
|
type Rule struct {
|
||||||
Username string
|
Username string
|
||||||
Password string
|
Password func(string) bool
|
||||||
Resources []string
|
Resources []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PasswordMatcher func(pw string) bool
|
||||||
|
|
||||||
|
var (
|
||||||
|
htpasswords map[string]map[string]PasswordMatcher
|
||||||
|
htpasswordsMu sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetHtpasswdMatcher(filename, username, siteRoot string) (PasswordMatcher, error) {
|
||||||
|
filename = filepath.Join(siteRoot, filename)
|
||||||
|
htpasswordsMu.Lock()
|
||||||
|
if htpasswords == nil {
|
||||||
|
htpasswords = make(map[string]map[string]PasswordMatcher)
|
||||||
|
}
|
||||||
|
pm := htpasswords[filename]
|
||||||
|
if pm == nil {
|
||||||
|
fh, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("open %q: %v", filename, err)
|
||||||
|
}
|
||||||
|
defer fh.Close()
|
||||||
|
pm = make(map[string]PasswordMatcher)
|
||||||
|
if err = parseHtpasswd(pm, fh); err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing htpasswd %q: %v", fh.Name(), err)
|
||||||
|
}
|
||||||
|
htpasswords[filename] = pm
|
||||||
|
}
|
||||||
|
htpasswordsMu.Unlock()
|
||||||
|
if pm[username] == nil {
|
||||||
|
return nil, fmt.Errorf("username %q not found in %q", username, filename)
|
||||||
|
}
|
||||||
|
return pm[username], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseHtpasswd(pm map[string]PasswordMatcher, r io.Reader) error {
|
||||||
|
scanner := bufio.NewScanner(r)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := strings.TrimSpace(scanner.Text())
|
||||||
|
if line == "" || strings.IndexByte(line, '#') == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
i := strings.IndexByte(line, ':')
|
||||||
|
if i <= 0 {
|
||||||
|
return fmt.Errorf("malformed line, no color: %q", line)
|
||||||
|
}
|
||||||
|
user, encoded := line[:i], line[i+1:]
|
||||||
|
for _, p := range basic.DefaultSystems {
|
||||||
|
matcher, err := p(encoded)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if matcher != nil {
|
||||||
|
pm[user] = matcher.MatchesPassword
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return scanner.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func PlainMatcher(passw string) PasswordMatcher {
|
||||||
|
return func(pw string) bool {
|
||||||
|
return subtle.ConstantTimeCompare([]byte(pw), []byte(passw)) == 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,8 +3,10 @@ package basicauth
|
|||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/mholt/caddy/middleware"
|
"github.com/mholt/caddy/middleware"
|
||||||
@ -15,7 +17,7 @@ func TestBasicAuth(t *testing.T) {
|
|||||||
rw := BasicAuth{
|
rw := BasicAuth{
|
||||||
Next: middleware.HandlerFunc(contentHandler),
|
Next: middleware.HandlerFunc(contentHandler),
|
||||||
Rules: []Rule{
|
Rules: []Rule{
|
||||||
{Username: "test", Password: "ttest", Resources: []string{"/testing"}},
|
{Username: "test", Password: PlainMatcher("ttest"), Resources: []string{"/testing"}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,8 +68,8 @@ func TestMultipleOverlappingRules(t *testing.T) {
|
|||||||
rw := BasicAuth{
|
rw := BasicAuth{
|
||||||
Next: middleware.HandlerFunc(contentHandler),
|
Next: middleware.HandlerFunc(contentHandler),
|
||||||
Rules: []Rule{
|
Rules: []Rule{
|
||||||
{Username: "t", Password: "p1", Resources: []string{"/t"}},
|
{Username: "t", Password: PlainMatcher("p1"), Resources: []string{"/t"}},
|
||||||
{Username: "t1", Password: "p2", Resources: []string{"/t/t"}},
|
{Username: "t1", Password: PlainMatcher("p2"), Resources: []string{"/t/t"}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,3 +113,31 @@ func contentHandler(w http.ResponseWriter, r *http.Request) (int, error) {
|
|||||||
fmt.Fprintf(w, r.URL.String())
|
fmt.Fprintf(w, r.URL.String())
|
||||||
return http.StatusOK, nil
|
return http.StatusOK, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHtpasswd(t *testing.T) {
|
||||||
|
htpasswdPasswd := "IedFOuGmTpT8"
|
||||||
|
htpasswdFile := `sha1:{SHA}dcAUljwz99qFjYR0YLTXx0RqLww=
|
||||||
|
md5:$apr1$l42y8rex$pOA2VJ0x/0TwaFeAF9nX61`
|
||||||
|
|
||||||
|
htfh, err := ioutil.TempFile("", "basicauth-")
|
||||||
|
if err != nil {
|
||||||
|
t.Skipf("Error creating temp file (%v), will skip htpassword test")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err = htfh.Write([]byte(htpasswdFile)); err != nil {
|
||||||
|
t.Fatalf("write htpasswd file %q: %v", htfh.Name(), err)
|
||||||
|
}
|
||||||
|
htfh.Close()
|
||||||
|
defer os.Remove(htfh.Name())
|
||||||
|
|
||||||
|
for i, username := range []string{"sha1", "md5"} {
|
||||||
|
rule := Rule{Username: username, Resources: []string{"/testing"}}
|
||||||
|
if rule.Password, err = GetHtpasswdMatcher(htfh.Name(), rule.Username, "/"); err != nil {
|
||||||
|
t.Fatalf("GetHtpasswdMatcher(%q, %q): %v", htfh.Name(), rule.Username, err)
|
||||||
|
}
|
||||||
|
t.Logf("%d. username=%q password=%v", i, rule.Username, rule.Password)
|
||||||
|
if !rule.Password(htpasswdPasswd) || rule.Password(htpasswdPasswd+"!") {
|
||||||
|
t.Errorf("%d (%s) password does not match.", i, rule.Username)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -236,7 +236,6 @@ func TestBrowseJson(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unable to Marshal the listing ")
|
t.Fatalf("Unable to Marshal the listing ")
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedJsonString := string(marsh)
|
expectedJsonString := string(marsh)
|
||||||
if actualJsonResponseString != expectedJsonString {
|
if actualJsonResponseString != expectedJsonString {
|
||||||
t.Errorf("Json response string doesnt match the expected Json response ")
|
t.Errorf("Json response string doesnt match the expected Json response ")
|
||||||
|
@ -20,6 +20,7 @@ type ErrorHandler struct {
|
|||||||
ErrorPages map[int]string // map of status code to filename
|
ErrorPages map[int]string // map of status code to filename
|
||||||
LogFile string
|
LogFile string
|
||||||
Log *log.Logger
|
Log *log.Logger
|
||||||
|
LogRoller *middleware.LogRoller
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h ErrorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
func (h ErrorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||||
|
@ -47,6 +47,7 @@ type Rule struct {
|
|||||||
OutputFile string
|
OutputFile string
|
||||||
Format string
|
Format string
|
||||||
Log *log.Logger
|
Log *log.Logger
|
||||||
|
Roller *middleware.LogRoller
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -13,7 +13,7 @@ func TestNewResponseRecorder(t *testing.T) {
|
|||||||
t.Fatalf("Expected Response writer in the Recording to be same as the one sent\n")
|
t.Fatalf("Expected Response writer in the Recording to be same as the one sent\n")
|
||||||
}
|
}
|
||||||
if recordRequest.status != http.StatusOK {
|
if recordRequest.status != http.StatusOK {
|
||||||
t.Fatalf("Expected recorded status to be http.StatusOK (%d) , but found %d\n ", recordRequest.status)
|
t.Fatalf("Expected recorded status to be http.StatusOK (%d) , but found %d\n ", http.StatusOK, recordRequest.status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func TestWriteHeader(t *testing.T) {
|
func TestWriteHeader(t *testing.T) {
|
||||||
@ -35,6 +35,6 @@ func TestWrite(t *testing.T) {
|
|||||||
t.Fatalf("Expected the bytes written counter to be %d, but instead found %d\n", len(buf), recordRequest.size)
|
t.Fatalf("Expected the bytes written counter to be %d, but instead found %d\n", len(buf), recordRequest.size)
|
||||||
}
|
}
|
||||||
if w.Body.String() != responseTestString {
|
if w.Body.String() != responseTestString {
|
||||||
t.Fatalf("Expected Response Body to be %s , but found %s\n", w.Body.String())
|
t.Fatalf("Expected Response Body to be %s , but found %s\n", responseTestString, w.Body.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ func TestNewReplacer(t *testing.T) {
|
|||||||
|
|
||||||
switch v := replaceValues.(type) {
|
switch v := replaceValues.(type) {
|
||||||
case replacer:
|
case replacer:
|
||||||
|
|
||||||
if v.replacements["{host}"] != "caddyserver.com" {
|
if v.replacements["{host}"] != "caddyserver.com" {
|
||||||
t.Errorf("Expected host to be caddyserver.com")
|
t.Errorf("Expected host to be caddyserver.com")
|
||||||
}
|
}
|
||||||
@ -36,3 +37,35 @@ func TestNewReplacer(t *testing.T) {
|
|||||||
t.Fatalf("Return Value from New Replacer expected pass type assertion into a replacer type \n")
|
t.Fatalf("Return Value from New Replacer expected pass type assertion into a replacer type \n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReplace(t *testing.T) {
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
recordRequest := NewResponseRecorder(w)
|
||||||
|
userJson := `{"username": "dennis"}`
|
||||||
|
|
||||||
|
reader := strings.NewReader(userJson) //Convert string to reader
|
||||||
|
|
||||||
|
request, err := http.NewRequest("POST", "http://caddyserver.com", reader) //Create request with JSON body
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Request Formation Failed \n")
|
||||||
|
}
|
||||||
|
replaceValues := NewReplacer(request, recordRequest, "")
|
||||||
|
|
||||||
|
switch v := replaceValues.(type) {
|
||||||
|
case replacer:
|
||||||
|
|
||||||
|
if v.Replace("This host is {host}") != "This host is caddyserver.com" {
|
||||||
|
t.Errorf("Expected host replacement failed")
|
||||||
|
}
|
||||||
|
if v.Replace("This request method is {method}") != "This request method is POST" {
|
||||||
|
t.Errorf("Expected method replacement failed")
|
||||||
|
}
|
||||||
|
if v.Replace("The response status is {status}") != "The response status is 200" {
|
||||||
|
t.Errorf("Expected status replacement failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
t.Fatalf("Return Value from New Replacer expected pass type assertion into a replacer type \n")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
25
middleware/roller.go
Normal file
25
middleware/roller.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"gopkg.in/natefinch/lumberjack.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LogRoller struct {
|
||||||
|
Filename string
|
||||||
|
MaxSize int
|
||||||
|
MaxAge int
|
||||||
|
MaxBackups int
|
||||||
|
LocalTime bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l LogRoller) GetLogWriter() io.Writer {
|
||||||
|
return &lumberjack.Logger{
|
||||||
|
Filename: l.Filename,
|
||||||
|
MaxSize: l.MaxSize,
|
||||||
|
MaxAge: l.MaxAge,
|
||||||
|
MaxBackups: l.MaxBackups,
|
||||||
|
LocalTime: l.LocalTime,
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user