mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-03 19:17:29 -05:00 
			
		
		
		
	Merge pull request #267 from zmb3/reportcard
Ran gofmt -s, fixed some golint warnings, refactored some large functions Minor quality improvements (closes #253)
This commit is contained in:
		
						commit
						837c17c396
					
				@ -33,8 +33,8 @@ var (
 | 
			
		||||
	// Wg is used to wait for all servers to shut down
 | 
			
		||||
	Wg sync.WaitGroup
 | 
			
		||||
 | 
			
		||||
	// Http2 indicates whether HTTP2 is enabled or not
 | 
			
		||||
	Http2 bool // TODO: temporary flag until http2 is standard
 | 
			
		||||
	// HTTP2 indicates whether HTTP2 is enabled or not
 | 
			
		||||
	HTTP2 bool // TODO: temporary flag until http2 is standard
 | 
			
		||||
 | 
			
		||||
	// Quiet mode hides non-error initialization output
 | 
			
		||||
	Quiet bool
 | 
			
		||||
 | 
			
		||||
@ -234,6 +234,8 @@ func validDirective(d string) bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewDefault creates a default configuration using the default
 | 
			
		||||
// root, host, and port.
 | 
			
		||||
func NewDefault() server.Config {
 | 
			
		||||
	return server.Config{
 | 
			
		||||
		Root: Root,
 | 
			
		||||
@ -256,4 +258,5 @@ var (
 | 
			
		||||
	Port = DefaultPort
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Group maps network addresses to their configurations.
 | 
			
		||||
type Group map[*net.TCPAddr][]server.Config
 | 
			
		||||
 | 
			
		||||
@ -74,7 +74,7 @@ type directive struct {
 | 
			
		||||
	setup SetupFunc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A setup function takes a setup controller. Its return values may
 | 
			
		||||
// both be nil. If middleware is not nil, it will be chained into
 | 
			
		||||
// SetupFunc takes a controller and may optionally return a middleware.
 | 
			
		||||
// If the resulting middleware is not nil, it will be chained into
 | 
			
		||||
// the HTTP handlers in the order specified in this package.
 | 
			
		||||
type SetupFunc func(c *setup.Controller) (middleware.Middleware, error)
 | 
			
		||||
 | 
			
		||||
@ -119,6 +119,7 @@ func (d *Dispenser) NextBlock() bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IncrNest adds a level of nesting to the dispenser.
 | 
			
		||||
func (d *Dispenser) IncrNest() {
 | 
			
		||||
	d.nesting++
 | 
			
		||||
	return
 | 
			
		||||
@ -208,9 +209,9 @@ func (d *Dispenser) SyntaxErr(expected string) error {
 | 
			
		||||
	return errors.New(msg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EofErr returns an EOF error, meaning that end of input
 | 
			
		||||
// was found when another token was expected.
 | 
			
		||||
func (d *Dispenser) EofErr() error {
 | 
			
		||||
// EOFErr returns an error indicating that the dispenser reached
 | 
			
		||||
// the end of the input when searching for the next token.
 | 
			
		||||
func (d *Dispenser) EOFErr() error {
 | 
			
		||||
	return d.Errf("Unexpected EOF")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -108,7 +108,7 @@ func (p *parser) addresses() error {
 | 
			
		||||
		// Advance token and possibly break out of loop or return error
 | 
			
		||||
		hasNext := p.Next()
 | 
			
		||||
		if expectingAnother && !hasNext {
 | 
			
		||||
			return p.EofErr()
 | 
			
		||||
			return p.EOFErr()
 | 
			
		||||
		}
 | 
			
		||||
		if !hasNext {
 | 
			
		||||
			p.eof = true
 | 
			
		||||
@ -242,7 +242,7 @@ func (p *parser) directive() error {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if nesting > 0 {
 | 
			
		||||
		return p.EofErr()
 | 
			
		||||
		return p.EOFErr()
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -338,9 +338,9 @@ func TestParseAll(t *testing.T) {
 | 
			
		||||
func setupParseTests() {
 | 
			
		||||
	// Set up some bogus directives for testing
 | 
			
		||||
	ValidDirectives = map[string]struct{}{
 | 
			
		||||
		"dir1": struct{}{},
 | 
			
		||||
		"dir2": struct{}{},
 | 
			
		||||
		"dir3": struct{}{},
 | 
			
		||||
		"dir1": {},
 | 
			
		||||
		"dir2": {},
 | 
			
		||||
		"dir3": {},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -68,62 +68,8 @@ func markdownParse(c *Controller) ([]*markdown.Config, error) {
 | 
			
		||||
 | 
			
		||||
		// Load any other configuration parameters
 | 
			
		||||
		for c.NextBlock() {
 | 
			
		||||
			switch c.Val() {
 | 
			
		||||
			case "ext":
 | 
			
		||||
				exts := c.RemainingArgs()
 | 
			
		||||
				if len(exts) == 0 {
 | 
			
		||||
					return mdconfigs, c.ArgErr()
 | 
			
		||||
				}
 | 
			
		||||
				md.Extensions = append(md.Extensions, exts...)
 | 
			
		||||
			case "css":
 | 
			
		||||
				if !c.NextArg() {
 | 
			
		||||
					return mdconfigs, c.ArgErr()
 | 
			
		||||
				}
 | 
			
		||||
				md.Styles = append(md.Styles, c.Val())
 | 
			
		||||
			case "js":
 | 
			
		||||
				if !c.NextArg() {
 | 
			
		||||
					return mdconfigs, c.ArgErr()
 | 
			
		||||
				}
 | 
			
		||||
				md.Scripts = append(md.Scripts, c.Val())
 | 
			
		||||
			case "template":
 | 
			
		||||
				tArgs := c.RemainingArgs()
 | 
			
		||||
				switch len(tArgs) {
 | 
			
		||||
				case 0:
 | 
			
		||||
					return mdconfigs, c.ArgErr()
 | 
			
		||||
				case 1:
 | 
			
		||||
					if _, ok := md.Templates[markdown.DefaultTemplate]; ok {
 | 
			
		||||
						return mdconfigs, c.Err("only one default template is allowed, use alias.")
 | 
			
		||||
					}
 | 
			
		||||
					fpath := filepath.Clean(c.Root + string(filepath.Separator) + tArgs[0])
 | 
			
		||||
					md.Templates[markdown.DefaultTemplate] = fpath
 | 
			
		||||
				case 2:
 | 
			
		||||
					fpath := filepath.Clean(c.Root + string(filepath.Separator) + tArgs[1])
 | 
			
		||||
					md.Templates[tArgs[0]] = fpath
 | 
			
		||||
				default:
 | 
			
		||||
					return mdconfigs, c.ArgErr()
 | 
			
		||||
				}
 | 
			
		||||
			case "sitegen":
 | 
			
		||||
				if c.NextArg() {
 | 
			
		||||
					md.StaticDir = path.Join(c.Root, c.Val())
 | 
			
		||||
				} else {
 | 
			
		||||
					md.StaticDir = path.Join(c.Root, markdown.DefaultStaticDir)
 | 
			
		||||
				}
 | 
			
		||||
				if c.NextArg() {
 | 
			
		||||
					// only 1 argument allowed
 | 
			
		||||
					return mdconfigs, c.ArgErr()
 | 
			
		||||
				}
 | 
			
		||||
			case "dev":
 | 
			
		||||
				if c.NextArg() {
 | 
			
		||||
					md.Development = strings.ToLower(c.Val()) == "true"
 | 
			
		||||
				} else {
 | 
			
		||||
					md.Development = true
 | 
			
		||||
				}
 | 
			
		||||
				if c.NextArg() {
 | 
			
		||||
					// only 1 argument allowed
 | 
			
		||||
					return mdconfigs, c.ArgErr()
 | 
			
		||||
				}
 | 
			
		||||
			default:
 | 
			
		||||
				return mdconfigs, c.Err("Expected valid markdown configuration property")
 | 
			
		||||
			if err := loadParams(c, md); err != nil {
 | 
			
		||||
				return mdconfigs, err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -137,3 +83,70 @@ func markdownParse(c *Controller) ([]*markdown.Config, error) {
 | 
			
		||||
 | 
			
		||||
	return mdconfigs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func loadParams(c *Controller, mdc *markdown.Config) error {
 | 
			
		||||
	switch c.Val() {
 | 
			
		||||
	case "ext":
 | 
			
		||||
		exts := c.RemainingArgs()
 | 
			
		||||
		if len(exts) == 0 {
 | 
			
		||||
			return c.ArgErr()
 | 
			
		||||
		}
 | 
			
		||||
		mdc.Extensions = append(mdc.Extensions, exts...)
 | 
			
		||||
		return nil
 | 
			
		||||
	case "css":
 | 
			
		||||
		if !c.NextArg() {
 | 
			
		||||
			return c.ArgErr()
 | 
			
		||||
		}
 | 
			
		||||
		mdc.Styles = append(mdc.Styles, c.Val())
 | 
			
		||||
		return nil
 | 
			
		||||
	case "js":
 | 
			
		||||
		if !c.NextArg() {
 | 
			
		||||
			return c.ArgErr()
 | 
			
		||||
		}
 | 
			
		||||
		mdc.Scripts = append(mdc.Scripts, c.Val())
 | 
			
		||||
		return nil
 | 
			
		||||
	case "template":
 | 
			
		||||
		tArgs := c.RemainingArgs()
 | 
			
		||||
		switch len(tArgs) {
 | 
			
		||||
		case 0:
 | 
			
		||||
			return c.ArgErr()
 | 
			
		||||
		case 1:
 | 
			
		||||
			if _, ok := mdc.Templates[markdown.DefaultTemplate]; ok {
 | 
			
		||||
				return c.Err("only one default template is allowed, use alias.")
 | 
			
		||||
			}
 | 
			
		||||
			fpath := filepath.Clean(c.Root + string(filepath.Separator) + tArgs[0])
 | 
			
		||||
			mdc.Templates[markdown.DefaultTemplate] = fpath
 | 
			
		||||
			return nil
 | 
			
		||||
		case 2:
 | 
			
		||||
			fpath := filepath.Clean(c.Root + string(filepath.Separator) + tArgs[1])
 | 
			
		||||
			mdc.Templates[tArgs[0]] = fpath
 | 
			
		||||
			return nil
 | 
			
		||||
		default:
 | 
			
		||||
			return c.ArgErr()
 | 
			
		||||
		}
 | 
			
		||||
	case "sitegen":
 | 
			
		||||
		if c.NextArg() {
 | 
			
		||||
			mdc.StaticDir = path.Join(c.Root, c.Val())
 | 
			
		||||
		} else {
 | 
			
		||||
			mdc.StaticDir = path.Join(c.Root, markdown.DefaultStaticDir)
 | 
			
		||||
		}
 | 
			
		||||
		if c.NextArg() {
 | 
			
		||||
			// only 1 argument allowed
 | 
			
		||||
			return c.ArgErr()
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	case "dev":
 | 
			
		||||
		if c.NextArg() {
 | 
			
		||||
			mdc.Development = strings.ToLower(c.Val()) == "true"
 | 
			
		||||
		} else {
 | 
			
		||||
			mdc.Development = true
 | 
			
		||||
		}
 | 
			
		||||
		if c.NextArg() {
 | 
			
		||||
			// only 1 argument allowed
 | 
			
		||||
			return c.ArgErr()
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	default:
 | 
			
		||||
		return c.Err("Expected valid markdown configuration property")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -7,11 +7,11 @@ import (
 | 
			
		||||
 | 
			
		||||
// Proxy configures a new Proxy middleware instance.
 | 
			
		||||
func Proxy(c *Controller) (middleware.Middleware, error) {
 | 
			
		||||
	if upstreams, err := proxy.NewStaticUpstreams(c.Dispenser); err == nil {
 | 
			
		||||
	upstreams, err := proxy.NewStaticUpstreams(c.Dispenser)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return func(next middleware.Handler) middleware.Handler {
 | 
			
		||||
		return proxy.Proxy{Next: next, Upstreams: upstreams}
 | 
			
		||||
	}, nil
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -46,9 +46,8 @@ func registerCallback(c *Controller, list *[]func() error) error {
 | 
			
		||||
 | 
			
		||||
			if nonblock {
 | 
			
		||||
				return cmd.Start()
 | 
			
		||||
			} else {
 | 
			
		||||
				return cmd.Run()
 | 
			
		||||
			}
 | 
			
		||||
			return cmd.Run()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		*list = append(*list, fn)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										4
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								main.go
									
									
									
									
									
								
							@ -26,7 +26,7 @@ var (
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	flag.StringVar(&conf, "conf", "", "Configuration file to use (default="+config.DefaultConfigFile+")")
 | 
			
		||||
	flag.BoolVar(&app.Http2, "http2", true, "Enable HTTP/2 support") // TODO: temporary flag until http2 merged into std lib
 | 
			
		||||
	flag.BoolVar(&app.HTTP2, "http2", true, "Enable HTTP/2 support") // TODO: temporary flag until http2 merged into std lib
 | 
			
		||||
	flag.BoolVar(&app.Quiet, "quiet", false, "Quiet mode (no initialization output)")
 | 
			
		||||
	flag.StringVar(&cpu, "cpu", "100%", "CPU cap")
 | 
			
		||||
	flag.StringVar(&config.Root, "root", config.DefaultRoot, "Root path to default site")
 | 
			
		||||
@ -61,7 +61,7 @@ func main() {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
		s.HTTP2 = app.Http2 // TODO: This setting is temporary
 | 
			
		||||
		s.HTTP2 = app.HTTP2 // TODO: This setting is temporary
 | 
			
		||||
		app.Wg.Add(1)
 | 
			
		||||
		go func(s *server.Server) {
 | 
			
		||||
			defer app.Wg.Done()
 | 
			
		||||
 | 
			
		||||
@ -78,6 +78,7 @@ type Rule struct {
 | 
			
		||||
	Resources []string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PasswordMatcher determines whether a password mathes a rule.
 | 
			
		||||
type PasswordMatcher func(pw string) bool
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
@ -137,6 +138,8 @@ func parseHtpasswd(pm map[string]PasswordMatcher, r io.Reader) error {
 | 
			
		||||
	return scanner.Err()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PlainMatcher returns a PasswordMatcher that does a constant-time
 | 
			
		||||
// byte-wise comparison.
 | 
			
		||||
func PlainMatcher(passw string) PasswordMatcher {
 | 
			
		||||
	return func(pw string) bool {
 | 
			
		||||
		return subtle.ConstantTimeCompare([]byte(pw), []byte(passw)) == 1
 | 
			
		||||
 | 
			
		||||
@ -117,7 +117,7 @@ func TestBrowseTemplate(t *testing.T) {
 | 
			
		||||
		}),
 | 
			
		||||
		Root: "./testdata",
 | 
			
		||||
		Configs: []Config{
 | 
			
		||||
			Config{
 | 
			
		||||
			{
 | 
			
		||||
				PathScope: "/photos",
 | 
			
		||||
				Template:  tmpl,
 | 
			
		||||
			},
 | 
			
		||||
@ -172,7 +172,7 @@ func TestBrowseJson(t *testing.T) {
 | 
			
		||||
		}),
 | 
			
		||||
		Root: "./testdata",
 | 
			
		||||
		Configs: []Config{
 | 
			
		||||
			Config{
 | 
			
		||||
			{
 | 
			
		||||
				PathScope: "/photos/",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
@ -283,7 +283,7 @@ func TestBrowseJson(t *testing.T) {
 | 
			
		||||
			t.Fatalf("Expected Content type to be application/json; charset=utf-8, but got %s ", rec.HeaderMap.Get("Content-Type"))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		actualJsonResponseString := rec.Body.String()
 | 
			
		||||
		actualJSONResponse := rec.Body.String()
 | 
			
		||||
		copyOflisting := listing
 | 
			
		||||
		if test.SortBy == "" {
 | 
			
		||||
			copyOflisting.Sort = "name"
 | 
			
		||||
@ -308,12 +308,11 @@ func TestBrowseJson(t *testing.T) {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatalf("Unable to Marshal the listing ")
 | 
			
		||||
		}
 | 
			
		||||
		expectedJsonString := string(marsh)
 | 
			
		||||
		expectedJSON := string(marsh)
 | 
			
		||||
 | 
			
		||||
		if actualJsonResponseString != expectedJsonString {
 | 
			
		||||
		if actualJSONResponse != expectedJSON {
 | 
			
		||||
			t.Errorf("JSON response doesn't match the expected for test number %d with sort=%s, order=%s\nExpected response %s\nActual response = %s\n",
 | 
			
		||||
				i+1, test.SortBy, test.OrderBy, expectedJsonString, actualJsonResponseString)
 | 
			
		||||
				i+1, test.SortBy, test.OrderBy, expectedJSON, actualJSONResponse)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -37,9 +37,8 @@ func (h ErrorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, er
 | 
			
		||||
			w.WriteHeader(status)
 | 
			
		||||
			fmt.Fprintln(w, errMsg)
 | 
			
		||||
			return 0, err // returning < 400 signals that a response has been written
 | 
			
		||||
		} else {
 | 
			
		||||
			h.Log.Println(errMsg)
 | 
			
		||||
		}
 | 
			
		||||
		h.Log.Println(errMsg)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if status >= 400 {
 | 
			
		||||
 | 
			
		||||
@ -58,17 +58,7 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Connect to FastCGI gateway
 | 
			
		||||
			var fcgi *FCGIClient
 | 
			
		||||
 | 
			
		||||
			// check if unix socket or tcp
 | 
			
		||||
			if strings.HasPrefix(rule.Address, "/") || strings.HasPrefix(rule.Address, "unix:") {
 | 
			
		||||
				if strings.HasPrefix(rule.Address, "unix:") {
 | 
			
		||||
					rule.Address = rule.Address[len("unix:"):]
 | 
			
		||||
				}
 | 
			
		||||
				fcgi, err = Dial("unix", rule.Address)
 | 
			
		||||
			} else {
 | 
			
		||||
				fcgi, err = Dial("tcp", rule.Address)
 | 
			
		||||
			}
 | 
			
		||||
			fcgi, err := getClient(&rule)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return http.StatusBadGateway, err
 | 
			
		||||
			}
 | 
			
		||||
@ -102,13 +92,7 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error)
 | 
			
		||||
				return http.StatusBadGateway, err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Write the response header
 | 
			
		||||
			for key, vals := range resp.Header {
 | 
			
		||||
				for _, val := range vals {
 | 
			
		||||
					w.Header().Add(key, val)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			w.WriteHeader(resp.StatusCode)
 | 
			
		||||
			writeHeader(w, resp)
 | 
			
		||||
 | 
			
		||||
			// Write the response body
 | 
			
		||||
			// TODO: If this has an error, the response will already be
 | 
			
		||||
@ -126,6 +110,26 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error)
 | 
			
		||||
	return h.Next.ServeHTTP(w, r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getClient(r *Rule) (*FCGIClient, error) {
 | 
			
		||||
	// check if unix socket or TCP
 | 
			
		||||
	if trim := strings.HasPrefix(r.Address, "unix"); strings.HasPrefix(r.Address, "/") || trim {
 | 
			
		||||
		if trim {
 | 
			
		||||
			r.Address = r.Address[len("unix:"):]
 | 
			
		||||
		}
 | 
			
		||||
		return Dial("unix", r.Address)
 | 
			
		||||
	}
 | 
			
		||||
	return Dial("tcp", r.Address)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func writeHeader(w http.ResponseWriter, r *http.Response) {
 | 
			
		||||
	for key, vals := range r.Header {
 | 
			
		||||
		for _, val := range vals {
 | 
			
		||||
			w.Header().Add(key, val)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	w.WriteHeader(r.StatusCode)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h Handler) exists(path string) bool {
 | 
			
		||||
	if _, err := os.Stat(h.Root + path); err == nil {
 | 
			
		||||
		return true
 | 
			
		||||
 | 
			
		||||
@ -30,45 +30,45 @@ import (
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const FCGI_LISTENSOCK_FILENO uint8 = 0
 | 
			
		||||
const FCGI_HEADER_LEN uint8 = 8
 | 
			
		||||
const VERSION_1 uint8 = 1
 | 
			
		||||
const FCGI_NULL_REQUEST_ID uint8 = 0
 | 
			
		||||
const FCGI_KEEP_CONN uint8 = 1
 | 
			
		||||
const FCGIListenSockFileno uint8 = 0
 | 
			
		||||
const FCGIHeaderLen uint8 = 8
 | 
			
		||||
const Version1 uint8 = 1
 | 
			
		||||
const FCGINullRequestID uint8 = 0
 | 
			
		||||
const FCGIKeepConn uint8 = 1
 | 
			
		||||
const doubleCRLF = "\r\n\r\n"
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	FCGI_BEGIN_REQUEST uint8 = iota + 1
 | 
			
		||||
	FCGI_ABORT_REQUEST
 | 
			
		||||
	FCGI_END_REQUEST
 | 
			
		||||
	FCGI_PARAMS
 | 
			
		||||
	FCGI_STDIN
 | 
			
		||||
	FCGI_STDOUT
 | 
			
		||||
	FCGI_STDERR
 | 
			
		||||
	FCGI_DATA
 | 
			
		||||
	FCGI_GET_VALUES
 | 
			
		||||
	FCGI_GET_VALUES_RESULT
 | 
			
		||||
	FCGI_UNKNOWN_TYPE
 | 
			
		||||
	FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
 | 
			
		||||
	BeginRequest uint8 = iota + 1
 | 
			
		||||
	AbortRequest
 | 
			
		||||
	EndRequest
 | 
			
		||||
	Params
 | 
			
		||||
	Stdin
 | 
			
		||||
	Stdout
 | 
			
		||||
	Stderr
 | 
			
		||||
	Data
 | 
			
		||||
	GetValues
 | 
			
		||||
	GetValuesResult
 | 
			
		||||
	UnknownType
 | 
			
		||||
	MaxType = UnknownType
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	FCGI_RESPONDER uint8 = iota + 1
 | 
			
		||||
	FCGI_AUTHORIZER
 | 
			
		||||
	FCGI_FILTER
 | 
			
		||||
	Responder uint8 = iota + 1
 | 
			
		||||
	Authorizer
 | 
			
		||||
	Filter
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	FCGI_REQUEST_COMPLETE uint8 = iota
 | 
			
		||||
	FCGI_CANT_MPX_CONN
 | 
			
		||||
	FCGI_OVERLOADED
 | 
			
		||||
	FCGI_UNKNOWN_ROLE
 | 
			
		||||
	RequestComplete uint8 = iota
 | 
			
		||||
	CantMultiplexConns
 | 
			
		||||
	Overloaded
 | 
			
		||||
	UnknownRole
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	FCGI_MAX_CONNS  string = "MAX_CONNS"
 | 
			
		||||
	FCGI_MAX_REQS   string = "MAX_REQS"
 | 
			
		||||
	FCGI_MPXS_CONNS string = "MPXS_CONNS"
 | 
			
		||||
	MaxConns       string = "MAX_CONNS"
 | 
			
		||||
	MaxRequests    string = "MAX_REQS"
 | 
			
		||||
	MultiplexConns string = "MPXS_CONNS"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
@ -79,7 +79,7 @@ const (
 | 
			
		||||
type header struct {
 | 
			
		||||
	Version       uint8
 | 
			
		||||
	Type          uint8
 | 
			
		||||
	Id            uint16
 | 
			
		||||
	ID            uint16
 | 
			
		||||
	ContentLength uint16
 | 
			
		||||
	PaddingLength uint8
 | 
			
		||||
	Reserved      uint8
 | 
			
		||||
@ -92,7 +92,7 @@ var pad [maxPad]byte
 | 
			
		||||
func (h *header) init(recType uint8, reqID uint16, contentLength int) {
 | 
			
		||||
	h.Version = 1
 | 
			
		||||
	h.Type = recType
 | 
			
		||||
	h.Id = reqID
 | 
			
		||||
	h.ID = reqID
 | 
			
		||||
	h.ContentLength = uint16(contentLength)
 | 
			
		||||
	h.PaddingLength = uint8(-contentLength & 7)
 | 
			
		||||
}
 | 
			
		||||
@ -110,7 +110,7 @@ func (rec *record) read(r io.Reader) (buf []byte, err error) {
 | 
			
		||||
		err = errors.New("fcgi: invalid header version")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if rec.h.Type == FCGI_END_REQUEST {
 | 
			
		||||
	if rec.h.Type == EndRequest {
 | 
			
		||||
		err = io.EOF
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@ -126,13 +126,15 @@ func (rec *record) read(r io.Reader) (buf []byte, err error) {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FCGIClient implements a FastCGI client, which is a standard for
 | 
			
		||||
// interfacing external applications with Web servers.
 | 
			
		||||
type FCGIClient struct {
 | 
			
		||||
	mutex     sync.Mutex
 | 
			
		||||
	rwc       io.ReadWriteCloser
 | 
			
		||||
	h         header
 | 
			
		||||
	buf       bytes.Buffer
 | 
			
		||||
	keepAlive bool
 | 
			
		||||
	reqId     uint16
 | 
			
		||||
	reqID     uint16
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Dial connects to the fcgi responder at the specified network address.
 | 
			
		||||
@ -148,7 +150,7 @@ func Dial(network, address string) (fcgi *FCGIClient, err error) {
 | 
			
		||||
	fcgi = &FCGIClient{
 | 
			
		||||
		rwc:       conn,
 | 
			
		||||
		keepAlive: false,
 | 
			
		||||
		reqId:     1,
 | 
			
		||||
		reqID:     1,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
@ -163,7 +165,7 @@ func (c *FCGIClient) writeRecord(recType uint8, content []byte) (err error) {
 | 
			
		||||
	c.mutex.Lock()
 | 
			
		||||
	defer c.mutex.Unlock()
 | 
			
		||||
	c.buf.Reset()
 | 
			
		||||
	c.h.init(recType, c.reqId, len(content))
 | 
			
		||||
	c.h.init(recType, c.reqID, len(content))
 | 
			
		||||
	if err := binary.Write(&c.buf, binary.BigEndian, c.h); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
@ -179,14 +181,14 @@ func (c *FCGIClient) writeRecord(recType uint8, content []byte) (err error) {
 | 
			
		||||
 | 
			
		||||
func (c *FCGIClient) writeBeginRequest(role uint16, flags uint8) error {
 | 
			
		||||
	b := [8]byte{byte(role >> 8), byte(role), flags}
 | 
			
		||||
	return c.writeRecord(FCGI_BEGIN_REQUEST, b[:])
 | 
			
		||||
	return c.writeRecord(BeginRequest, b[:])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *FCGIClient) writeEndRequest(appStatus int, protocolStatus uint8) error {
 | 
			
		||||
	b := make([]byte, 8)
 | 
			
		||||
	binary.BigEndian.PutUint32(b, uint32(appStatus))
 | 
			
		||||
	b[4] = protocolStatus
 | 
			
		||||
	return c.writeRecord(FCGI_END_REQUEST, b)
 | 
			
		||||
	return c.writeRecord(EndRequest, b)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *FCGIClient) writePairs(recType uint8, pairs map[string]string) error {
 | 
			
		||||
@ -334,17 +336,17 @@ func (w *streamReader) Read(p []byte) (n int, err error) {
 | 
			
		||||
// Do made the request and returns a io.Reader that translates the data read
 | 
			
		||||
// from fcgi responder out of fcgi packet before returning it.
 | 
			
		||||
func (c *FCGIClient) Do(p map[string]string, req io.Reader) (r io.Reader, err error) {
 | 
			
		||||
	err = c.writeBeginRequest(uint16(FCGI_RESPONDER), 0)
 | 
			
		||||
	err = c.writeBeginRequest(uint16(Responder), 0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = c.writePairs(FCGI_PARAMS, p)
 | 
			
		||||
	err = c.writePairs(Params, p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	body := newWriter(c, FCGI_STDIN)
 | 
			
		||||
	body := newWriter(c, Stdin)
 | 
			
		||||
	if req != nil {
 | 
			
		||||
		io.Copy(body, req)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -34,13 +34,13 @@ import (
 | 
			
		||||
// test failed if the remote fcgi(script) failed md5 verification
 | 
			
		||||
// and output "FAILED" in response
 | 
			
		||||
const (
 | 
			
		||||
	script_file = "/tank/www/fcgic_test.php"
 | 
			
		||||
	//ip_port = "remote-php-serv:59000"
 | 
			
		||||
	ip_port = "127.0.0.1:59000"
 | 
			
		||||
	scriptFile = "/tank/www/fcgic_test.php"
 | 
			
		||||
	//ipPort = "remote-php-serv:59000"
 | 
			
		||||
	ipPort = "127.0.0.1:59000"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	t_ *testing.T = nil
 | 
			
		||||
	t_ *testing.T
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type FastCGIServer struct{}
 | 
			
		||||
@ -51,7 +51,7 @@ func (s FastCGIServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
 | 
			
		||||
 | 
			
		||||
	stat := "PASSED"
 | 
			
		||||
	fmt.Fprintln(resp, "-")
 | 
			
		||||
	file_num := 0
 | 
			
		||||
	fileNum := 0
 | 
			
		||||
	{
 | 
			
		||||
		length := 0
 | 
			
		||||
		for k0, v0 := range req.Form {
 | 
			
		||||
@ -69,7 +69,7 @@ func (s FastCGIServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if req.MultipartForm != nil {
 | 
			
		||||
			file_num = len(req.MultipartForm.File)
 | 
			
		||||
			fileNum = len(req.MultipartForm.File)
 | 
			
		||||
			for kn, fns := range req.MultipartForm.File {
 | 
			
		||||
				//fmt.Fprintln(resp, "server:filekey ", kn )
 | 
			
		||||
				length += len(kn)
 | 
			
		||||
@ -101,11 +101,11 @@ func (s FastCGIServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
 | 
			
		||||
 | 
			
		||||
		fmt.Fprintln(resp, "server:got data length", length)
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Fprintln(resp, "-"+stat+"-POST(", len(req.Form), ")-FILE(", file_num, ")--")
 | 
			
		||||
	fmt.Fprintln(resp, "-"+stat+"-POST(", len(req.Form), ")-FILE(", fileNum, ")--")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sendFcgi(reqType int, fcgi_params map[string]string, data []byte, posts map[string]string, files map[string]string) (content []byte) {
 | 
			
		||||
	fcgi, err := Dial("tcp", ip_port)
 | 
			
		||||
func sendFcgi(reqType int, fcgiParams map[string]string, data []byte, posts map[string]string, files map[string]string) (content []byte) {
 | 
			
		||||
	fcgi, err := Dial("tcp", ipPort)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Println("err:", err)
 | 
			
		||||
		return
 | 
			
		||||
@ -119,16 +119,16 @@ func sendFcgi(reqType int, fcgi_params map[string]string, data []byte, posts map
 | 
			
		||||
		if len(data) > 0 {
 | 
			
		||||
			length = len(data)
 | 
			
		||||
			rd := bytes.NewReader(data)
 | 
			
		||||
			resp, err = fcgi.Post(fcgi_params, "", rd, rd.Len())
 | 
			
		||||
			resp, err = fcgi.Post(fcgiParams, "", rd, rd.Len())
 | 
			
		||||
		} else if len(posts) > 0 {
 | 
			
		||||
			values := url.Values{}
 | 
			
		||||
			for k, v := range posts {
 | 
			
		||||
				values.Set(k, v)
 | 
			
		||||
				length += len(k) + 2 + len(v)
 | 
			
		||||
			}
 | 
			
		||||
			resp, err = fcgi.PostForm(fcgi_params, values)
 | 
			
		||||
			resp, err = fcgi.PostForm(fcgiParams, values)
 | 
			
		||||
		} else {
 | 
			
		||||
			resp, err = fcgi.Get(fcgi_params)
 | 
			
		||||
			resp, err = fcgi.Get(fcgiParams)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
@ -142,7 +142,7 @@ func sendFcgi(reqType int, fcgi_params map[string]string, data []byte, posts map
 | 
			
		||||
			fi, _ := os.Lstat(v)
 | 
			
		||||
			length += len(k) + int(fi.Size())
 | 
			
		||||
		}
 | 
			
		||||
		resp, err = fcgi.PostFile(fcgi_params, values, files)
 | 
			
		||||
		resp, err = fcgi.PostFile(fcgiParams, values, files)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@ -191,7 +191,7 @@ func generateRandFile(size int) (p string, m string) {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Disabled_Test(t *testing.T) {
 | 
			
		||||
func DisabledTest(t *testing.T) {
 | 
			
		||||
	// TODO: test chunked reader
 | 
			
		||||
 | 
			
		||||
	t_ = t
 | 
			
		||||
@ -199,7 +199,7 @@ func Disabled_Test(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
	// server
 | 
			
		||||
	go func() {
 | 
			
		||||
		listener, err := net.Listen("tcp", ip_port)
 | 
			
		||||
		listener, err := net.Listen("tcp", ipPort)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			// handle error
 | 
			
		||||
			log.Println("listener creatation failed: ", err)
 | 
			
		||||
@ -212,19 +212,19 @@ func Disabled_Test(t *testing.T) {
 | 
			
		||||
	time.Sleep(1 * time.Second)
 | 
			
		||||
 | 
			
		||||
	// init
 | 
			
		||||
	fcgi_params := make(map[string]string)
 | 
			
		||||
	fcgi_params["REQUEST_METHOD"] = "GET"
 | 
			
		||||
	fcgi_params["SERVER_PROTOCOL"] = "HTTP/1.1"
 | 
			
		||||
	fcgiParams := make(map[string]string)
 | 
			
		||||
	fcgiParams["REQUEST_METHOD"] = "GET"
 | 
			
		||||
	fcgiParams["SERVER_PROTOCOL"] = "HTTP/1.1"
 | 
			
		||||
	//fcgi_params["GATEWAY_INTERFACE"] = "CGI/1.1"
 | 
			
		||||
	fcgi_params["SCRIPT_FILENAME"] = script_file
 | 
			
		||||
	fcgiParams["SCRIPT_FILENAME"] = scriptFile
 | 
			
		||||
 | 
			
		||||
	// simple GET
 | 
			
		||||
	log.Println("test:", "get")
 | 
			
		||||
	sendFcgi(0, fcgi_params, nil, nil, nil)
 | 
			
		||||
	sendFcgi(0, fcgiParams, nil, nil, nil)
 | 
			
		||||
 | 
			
		||||
	// simple post data
 | 
			
		||||
	log.Println("test:", "post")
 | 
			
		||||
	sendFcgi(0, fcgi_params, []byte("c4ca4238a0b923820dcc509a6f75849b=1&7b8b965ad4bca0e41ab51de7b31363a1=n"), nil, nil)
 | 
			
		||||
	sendFcgi(0, fcgiParams, []byte("c4ca4238a0b923820dcc509a6f75849b=1&7b8b965ad4bca0e41ab51de7b31363a1=n"), nil, nil)
 | 
			
		||||
 | 
			
		||||
	log.Println("test:", "post data (more than 60KB)")
 | 
			
		||||
	data := ""
 | 
			
		||||
@ -240,13 +240,13 @@ func Disabled_Test(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
		data += k0 + "=" + url.QueryEscape(v0) + "&"
 | 
			
		||||
	}
 | 
			
		||||
	sendFcgi(0, fcgi_params, []byte(data), nil, nil)
 | 
			
		||||
	sendFcgi(0, fcgiParams, []byte(data), nil, nil)
 | 
			
		||||
 | 
			
		||||
	log.Println("test:", "post form (use url.Values)")
 | 
			
		||||
	p0 := make(map[string]string, 1)
 | 
			
		||||
	p0["c4ca4238a0b923820dcc509a6f75849b"] = "1"
 | 
			
		||||
	p0["7b8b965ad4bca0e41ab51de7b31363a1"] = "n"
 | 
			
		||||
	sendFcgi(1, fcgi_params, nil, p0, nil)
 | 
			
		||||
	sendFcgi(1, fcgiParams, nil, p0, nil)
 | 
			
		||||
 | 
			
		||||
	log.Println("test:", "post forms (256 keys, more than 1MB)")
 | 
			
		||||
	p1 := make(map[string]string, 1)
 | 
			
		||||
@ -257,25 +257,25 @@ func Disabled_Test(t *testing.T) {
 | 
			
		||||
		k0 := fmt.Sprintf("%x", h.Sum(nil))
 | 
			
		||||
		p1[k0] = v0
 | 
			
		||||
	}
 | 
			
		||||
	sendFcgi(1, fcgi_params, nil, p1, nil)
 | 
			
		||||
	sendFcgi(1, fcgiParams, nil, p1, nil)
 | 
			
		||||
 | 
			
		||||
	log.Println("test:", "post file (1 file, 500KB)) ")
 | 
			
		||||
	f0 := make(map[string]string, 1)
 | 
			
		||||
	path0, m0 := generateRandFile(500000)
 | 
			
		||||
	f0[m0] = path0
 | 
			
		||||
	sendFcgi(1, fcgi_params, nil, p1, f0)
 | 
			
		||||
	sendFcgi(1, fcgiParams, nil, p1, f0)
 | 
			
		||||
 | 
			
		||||
	log.Println("test:", "post multiple files (2 files, 5M each) and forms (256 keys, more than 1MB data")
 | 
			
		||||
	path1, m1 := generateRandFile(5000000)
 | 
			
		||||
	f0[m1] = path1
 | 
			
		||||
	sendFcgi(1, fcgi_params, nil, p1, f0)
 | 
			
		||||
	sendFcgi(1, fcgiParams, nil, p1, f0)
 | 
			
		||||
 | 
			
		||||
	log.Println("test:", "post only files (2 files, 5M each)")
 | 
			
		||||
	sendFcgi(1, fcgi_params, nil, nil, f0)
 | 
			
		||||
	sendFcgi(1, fcgiParams, nil, nil, f0)
 | 
			
		||||
 | 
			
		||||
	log.Println("test:", "post only 1 file")
 | 
			
		||||
	delete(f0, "m0")
 | 
			
		||||
	sendFcgi(1, fcgi_params, nil, nil, f0)
 | 
			
		||||
	sendFcgi(1, fcgiParams, nil, nil, f0)
 | 
			
		||||
 | 
			
		||||
	os.Remove(path0)
 | 
			
		||||
	os.Remove(path1)
 | 
			
		||||
 | 
			
		||||
@ -81,7 +81,7 @@ func (s Set) Contains(value string) bool {
 | 
			
		||||
// elements in the set and passes each to f. It returns true
 | 
			
		||||
// on the first call to f that returns true and false otherwise.
 | 
			
		||||
func (s Set) ContainsFunc(f func(string) bool) bool {
 | 
			
		||||
	for k, _ := range s {
 | 
			
		||||
	for k := range s {
 | 
			
		||||
		if f(k) {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@ func TestGzipHandler(t *testing.T) {
 | 
			
		||||
		extFilter.Exts.Add(e)
 | 
			
		||||
	}
 | 
			
		||||
	gz := Gzip{Configs: []Config{
 | 
			
		||||
		Config{Filters: []Filter{pathFilter, extFilter}},
 | 
			
		||||
		{Filters: []Filter{pathFilter, extFilter}},
 | 
			
		||||
	}}
 | 
			
		||||
 | 
			
		||||
	w := httptest.NewRecorder()
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@ func TestMarkdown(t *testing.T) {
 | 
			
		||||
		Root:    "./testdata",
 | 
			
		||||
		FileSys: http.Dir("./testdata"),
 | 
			
		||||
		Configs: []*Config{
 | 
			
		||||
			&Config{
 | 
			
		||||
			{
 | 
			
		||||
				Renderer:    blackfriday.HtmlRenderer(0, "", ""),
 | 
			
		||||
				PathScope:   "/blog",
 | 
			
		||||
				Extensions:  []string{".md"},
 | 
			
		||||
@ -32,7 +32,7 @@ func TestMarkdown(t *testing.T) {
 | 
			
		||||
				StaticDir:   DefaultStaticDir,
 | 
			
		||||
				StaticFiles: make(map[string]string),
 | 
			
		||||
			},
 | 
			
		||||
			&Config{
 | 
			
		||||
			{
 | 
			
		||||
				Renderer:    blackfriday.HtmlRenderer(0, "", ""),
 | 
			
		||||
				PathScope:   "/log",
 | 
			
		||||
				Extensions:  []string{".md"},
 | 
			
		||||
@ -42,7 +42,7 @@ func TestMarkdown(t *testing.T) {
 | 
			
		||||
				StaticDir:   DefaultStaticDir,
 | 
			
		||||
				StaticFiles: make(map[string]string),
 | 
			
		||||
			},
 | 
			
		||||
			&Config{
 | 
			
		||||
			{
 | 
			
		||||
				Renderer:    blackfriday.HtmlRenderer(0, "", ""),
 | 
			
		||||
				PathScope:   "/og",
 | 
			
		||||
				Extensions:  []string{".md"},
 | 
			
		||||
@ -52,7 +52,7 @@ func TestMarkdown(t *testing.T) {
 | 
			
		||||
				StaticDir:   "testdata/og_static",
 | 
			
		||||
				StaticFiles: map[string]string{"/og/first.md": "testdata/og_static/og/first.md/index.html"},
 | 
			
		||||
				Links: []PageLink{
 | 
			
		||||
					PageLink{
 | 
			
		||||
					{
 | 
			
		||||
						Title:   "first",
 | 
			
		||||
						Summary: "",
 | 
			
		||||
						Date:    time.Now(),
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,7 @@ const (
 | 
			
		||||
	DefaultStaticDir = "generated_site"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type MarkdownData struct {
 | 
			
		||||
type Data struct {
 | 
			
		||||
	middleware.Context
 | 
			
		||||
	Doc   map[string]string
 | 
			
		||||
	Links []PageLink
 | 
			
		||||
@ -95,7 +95,7 @@ func (md Markdown) processTemplate(c *Config, requestPath string, tmpl []byte, m
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	mdData := MarkdownData{
 | 
			
		||||
	mdData := Data{
 | 
			
		||||
		Context: ctx,
 | 
			
		||||
		Doc:     metadata.Variables,
 | 
			
		||||
		Links:   c.Links,
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,8 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DefaultInterval is the default interval at which the markdown watcher
 | 
			
		||||
// checks for changes.
 | 
			
		||||
const DefaultInterval = time.Second * 60
 | 
			
		||||
 | 
			
		||||
// Watch monitors the configured markdown directory for changes. It calls GenerateLinks
 | 
			
		||||
 | 
			
		||||
@ -12,13 +12,13 @@ func (r *customPolicy) Select(pool HostPool) *UpstreamHost {
 | 
			
		||||
 | 
			
		||||
func testPool() HostPool {
 | 
			
		||||
	pool := []*UpstreamHost{
 | 
			
		||||
		&UpstreamHost{
 | 
			
		||||
		{
 | 
			
		||||
			Name: "http://google.com", // this should resolve (healthcheck test)
 | 
			
		||||
		},
 | 
			
		||||
		&UpstreamHost{
 | 
			
		||||
		{
 | 
			
		||||
			Name: "http://shouldnot.resolve", // this shouldn't
 | 
			
		||||
		},
 | 
			
		||||
		&UpstreamHost{
 | 
			
		||||
		{
 | 
			
		||||
			Name: "http://C",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -13,8 +13,8 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	supportedPolicies map[string]func() Policy = make(map[string]func() Policy)
 | 
			
		||||
	proxyHeaders      http.Header              = make(http.Header)
 | 
			
		||||
	supportedPolicies = make(map[string]func() Policy)
 | 
			
		||||
	proxyHeaders      = make(http.Header)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type staticUpstream struct {
 | 
			
		||||
@ -53,65 +53,9 @@ func NewStaticUpstreams(c parse.Dispenser) ([]Upstream, error) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for c.NextBlock() {
 | 
			
		||||
			switch c.Val() {
 | 
			
		||||
			case "policy":
 | 
			
		||||
				if !c.NextArg() {
 | 
			
		||||
					return upstreams, c.ArgErr()
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if policyCreateFunc, ok := supportedPolicies[c.Val()]; ok {
 | 
			
		||||
					upstream.Policy = policyCreateFunc()
 | 
			
		||||
				} else {
 | 
			
		||||
					return upstreams, c.ArgErr()
 | 
			
		||||
				}
 | 
			
		||||
			case "fail_timeout":
 | 
			
		||||
				if !c.NextArg() {
 | 
			
		||||
					return upstreams, c.ArgErr()
 | 
			
		||||
				}
 | 
			
		||||
				if dur, err := time.ParseDuration(c.Val()); err == nil {
 | 
			
		||||
					upstream.FailTimeout = dur
 | 
			
		||||
				} else {
 | 
			
		||||
			if err := parseBlock(&c, upstream); err != nil {
 | 
			
		||||
				return upstreams, err
 | 
			
		||||
			}
 | 
			
		||||
			case "max_fails":
 | 
			
		||||
				if !c.NextArg() {
 | 
			
		||||
					return upstreams, c.ArgErr()
 | 
			
		||||
				}
 | 
			
		||||
				if n, err := strconv.Atoi(c.Val()); err == nil {
 | 
			
		||||
					upstream.MaxFails = int32(n)
 | 
			
		||||
				} else {
 | 
			
		||||
					return upstreams, err
 | 
			
		||||
				}
 | 
			
		||||
			case "health_check":
 | 
			
		||||
				if !c.NextArg() {
 | 
			
		||||
					return upstreams, c.ArgErr()
 | 
			
		||||
				}
 | 
			
		||||
				upstream.HealthCheck.Path = c.Val()
 | 
			
		||||
				upstream.HealthCheck.Interval = 30 * time.Second
 | 
			
		||||
				if c.NextArg() {
 | 
			
		||||
					if dur, err := time.ParseDuration(c.Val()); err == nil {
 | 
			
		||||
						upstream.HealthCheck.Interval = dur
 | 
			
		||||
					} else {
 | 
			
		||||
						return upstreams, err
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			case "proxy_header":
 | 
			
		||||
				var header, value string
 | 
			
		||||
				if !c.Args(&header, &value) {
 | 
			
		||||
					return upstreams, c.ArgErr()
 | 
			
		||||
				}
 | 
			
		||||
				proxyHeaders.Add(header, value)
 | 
			
		||||
			case "websocket":
 | 
			
		||||
				proxyHeaders.Add("Connection", "{>Connection}")
 | 
			
		||||
				proxyHeaders.Add("Upgrade", "{>Upgrade}")
 | 
			
		||||
			case "without":
 | 
			
		||||
				if !c.NextArg() {
 | 
			
		||||
					return upstreams, c.ArgErr()
 | 
			
		||||
				}
 | 
			
		||||
				upstream.WithoutPathPrefix = c.Val()
 | 
			
		||||
			default:
 | 
			
		||||
				return upstreams, c.Errf("unknown property '%s'", c.Val())
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		upstream.Hosts = make([]*UpstreamHost, len(to))
 | 
			
		||||
@ -165,6 +109,68 @@ func (u *staticUpstream) From() string {
 | 
			
		||||
	return u.from
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseBlock(c *parse.Dispenser, u *staticUpstream) error {
 | 
			
		||||
	switch c.Val() {
 | 
			
		||||
	case "policy":
 | 
			
		||||
		if !c.NextArg() {
 | 
			
		||||
			return c.ArgErr()
 | 
			
		||||
		}
 | 
			
		||||
		policyCreateFunc, ok := supportedPolicies[c.Val()]
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return c.ArgErr()
 | 
			
		||||
		}
 | 
			
		||||
		u.Policy = policyCreateFunc()
 | 
			
		||||
	case "fail_timeout":
 | 
			
		||||
		if !c.NextArg() {
 | 
			
		||||
			return c.ArgErr()
 | 
			
		||||
		}
 | 
			
		||||
		dur, err := time.ParseDuration(c.Val())
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		u.FailTimeout = dur
 | 
			
		||||
	case "max_fails":
 | 
			
		||||
		if !c.NextArg() {
 | 
			
		||||
			return c.ArgErr()
 | 
			
		||||
		}
 | 
			
		||||
		n, err := strconv.Atoi(c.Val())
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		u.MaxFails = int32(n)
 | 
			
		||||
	case "health_check":
 | 
			
		||||
		if !c.NextArg() {
 | 
			
		||||
			return c.ArgErr()
 | 
			
		||||
		}
 | 
			
		||||
		u.HealthCheck.Path = c.Val()
 | 
			
		||||
		u.HealthCheck.Interval = 30 * time.Second
 | 
			
		||||
		if c.NextArg() {
 | 
			
		||||
			dur, err := time.ParseDuration(c.Val())
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			u.HealthCheck.Interval = dur
 | 
			
		||||
		}
 | 
			
		||||
	case "proxy_header":
 | 
			
		||||
		var header, value string
 | 
			
		||||
		if !c.Args(&header, &value) {
 | 
			
		||||
			return c.ArgErr()
 | 
			
		||||
		}
 | 
			
		||||
		proxyHeaders.Add(header, value)
 | 
			
		||||
	case "websocket":
 | 
			
		||||
		proxyHeaders.Add("Connection", "{>Connection}")
 | 
			
		||||
		proxyHeaders.Add("Upgrade", "{>Upgrade}")
 | 
			
		||||
	case "without":
 | 
			
		||||
		if !c.NextArg() {
 | 
			
		||||
			return c.ArgErr()
 | 
			
		||||
		}
 | 
			
		||||
		u.WithoutPathPrefix = c.Val()
 | 
			
		||||
	default:
 | 
			
		||||
		return c.Errf("unknown property '%s'", c.Val())
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *staticUpstream) healthCheck() {
 | 
			
		||||
	for _, host := range u.Hosts {
 | 
			
		||||
		hostURL := host.Name + u.HealthCheck.Path
 | 
			
		||||
 | 
			
		||||
@ -10,9 +10,9 @@ import (
 | 
			
		||||
func TestNewReplacer(t *testing.T) {
 | 
			
		||||
	w := httptest.NewRecorder()
 | 
			
		||||
	recordRequest := NewResponseRecorder(w)
 | 
			
		||||
	userJson := `{"username": "dennis"}`
 | 
			
		||||
	userJSON := `{"username": "dennis"}`
 | 
			
		||||
 | 
			
		||||
	reader := strings.NewReader(userJson) //Convert string to reader
 | 
			
		||||
	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 {
 | 
			
		||||
@ -41,9 +41,9 @@ func TestNewReplacer(t *testing.T) {
 | 
			
		||||
func TestReplace(t *testing.T) {
 | 
			
		||||
	w := httptest.NewRecorder()
 | 
			
		||||
	recordRequest := NewResponseRecorder(w)
 | 
			
		||||
	userJson := `{"username": "dennis"}`
 | 
			
		||||
	userJSON := `{"username": "dennis"}`
 | 
			
		||||
 | 
			
		||||
	reader := strings.NewReader(userJson) //Convert string to reader
 | 
			
		||||
	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 {
 | 
			
		||||
 | 
			
		||||
@ -115,7 +115,7 @@ func (r *RegexpRule) Rewrite(req *http.Request) bool {
 | 
			
		||||
	// include trailing slash in regexp if present
 | 
			
		||||
	start := len(r.Base)
 | 
			
		||||
	if strings.HasSuffix(r.Base, "/") {
 | 
			
		||||
		start -= 1
 | 
			
		||||
		start--
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// validate regexp
 | 
			
		||||
 | 
			
		||||
@ -21,15 +21,15 @@ func TestRewrite(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	regexpRules := [][]string{
 | 
			
		||||
		[]string{"/reg/", ".*", "/to", ""},
 | 
			
		||||
		[]string{"/r/", "[a-z]+", "/toaz", "!.html|"},
 | 
			
		||||
		[]string{"/url/", "a([a-z0-9]*)s([A-Z]{2})", "/to/{path}", ""},
 | 
			
		||||
		[]string{"/ab/", "ab", "/ab?{query}", ".txt|"},
 | 
			
		||||
		[]string{"/ab/", "ab", "/ab?type=html&{query}", ".html|"},
 | 
			
		||||
		[]string{"/abc/", "ab", "/abc/{file}", ".html|"},
 | 
			
		||||
		[]string{"/abcd/", "ab", "/a/{dir}/{file}", ".html|"},
 | 
			
		||||
		[]string{"/abcde/", "ab", "/a#{fragment}", ".html|"},
 | 
			
		||||
		[]string{"/ab/", `.*\.jpg`, "/ajpg", ""},
 | 
			
		||||
		{"/reg/", ".*", "/to", ""},
 | 
			
		||||
		{"/r/", "[a-z]+", "/toaz", "!.html|"},
 | 
			
		||||
		{"/url/", "a([a-z0-9]*)s([A-Z]{2})", "/to/{path}", ""},
 | 
			
		||||
		{"/ab/", "ab", "/ab?{query}", ".txt|"},
 | 
			
		||||
		{"/ab/", "ab", "/ab?type=html&{query}", ".html|"},
 | 
			
		||||
		{"/abc/", "ab", "/abc/{file}", ".html|"},
 | 
			
		||||
		{"/abcd/", "ab", "/a/{dir}/{file}", ".html|"},
 | 
			
		||||
		{"/abcde/", "ab", "/a#{fragment}", ".html|"},
 | 
			
		||||
		{"/ab/", `.*\.jpg`, "/ajpg", ""},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, regexpRule := range regexpRules {
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,7 @@ import (
 | 
			
		||||
	"gopkg.in/natefinch/lumberjack.v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// LogRoller implements a middleware that provides a rolling logger.
 | 
			
		||||
type LogRoller struct {
 | 
			
		||||
	Filename   string
 | 
			
		||||
	MaxSize    int
 | 
			
		||||
@ -14,6 +15,7 @@ type LogRoller struct {
 | 
			
		||||
	LocalTime  bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLogWriter returns an io.Writer that writes to a rolling logger.
 | 
			
		||||
func (l LogRoller) GetLogWriter() io.Writer {
 | 
			
		||||
	return &lumberjack.Logger{
 | 
			
		||||
		Filename:   l.Filename,
 | 
			
		||||
 | 
			
		||||
@ -14,12 +14,12 @@ func Test(t *testing.T) {
 | 
			
		||||
			return 0, nil
 | 
			
		||||
		}),
 | 
			
		||||
		Rules: []Rule{
 | 
			
		||||
			Rule{
 | 
			
		||||
			{
 | 
			
		||||
				Extensions: []string{".html"},
 | 
			
		||||
				IndexFiles: []string{"index.html"},
 | 
			
		||||
				Path:       "/photos",
 | 
			
		||||
			},
 | 
			
		||||
			Rule{
 | 
			
		||||
			{
 | 
			
		||||
				Extensions: []string{".html", ".htm"},
 | 
			
		||||
				IndexFiles: []string{"index.html", "index.htm"},
 | 
			
		||||
				Path:       "/images",
 | 
			
		||||
@ -34,7 +34,7 @@ func Test(t *testing.T) {
 | 
			
		||||
			return 0, nil
 | 
			
		||||
		}),
 | 
			
		||||
		Rules: []Rule{
 | 
			
		||||
			Rule{
 | 
			
		||||
			{
 | 
			
		||||
				Extensions: []string{".html"},
 | 
			
		||||
				IndexFiles: []string{"index.html"},
 | 
			
		||||
				Path:       "/",
 | 
			
		||||
 | 
			
		||||
@ -264,6 +264,8 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DefaultErrorFunc responds to an HTTP request with a simple description
 | 
			
		||||
// of the specified HTTP status code.
 | 
			
		||||
func DefaultErrorFunc(w http.ResponseWriter, r *http.Request, status int) {
 | 
			
		||||
	w.WriteHeader(status)
 | 
			
		||||
	fmt.Fprintf(w, "%d %s", status, http.StatusText(status))
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user