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