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:
Matt Holt 2015-10-11 16:00:58 -06:00
commit 837c17c396
29 changed files with 293 additions and 258 deletions

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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")
} }

View File

@ -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
} }

View File

@ -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": {},
} }
} }

View File

@ -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")
}
}

View File

@ -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
} }

View File

@ -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)

View File

@ -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()

View File

@ -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

View File

@ -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)
} }
} }
} }

View File

@ -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 {

View File

@ -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

View File

@ -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)
} }

View File

@ -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)

View File

@ -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
} }

View File

@ -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()

View File

@ -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(),

View File

@ -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,

View File

@ -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

View File

@ -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",
}, },
} }

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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 {

View File

@ -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,

View File

@ -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: "/",

View File

@ -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))