mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-04 03:27:23 -05: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"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -15,6 +23,7 @@ import (
 | 
				
			|||||||
// what to protect with BasicAuth.
 | 
					// what to protect with BasicAuth.
 | 
				
			||||||
type BasicAuth struct {
 | 
					type BasicAuth struct {
 | 
				
			||||||
	Next     middleware.Handler
 | 
						Next     middleware.Handler
 | 
				
			||||||
 | 
						SiteRoot string
 | 
				
			||||||
	Rules    []Rule
 | 
						Rules    []Rule
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -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