diff --git a/.gitignore b/.gitignore index 9aab95cfd73..b718b098cce 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ data/sessions data/*.db data/log /bin/* +/grafana-pro diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index a3b20b4b404..03c8737f424 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -4,7 +4,7 @@ "Deps": [ { "ImportPath": "github.com/Unknwon/com", - "Rev": "6c41aba9600e474c1a9e0521402c996b52944c4b" + "Rev": "d9bcf409c8a368d06c9b347705c381e7c12d54df" }, { "ImportPath": "github.com/Unknwon/goconfig", @@ -12,7 +12,7 @@ }, { "ImportPath": "github.com/Unknwon/macaron", - "Rev": "634a5d99660f7104f290a54e95ada2f8e34e46b2" + "Rev": "0f55900b417c019233ec27f86950bfb9feda8379" }, { "ImportPath": "github.com/codegangsta/cli", diff --git a/Godeps/_workspace/src/github.com/Unknwon/com/dir.go b/Godeps/_workspace/src/github.com/Unknwon/com/dir.go index 7946e77ebd3..c126d79da86 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/com/dir.go +++ b/Godeps/_workspace/src/github.com/Unknwon/com/dir.go @@ -16,6 +16,7 @@ package com import ( "errors" + "fmt" "os" "path" "strings" @@ -95,6 +96,36 @@ func GetAllSubDirs(rootPath string) ([]string, error) { return statDir(rootPath, "", true, true) } +// GetFileListBySuffix returns an ordered list of file paths. +// It recognize if given path is a file, and don't do recursive find. +func GetFileListBySuffix(dirPath, suffix string) ([]string, error) { + if !IsExist(dirPath) { + return nil, fmt.Errorf("given path does not exist: %s", dirPath) + } else if IsFile(dirPath) { + return []string{dirPath}, nil + } + + // Given path is a directory. + dir, err := os.Open(dirPath) + if err != nil { + return nil, err + } + + fis, err := dir.Readdir(0) + if err != nil { + return nil, err + } + + files := make([]string, 0, len(fis)) + for _, fi := range fis { + if strings.HasSuffix(fi.Name(), suffix) { + files = append(files, path.Join(dirPath, fi.Name())) + } + } + + return files, nil +} + // CopyDir copy files recursively from source to target directory. // // The filter accepts a function that process the path info. diff --git a/Godeps/_workspace/src/github.com/Unknwon/com/http.go b/Godeps/_workspace/src/github.com/Unknwon/com/http.go index 6af72949774..3415059ae90 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/com/http.go +++ b/Godeps/_workspace/src/github.com/Unknwon/com/http.go @@ -15,6 +15,7 @@ package com import ( + "bytes" "encoding/json" "fmt" "io" @@ -43,10 +44,9 @@ func (e *RemoteError) Error() string { var UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1541.0 Safari/537.36" -// HttpGet gets the specified resource. ErrNotFound is returned if the -// server responds with status 404. -func HttpGet(client *http.Client, url string, header http.Header) (io.ReadCloser, error) { - req, err := http.NewRequest("GET", url, nil) +// HttpCall makes HTTP method call. +func HttpCall(client *http.Client, method, url string, header http.Header, body io.Reader) (io.ReadCloser, error) { + req, err := http.NewRequest(method, url, body) if err != nil { return nil, err } @@ -65,11 +65,23 @@ func HttpGet(client *http.Client, url string, header http.Header) (io.ReadCloser if resp.StatusCode == 404 { // 403 can be rate limit error. || resp.StatusCode == 403 { err = fmt.Errorf("resource not found: %s", url) } else { - err = fmt.Errorf("get %s -> %d", url, resp.StatusCode) + err = fmt.Errorf("%s %s -> %d", method, url, resp.StatusCode) } return nil, err } +// HttpGet gets the specified resource. +// ErrNotFound is returned if the server responds with status 404. +func HttpGet(client *http.Client, url string, header http.Header) (io.ReadCloser, error) { + return HttpCall(client, "GET", url, header, nil) +} + +// HttpPost posts the specified resource. +// ErrNotFound is returned if the server responds with status 404. +func HttpPost(client *http.Client, url string, header http.Header, body []byte) (io.ReadCloser, error) { + return HttpCall(client, "POST", url, header, bytes.NewBuffer(body)) +} + // HttpGetToFile gets the specified resource and writes to file. // ErrNotFound is returned if the server responds with status 404. func HttpGetToFile(client *http.Client, url string, header http.Header, fileName string) error { @@ -115,6 +127,26 @@ func HttpGetJSON(client *http.Client, url string, v interface{}) error { return nil } +// HttpPostJSON posts the specified resource with struct values, +// and maps results to struct. +// ErrNotFound is returned if the server responds with status 404. +func HttpPostJSON(client *http.Client, url string, body, v interface{}) error { + data, err := json.Marshal(body) + if err != nil { + return err + } + rc, err := HttpPost(client, url, http.Header{"content-type": []string{"application/json"}}, data) + if err != nil { + return err + } + defer rc.Close() + err = json.NewDecoder(rc).Decode(v) + if _, ok := err.(*json.SyntaxError); ok { + return fmt.Errorf("JSON syntax error at %s", url) + } + return nil +} + // A RawFile describes a file that can be downloaded. type RawFile interface { Name() string diff --git a/Godeps/_workspace/src/github.com/Unknwon/com/string.go b/Godeps/_workspace/src/github.com/Unknwon/com/string.go index ce841f782f4..1ebb9014040 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/com/string.go +++ b/Godeps/_workspace/src/github.com/Unknwon/com/string.go @@ -15,47 +15,53 @@ package com import ( + "crypto/aes" + "crypto/cipher" "crypto/rand" - "crypto/sha1" - "crypto/sha256" - "fmt" - "hash" + "encoding/base64" + "errors" "io" r "math/rand" "strconv" "strings" "time" - "unicode" ) -func sha(m hash.Hash, str string) string { - io.WriteString(m, str) - return fmt.Sprintf("%x", m.Sum(nil)) +// AESEncrypt encrypts text and given key with AES. +func AESEncrypt(key, text []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + b := base64.StdEncoding.EncodeToString(text) + ciphertext := make([]byte, aes.BlockSize+len(b)) + iv := ciphertext[:aes.BlockSize] + if _, err := io.ReadFull(rand.Reader, iv); err != nil { + return nil, err + } + cfb := cipher.NewCFBEncrypter(block, iv) + cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(b)) + return ciphertext, nil } -// sha1 hash string -func Sha1(str string) string { - return sha(sha1.New(), str) -} - -// sha256 hash string -func Sha256(str string) string { - return sha(sha256.New(), str) -} - -// trim space on left -func Ltrim(str string) string { - return strings.TrimLeftFunc(str, unicode.IsSpace) -} - -// trim space on right -func Rtrim(str string) string { - return strings.TrimRightFunc(str, unicode.IsSpace) -} - -// replace find all occurs to string -func StrReplace(str string, find string, to string) string { - return strings.Replace(str, find, to, -1) +// AESDecrypt decrypts text and given key with AES. +func AESDecrypt(key, text []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + if len(text) < aes.BlockSize { + return nil, errors.New("ciphertext too short") + } + iv := text[:aes.BlockSize] + text = text[aes.BlockSize:] + cfb := cipher.NewCFBDecrypter(block, iv) + cfb.XORKeyStream(text, text) + data, err := base64.StdEncoding.DecodeString(string(text)) + if err != nil { + return nil, err + } + return data, nil } // IsLetter returns true if the 'l' is an English letter. diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/README.md b/Godeps/_workspace/src/github.com/Unknwon/macaron/README.md index 75857b71fe4..7f06d736bf6 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/README.md +++ b/Godeps/_workspace/src/github.com/Unknwon/macaron/README.md @@ -5,7 +5,7 @@ Macaron [![Build Status](https://drone.io/github.com/Unknwon/macaron/status.png) Package macaron is a high productive and modular design web framework in Go. -##### Current version: 0.4.5 +##### Current version: 0.4.9 ## Getting Started @@ -36,6 +36,7 @@ func main() { - Unlimited nested group routers. - Directly integrate with existing services. - Dynamically change template files at runtime. +- Allow to use in-memory template and static files. - Easy to plugin/unplugin features with modular design. - Handy dependency injection powered by [inject](https://github.com/codegangsta/inject). - Better router layer and less reflection make faster speed. @@ -46,6 +47,9 @@ Middlewares allow you easily plugin/unplugin features for your Macaron applicati There are already many [middlewares](https://github.com/macaron-contrib) to simplify your work: +- gzip - Gzip compression to all requests +- render - Go template engine +- static - Serves static files - [binding](https://github.com/macaron-contrib/binding) - Request data binding and validation - [i18n](https://github.com/macaron-contrib/i18n) - Internationalization and Localization - [cache](https://github.com/macaron-contrib/cache) - Cache manager @@ -54,8 +58,12 @@ There are already many [middlewares](https://github.com/macaron-contrib) to simp - [captcha](https://github.com/macaron-contrib/captcha) - Captcha service - [pongo2](https://github.com/macaron-contrib/pongo2) - Pongo2 template engine support - [sockets](https://github.com/macaron-contrib/sockets) - WebSockets channels binding +- [bindata](https://github.com/macaron-contrib/bindata) - Embed binary data as static and template files - [toolbox](https://github.com/macaron-contrib/toolbox) - Health check, pprof, profile and statistic services +- [oauth2](https://github.com/macaron-contrib/oauth2) - OAuth 2.0 backend - [switcher](https://github.com/macaron-contrib/switcher) - Multiple-site support +- [method](https://github.com/macaron-contrib/method) - HTTP method override +- [permissions2](https://github.com/xyproto/permissions2) - Cookies, users and permissions - [renders](https://github.com/macaron-contrib/renders) - Beego-like render engine(Macaron has built-in template engine, this is another option) ## Use Cases diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/context.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/context.go index 202f482b64c..460fc0ffd2d 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/context.go +++ b/Godeps/_workspace/src/github.com/Unknwon/macaron/context.go @@ -15,19 +15,17 @@ package macaron import ( - "crypto/hmac" - "crypto/sha1" - "encoding/base64" - "fmt" + "crypto/md5" + "encoding/hex" "html/template" "io" "io/ioutil" "mime/multipart" "net/http" + "net/url" "path" "path/filepath" "reflect" - "strconv" "strings" "time" @@ -42,25 +40,28 @@ type Locale interface { Tr(string, ...interface{}) string } -// Body is the request's body. +// RequestBody represents a request body. type RequestBody struct { reader io.ReadCloser } +// Bytes reads and returns content of request body in bytes. func (rb *RequestBody) Bytes() ([]byte, error) { return ioutil.ReadAll(rb.reader) } +// String reads and returns content of request body in string. func (rb *RequestBody) String() (string, error) { data, err := rb.Bytes() return string(data), err } +// ReadCloser returns a ReadCloser for request body. func (rb *RequestBody) ReadCloser() io.ReadCloser { return rb.reader } -// A Request represents an HTTP request received by a server or to be sent by a client. +// Request represents an HTTP request received by a server or to be sent by a client. type Request struct { *http.Request } @@ -239,7 +240,7 @@ func (ctx *Context) GetFile(name string) (multipart.File, *multipart.FileHeader, func (ctx *Context) SetCookie(name string, value string, others ...interface{}) { cookie := http.Cookie{} cookie.Name = name - cookie.Value = value + cookie.Value = url.QueryEscape(value) if len(others) > 0 { switch v := others[0].(type) { @@ -296,7 +297,8 @@ func (ctx *Context) GetCookie(name string) string { if err != nil { return "" } - return cookie.Value + val, _ := url.QueryUnescape(cookie.Value) + return val } // GetCookieInt returns cookie result in int type. @@ -327,41 +329,32 @@ func (ctx *Context) GetSecureCookie(key string) (string, bool) { } // SetSuperSecureCookie sets given cookie value to response header with secret string. -func (ctx *Context) SetSuperSecureCookie(Secret, name, value string, others ...interface{}) { - vs := base64.URLEncoding.EncodeToString([]byte(value)) - timestamp := strconv.FormatInt(time.Now().UnixNano(), 10) - h := hmac.New(sha1.New, []byte(Secret)) - fmt.Fprintf(h, "%s%s", vs, timestamp) - sig := fmt.Sprintf("%02x", h.Sum(nil)) - cookie := strings.Join([]string{vs, timestamp, sig}, "|") - ctx.SetCookie(name, cookie, others...) +func (ctx *Context) SetSuperSecureCookie(secret, name, value string, others ...interface{}) { + m := md5.Sum([]byte(secret)) + secret = hex.EncodeToString(m[:]) + text, err := com.AESEncrypt([]byte(secret), []byte(value)) + if err != nil { + panic("error encrypting cookie: " + err.Error()) + } + ctx.SetCookie(name, hex.EncodeToString(text), others...) } // GetSuperSecureCookie returns given cookie value from request header with secret string. -func (ctx *Context) GetSuperSecureCookie(Secret, key string) (string, bool) { +func (ctx *Context) GetSuperSecureCookie(secret, key string) (string, bool) { val := ctx.GetCookie(key) if val == "" { return "", false } - parts := strings.SplitN(val, "|", 3) - - if len(parts) != 3 { + data, err := hex.DecodeString(val) + if err != nil { return "", false } - vs := parts[0] - timestamp := parts[1] - sig := parts[2] - - h := hmac.New(sha1.New, []byte(Secret)) - fmt.Fprintf(h, "%s%s", vs, timestamp) - - if fmt.Sprintf("%02x", h.Sum(nil)) != sig { - return "", false - } - res, _ := base64.URLEncoding.DecodeString(vs) - return string(res), true + m := md5.Sum([]byte(secret)) + secret = hex.EncodeToString(m[:]) + text, err := com.AESDecrypt([]byte(secret), data) + return string(text), err == nil } // ServeContent serves given content to response. diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/context_test.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/context_test.go index 28cb3904e3b..c10444ad3f7 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/context_test.go +++ b/Godeps/_workspace/src/github.com/Unknwon/macaron/context_test.go @@ -191,7 +191,8 @@ func Test_Context(t *testing.T) { req, err := http.NewRequest("GET", "/set", nil) So(err, ShouldBeNil) m.ServeHTTP(resp, req) - So(strings.HasPrefix(resp.Header().Get("Set-Cookie"), "user=VW5rbndvbg==|"), ShouldBeTrue) + + cookie := resp.Header().Get("Set-Cookie") m.Get("/get", func(ctx *Context) string { name, ok := ctx.GetSecureCookie("user") @@ -202,7 +203,7 @@ func Test_Context(t *testing.T) { resp = httptest.NewRecorder() req, err = http.NewRequest("GET", "/get", nil) So(err, ShouldBeNil) - req.Header.Set("Cookie", "user=VW5rbndvbg==|1409244667158399419|6097781707f68d9940ba1ef0e78cc84aaeebc48f; Path=/; Max-Age=1") + req.Header.Set("Cookie", cookie) m.ServeHTTP(resp, req) So(resp.Body.String(), ShouldEqual, "Unknwon") }) diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/docs/zh-CN/README.md b/Godeps/_workspace/src/github.com/Unknwon/macaron/docs/zh-CN/README.md deleted file mode 100644 index 75ce79bff9e..00000000000 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/docs/zh-CN/README.md +++ /dev/null @@ -1,19 +0,0 @@ -## 多站点支持 - -如果您想要运行 2 或 2 个以上的实例在一个程序里,[HostSwitcher](https://gowalker.org/github.com/Unknwon/macaron#HostSwitcher) 就是您需要的特性: - -```go -func main() { - m1 := macaron.Classic() - // Register m1 middlewares and routers. - - m2 := macaron.Classic() - // Register m2 middlewares and routers. - - hs := macaron.NewHostSwitcher() - // Set instance corresponding to host address. - hs.Set("gowalker.org", m1) - hs.Set("gogs.io", m2) - hs.Run() -} -``` diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/logger.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/logger.go index 01874fabfa7..2f8601e19d4 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/logger.go +++ b/Godeps/_workspace/src/github.com/Unknwon/macaron/logger.go @@ -31,21 +31,25 @@ func init() { // Logger returns a middleware handler that logs the request as it goes in and the response as it goes out. func Logger() Handler { - return func(res http.ResponseWriter, req *http.Request, c *Context, log *log.Logger) { + return func(ctx *Context, log *log.Logger) { start := time.Now() - log.Printf("Started %s %s for %s", req.Method, req.URL.Path, c.RemoteAddr()) + log.Printf("Started %s %s for %s", ctx.Req.Method,ctx.Req.RequestURI, ctx.RemoteAddr()) - rw := res.(ResponseWriter) - c.Next() + rw := ctx.Resp.(ResponseWriter) + ctx.Next() - content := fmt.Sprintf("Completed %s %v %s in %v", req.URL.Path, rw.Status(), http.StatusText(rw.Status()), time.Since(start)) + content := fmt.Sprintf("Completed %s %v %s in %v", ctx.Req.RequestURI, rw.Status(), http.StatusText(rw.Status()), time.Since(start)) if !isWindows { switch rw.Status() { - case 200: + case 200, 201, 202: content = fmt.Sprintf("\033[1;32m%s\033[0m", content) + case 301, 302: + content = fmt.Sprintf("\033[1;37m%s\033[0m", content) case 304: content = fmt.Sprintf("\033[1;33m%s\033[0m", content) + case 401, 403: + content = fmt.Sprintf("\033[4;31m%s\033[0m", content) case 404: content = fmt.Sprintf("\033[1;31m%s\033[0m", content) case 500: diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/logger_test.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/logger_test.go index 22611910eb6..e4951dc28cc 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/logger_test.go +++ b/Godeps/_workspace/src/github.com/Unknwon/macaron/logger_test.go @@ -22,6 +22,8 @@ import ( "net/http/httptest" "testing" + "github.com/Unknwon/com" + . "github.com/smartystreets/goconvey/convey" ) @@ -43,4 +45,23 @@ func Test_Logger(t *testing.T) { So(resp.Code, ShouldEqual, http.StatusNotFound) So(len(buf.String()), ShouldBeGreaterThan, 0) }) + + if !isWindows { + Convey("Color console output", t, func() { + m := Classic() + m.Get("/:code:int", func(ctx *Context) (int, string) { + return ctx.ParamsInt(":code"), "" + }) + + // Just for testing if logger would capture. + codes := []int{200, 201, 202, 301, 302, 304, 401, 403, 404, 500} + for _, code := range codes { + resp := httptest.NewRecorder() + req, err := http.NewRequest("GET", "http://localhost:4000/"+com.ToStr(code), nil) + So(err, ShouldBeNil) + m.ServeHTTP(resp, req) + So(resp.Code, ShouldEqual, code) + } + }) + } } diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/macaron.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/macaron.go index 2a9fbe101da..42328242b44 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/macaron.go +++ b/Godeps/_workspace/src/github.com/Unknwon/macaron/macaron.go @@ -24,12 +24,15 @@ import ( "strings" "github.com/Unknwon/com" + "gopkg.in/ini.v1" "github.com/Unknwon/macaron/inject" ) +const _VERSION = "0.4.9.1229" + func Version() string { - return "0.4.5.1122" + return _VERSION } // Handler can be any callable function. @@ -39,9 +42,17 @@ type Handler interface{} // validateHandler makes sure a handler is a callable function, // and panics if it is not. -func validateHandler(handler Handler) { - if reflect.TypeOf(handler).Kind() != reflect.Func { - panic("mocaron handler must be a callable function") +func validateHandler(h Handler) { + if reflect.TypeOf(h).Kind() != reflect.Func { + panic("Macaron handler must be a callable function") + } +} + +// validateHandlers makes sure handlers are callable functions, +// and panics if any of them is not. +func validateHandlers(handlers []Handler) { + for _, h := range handlers { + validateHandler(h) } } @@ -210,9 +221,9 @@ func (m *Macaron) SetURLPrefix(prefix string) { // \/ \/ \/ \/ \/ const ( - DEV string = "development" - PROD string = "production" - TEST string = "test" + DEV = "development" + PROD = "production" + TEST = "test" ) var ( @@ -225,6 +236,9 @@ var ( // Flash applies to current request. FlashNow bool + + // Configuration convention object. + cfg *ini.File ) func setENV(e string) { @@ -235,9 +249,25 @@ func setENV(e string) { func init() { setENV(os.Getenv("MACARON_ENV")) + var err error Root, err = os.Getwd() if err != nil { - panic(err) + panic("error getting work directory: " + err.Error()) } } + +// SetConfig sets data sources for configuration. +func SetConfig(source interface{}, others ...interface{}) (err error) { + cfg, err = ini.Load(source, others...) + return err +} + +// Config returns configuration convention object. +// It returns an empty object if there is no one available. +func Config() *ini.File { + if cfg == nil { + return &ini.File{} + } + return cfg +} diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/macaron_test.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/macaron_test.go index 01a7ccd5a9b..4e9cb913e3b 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/macaron_test.go +++ b/Godeps/_workspace/src/github.com/Unknwon/macaron/macaron_test.go @@ -25,6 +25,12 @@ import ( . "github.com/smartystreets/goconvey/convey" ) +func Test_Version(t *testing.T) { + Convey("Get version", t, func() { + So(Version(), ShouldEqual, _VERSION) + }) +} + func Test_New(t *testing.T) { Convey("Initialize a new instance", t, func() { So(New(), ShouldNotBeNil) @@ -171,11 +177,14 @@ func Test_Macaron_Written(t *testing.T) { func Test_Macaron_Basic_NoRace(t *testing.T) { Convey("Make sure no race between requests", t, func() { m := New() + handlers := []Handler{func() {}, func() {}} + // Ensure append will not realloc to trigger the race condition + m.handlers = handlers[:1] m.Get("/", func() {}) + req, _ := http.NewRequest("GET", "/", nil) for i := 0; i < 2; i++ { go func() { resp := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/", nil) m.ServeHTTP(resp, req) }() } @@ -199,8 +208,10 @@ func Test_SetENV(t *testing.T) { }) } -func Test_Version(t *testing.T) { - Convey("Get version", t, func() { - Version() +func Test_Config(t *testing.T) { + Convey("Set and get configuration object", t, func() { + So(Config(), ShouldNotBeNil) + So(SetConfig([]byte("")), ShouldBeNil) + So(Config(), ShouldNotBeNil) }) } diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/recovery.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/recovery.go index ef7c5bfa2e9..6ff1659e8ad 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/recovery.go +++ b/Godeps/_workspace/src/github.com/Unknwon/macaron/recovery.go @@ -1,4 +1,5 @@ // Copyright 2013 Martini Authors +// Copyright 2014 Unknwon // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/render.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/render.go index 31e0d661fde..bc13888dcc6 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/render.go +++ b/Godeps/_workspace/src/github.com/Unknwon/macaron/render.go @@ -307,9 +307,6 @@ func prepareOptions(options []RenderOptions) RenderOptions { if len(opt.HTMLContentType) == 0 { opt.HTMLContentType = ContentHTML } - // if opt.TemplateFileSystem == nil { - // opt.TemplateFileSystem = newTemplateFileSystem(opt) - // } return opt } diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/router.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/router.go index f49a9bb716d..7971450d5a4 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/router.go +++ b/Godeps/_workspace/src/github.com/Unknwon/macaron/router.go @@ -147,10 +147,7 @@ func (r *Router) Handle(method string, pattern string, handlers []Handler) { h = append(h, handlers...) handlers = h } - // verify handlers by cnphpbb at 20140803 23:51 - for _, handler := range handlers { - validateHandler(handler) - } + validateHandlers(handlers) r.handle(method, pattern, func(resp http.ResponseWriter, req *http.Request, params Params) { c := r.m.createContext(resp, req) @@ -217,8 +214,8 @@ func (r *Router) Route(pattern, methods string, h ...Handler) { } // Combo returns a combo router. -func (r *Router) Combo(pattern string) *ComboRouter { - return &ComboRouter{r, pattern, map[string]bool{}} +func (r *Router) Combo(pattern string, h ...Handler) *ComboRouter { + return &ComboRouter{r, pattern, h, map[string]bool{}} } // Configurable http.HandlerFunc which is called when no matching route is @@ -253,9 +250,10 @@ func (r *Router) ServeHTTP(rw http.ResponseWriter, req *http.Request) { // ComboRouter represents a combo router. type ComboRouter struct { - router *Router - pattern string - methods map[string]bool // Registered methods. + router *Router + pattern string + handlers []Handler + methods map[string]bool // Registered methods. } func (cr *ComboRouter) checkMethod(name string) { @@ -265,44 +263,36 @@ func (cr *ComboRouter) checkMethod(name string) { cr.methods[name] = true } -func (cr *ComboRouter) Get(h ...Handler) *ComboRouter { - cr.checkMethod("GET") - cr.router.Get(cr.pattern, h...) +func (cr *ComboRouter) route(fn func(string, ...Handler), method string, h ...Handler) *ComboRouter { + cr.checkMethod(method) + fn(cr.pattern, append(cr.handlers, h...)...) return cr } +func (cr *ComboRouter) Get(h ...Handler) *ComboRouter { + return cr.route(cr.router.Get, "GET", h...) +} + func (cr *ComboRouter) Patch(h ...Handler) *ComboRouter { - cr.checkMethod("PATCH") - cr.router.Patch(cr.pattern, h...) - return cr + return cr.route(cr.router.Patch, "PATCH", h...) } func (cr *ComboRouter) Post(h ...Handler) *ComboRouter { - cr.checkMethod("POST") - cr.router.Post(cr.pattern, h...) - return cr + return cr.route(cr.router.Post, "POST", h...) } func (cr *ComboRouter) Put(h ...Handler) *ComboRouter { - cr.checkMethod("PUT") - cr.router.Put(cr.pattern, h...) - return cr + return cr.route(cr.router.Put, "PUT", h...) } func (cr *ComboRouter) Delete(h ...Handler) *ComboRouter { - cr.checkMethod("DELETE") - cr.router.Delete(cr.pattern, h...) - return cr + return cr.route(cr.router.Delete, "DELETE", h...) } func (cr *ComboRouter) Options(h ...Handler) *ComboRouter { - cr.checkMethod("OPTIONS") - cr.router.Options(cr.pattern, h...) - return cr + return cr.route(cr.router.Options, "OPTIONS", h...) } func (cr *ComboRouter) Head(h ...Handler) *ComboRouter { - cr.checkMethod("HEAD") - cr.router.Head(cr.pattern, h...) - return cr + return cr.route(cr.router.Head, "HEAD", h...) } diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/router_test.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/router_test.go index 23215cc8387..f4044965bb3 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/router_test.go +++ b/Godeps/_workspace/src/github.com/Unknwon/macaron/router_test.go @@ -110,21 +110,24 @@ func Test_Router_Handle(t *testing.T) { Convey("Register all HTTP methods routes with combo", t, func() { m := Classic() m.SetURLPrefix("/prefix") - m.Combo("/"). - Get(func() string { return "GET" }). - Patch(func() string { return "PATCH" }). - Post(func() string { return "POST" }). - Put(func() string { return "PUT" }). - Delete(func() string { return "DELETE" }). - Options(func() string { return "OPTIONS" }). - Head(func() string { return "HEAD" }) + m.Use(Renderer()) + m.Combo("/", func(ctx *Context) { + ctx.Data["prefix"] = "Prefix_" + }). + Get(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "GET" }). + Patch(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "PATCH" }). + Post(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "POST" }). + Put(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "PUT" }). + Delete(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "DELETE" }). + Options(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "OPTIONS" }). + Head(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "HEAD" }) for name := range _HTTP_METHODS { resp := httptest.NewRecorder() req, err := http.NewRequest(name, "/", nil) So(err, ShouldBeNil) m.ServeHTTP(resp, req) - So(resp.Body.String(), ShouldEqual, name) + So(resp.Body.String(), ShouldEqual, "Prefix_"+name) } defer func() { diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/static.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/static.go index 13d2c66f697..69d523d6203 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/static.go +++ b/Godeps/_workspace/src/github.com/Unknwon/macaron/static.go @@ -116,7 +116,7 @@ func prepareStaticOptions(dir string, options []StaticOptions) StaticOptions { func staticHandler(ctx *Context, log *log.Logger, opt StaticOptions) bool { if ctx.Req.Method != "GET" && ctx.Req.Method != "HEAD" { - return true + return false } file := ctx.Req.URL.Path @@ -185,6 +185,7 @@ func Static(directory string, staticOpt ...StaticOptions) Handler { } } +// Statics registers multiple static middleware handlers all at once. func Statics(opt StaticOptions, dirs ...string) Handler { if len(dirs) == 0 { panic("no static directory is given") diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/tree.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/tree.go index a1c05fb51c3..7bde5add69d 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/tree.go +++ b/Godeps/_workspace/src/github.com/Unknwon/macaron/tree.go @@ -15,7 +15,7 @@ package macaron -// NOTE: last sync 90cff5f on Nov 2, 2014. +// NOTE: last sync 0c93364 on Dec 19, 2014. import ( "path" @@ -142,6 +142,10 @@ func NewTree() *Tree { // "/admin/" -> ["admin"] // "/admin/users" -> ["admin", "users"] func splitPath(pattern string) []string { + if len(pattern) == 0 { + return []string{} + } + elements := strings.Split(pattern, "/") if elements[0] == "" { elements = elements[1:] diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/tree_test.go b/Godeps/_workspace/src/github.com/Unknwon/macaron/tree_test.go index 0433d863bc3..c8144160e8c 100644 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/tree_test.go +++ b/Godeps/_workspace/src/github.com/Unknwon/macaron/tree_test.go @@ -66,6 +66,7 @@ func Test_Tree_Match(t *testing.T) { {"/:id", "/123", map[string]string{":id": "123"}}, {"/hello/?:id", "/hello", map[string]string{":id": ""}}, {"/", "/", nil}, + {"", "", nil}, {"/customer/login", "/customer/login", nil}, {"/customer/login", "/customer/login.json", map[string]string{":ext": "json"}}, {"/*", "/customer/123", map[string]string{":splat": "customer/123"}}, diff --git a/Godeps/_workspace/src/github.com/Unknwon/macaron/wercker.yml b/Godeps/_workspace/src/github.com/Unknwon/macaron/wercker.yml deleted file mode 100644 index 3ab8084cc6a..00000000000 --- a/Godeps/_workspace/src/github.com/Unknwon/macaron/wercker.yml +++ /dev/null @@ -1 +0,0 @@ -box: wercker/default \ No newline at end of file diff --git a/Makefile b/Makefile index 8e803d6980c..7b74528cea0 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ build: go test ./pkg/... lint: - @gofmt -w . && go tool vet pkg/**/*.go && echo "$(GOLINT)" + @gofmt -w pkg && go tool vet pkg/**/*.go && echo "$(GOLINT)" setup: go get github.com/tools/godep diff --git a/grafana-pro b/grafana-pro index d160414c599..f1cd9a058e8 100755 Binary files a/grafana-pro and b/grafana-pro differ