diff --git a/app/app.go b/app/app.go index 07c0661f0..c495a43a4 100644 --- a/app/app.go +++ b/app/app.go @@ -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 diff --git a/config/config.go b/config/config.go index 82a29585f..cc7c5b11c 100644 --- a/config/config.go +++ b/config/config.go @@ -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 diff --git a/config/directives.go b/config/directives.go index c7d0d06a2..354b55959 100644 --- a/config/directives.go +++ b/config/directives.go @@ -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) diff --git a/config/parse/dispenser.go b/config/parse/dispenser.go index a7457f561..08aa6e76d 100644 --- a/config/parse/dispenser.go +++ b/config/parse/dispenser.go @@ -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") } diff --git a/config/parse/parsing.go b/config/parse/parsing.go index bd903503d..4fb1f3dfd 100644 --- a/config/parse/parsing.go +++ b/config/parse/parsing.go @@ -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 } diff --git a/config/parse/parsing_test.go b/config/parse/parsing_test.go index 8fa55fd60..197ec0da2 100644 --- a/config/parse/parsing_test.go +++ b/config/parse/parsing_test.go @@ -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": {}, } } diff --git a/config/setup/markdown.go b/config/setup/markdown.go index b67b32bc6..eb24a5955 100644 --- a/config/setup/markdown.go +++ b/config/setup/markdown.go @@ -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") + } +} diff --git a/config/setup/proxy.go b/config/setup/proxy.go index 42aebf9d7..3011cb0e4 100644 --- a/config/setup/proxy.go +++ b/config/setup/proxy.go @@ -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 { - return func(next middleware.Handler) middleware.Handler { - return proxy.Proxy{Next: next, Upstreams: upstreams} - }, nil - } else { + 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 } diff --git a/config/setup/startupshutdown.go b/config/setup/startupshutdown.go index ab7ce03e7..befadd425 100644 --- a/config/setup/startupshutdown.go +++ b/config/setup/startupshutdown.go @@ -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) diff --git a/main.go b/main.go index c1c0d1430..0740346cf 100644 --- a/main.go +++ b/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() diff --git a/middleware/basicauth/basicauth.go b/middleware/basicauth/basicauth.go index eeeb54761..14e7d2105 100644 --- a/middleware/basicauth/basicauth.go +++ b/middleware/basicauth/basicauth.go @@ -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 diff --git a/middleware/browse/browse_test.go b/middleware/browse/browse_test.go index 44ac4d8af..2d653c98e 100644 --- a/middleware/browse/browse_test.go +++ b/middleware/browse/browse_test.go @@ -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) } - } } diff --git a/middleware/errors/errors.go b/middleware/errors/errors.go index e148899a8..e9eef90e7 100644 --- a/middleware/errors/errors.go +++ b/middleware/errors/errors.go @@ -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 { diff --git a/middleware/fastcgi/fastcgi.go b/middleware/fastcgi/fastcgi.go index 5dd262ff6..eef6f4bb8 100755 --- a/middleware/fastcgi/fastcgi.go +++ b/middleware/fastcgi/fastcgi.go @@ -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 diff --git a/middleware/fastcgi/fcgiclient.go b/middleware/fastcgi/fcgiclient.go index 91f8cb877..de46a4e8e 100644 --- a/middleware/fastcgi/fcgiclient.go +++ b/middleware/fastcgi/fcgiclient.go @@ -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) } diff --git a/middleware/fastcgi/fcgiclient_test.go b/middleware/fastcgi/fcgiclient_test.go index f843548e2..1abaf10fc 100644 --- a/middleware/fastcgi/fcgiclient_test.go +++ b/middleware/fastcgi/fcgiclient_test.go @@ -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) diff --git a/middleware/gzip/filter.go b/middleware/gzip/filter.go index ceac660e6..7871112f5 100644 --- a/middleware/gzip/filter.go +++ b/middleware/gzip/filter.go @@ -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 } diff --git a/middleware/gzip/gzip_test.go b/middleware/gzip/gzip_test.go index 8798f4c54..8d49e8e27 100644 --- a/middleware/gzip/gzip_test.go +++ b/middleware/gzip/gzip_test.go @@ -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() diff --git a/middleware/markdown/markdown_test.go b/middleware/markdown/markdown_test.go index 6012d0dfd..8cb89fae5 100644 --- a/middleware/markdown/markdown_test.go +++ b/middleware/markdown/markdown_test.go @@ -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(), diff --git a/middleware/markdown/process.go b/middleware/markdown/process.go index b7f644043..93fe51479 100644 --- a/middleware/markdown/process.go +++ b/middleware/markdown/process.go @@ -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, diff --git a/middleware/markdown/watcher.go b/middleware/markdown/watcher.go index 76ef2ed5f..0b39c7414 100644 --- a/middleware/markdown/watcher.go +++ b/middleware/markdown/watcher.go @@ -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 diff --git a/middleware/proxy/policy_test.go b/middleware/proxy/policy_test.go index d4c752256..b7d633277 100644 --- a/middleware/proxy/policy_test.go +++ b/middleware/proxy/policy_test.go @@ -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", }, } diff --git a/middleware/proxy/upstream.go b/middleware/proxy/upstream.go index 9a2a01048..3ab8aa9b8 100644 --- a/middleware/proxy/upstream.go +++ b/middleware/proxy/upstream.go @@ -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,64 +53,8 @@ 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 { - 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()) + if err := parseBlock(&c, upstream); err != nil { + return upstreams, err } } @@ -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 diff --git a/middleware/replacer_test.go b/middleware/replacer_test.go index 7986a7353..39d91a222 100644 --- a/middleware/replacer_test.go +++ b/middleware/replacer_test.go @@ -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 { diff --git a/middleware/rewrite/rewrite.go b/middleware/rewrite/rewrite.go index 8732d2b94..885bfad2c 100644 --- a/middleware/rewrite/rewrite.go +++ b/middleware/rewrite/rewrite.go @@ -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 diff --git a/middleware/rewrite/rewrite_test.go b/middleware/rewrite/rewrite_test.go index b4845f106..23280aeec 100644 --- a/middleware/rewrite/rewrite_test.go +++ b/middleware/rewrite/rewrite_test.go @@ -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 { diff --git a/middleware/roller.go b/middleware/roller.go index 0f8a2b2e3..995cabf91 100644 --- a/middleware/roller.go +++ b/middleware/roller.go @@ -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, diff --git a/middleware/templates/templates_test.go b/middleware/templates/templates_test.go index 5c283390f..3ee6072ce 100644 --- a/middleware/templates/templates_test.go +++ b/middleware/templates/templates_test.go @@ -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: "/", diff --git a/server/server.go b/server/server.go index 03bca6078..8e93c2941 100644 --- a/server/server.go +++ b/server/server.go @@ -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))