From c80c882d08e9807386a9e8d7abf8836093f70bd2 Mon Sep 17 00:00:00 2001 From: yuyuanshifu <747342561@qq.com> Date: Wed, 21 Oct 2020 17:41:44 +0800 Subject: [PATCH] add interface: query public datasets --- custom/conf/app.ini.sample | 3 + go.mod | 2 + go.sum | 6 + models/attachment.go | 9 + modules/context/auth.go | 29 + modules/setting/setting.go | 8 + routers/repo/attachment.go | 44 +- routers/routes/routes.go | 5 + vendor/github.com/go-macaron/auth/LICENSE | 20 + vendor/github.com/go-macaron/auth/README.md | 68 ++ vendor/github.com/go-macaron/auth/basic.go | 59 ++ vendor/github.com/go-macaron/auth/bearer.go | 44 ++ vendor/github.com/go-macaron/auth/util.go | 14 + vendor/github.com/go-macaron/auth/wercker.yml | 1 + .../github.com/go-macaron/inject/.travis.yml | 14 + vendor/github.com/go-macaron/inject/LICENSE | 191 +++++ vendor/github.com/go-macaron/inject/README.md | 11 + vendor/github.com/go-macaron/inject/inject.go | 262 +++++++ vendor/gopkg.in/macaron.v1/.gitignore | 3 + vendor/gopkg.in/macaron.v1/LICENSE | 191 +++++ vendor/gopkg.in/macaron.v1/README.md | 96 +++ vendor/gopkg.in/macaron.v1/codecov.yml | 9 + vendor/gopkg.in/macaron.v1/context.go | 537 +++++++++++++ vendor/gopkg.in/macaron.v1/go.mod | 13 + vendor/gopkg.in/macaron.v1/go.sum | 32 + vendor/gopkg.in/macaron.v1/logger.go | 73 ++ vendor/gopkg.in/macaron.v1/macaron.go | 334 ++++++++ vendor/gopkg.in/macaron.v1/macaronlogo.png | Bin 0 -> 88924 bytes vendor/gopkg.in/macaron.v1/recovery.go | 163 ++++ vendor/gopkg.in/macaron.v1/render.go | 724 ++++++++++++++++++ vendor/gopkg.in/macaron.v1/response_writer.go | 124 +++ vendor/gopkg.in/macaron.v1/return_handler.go | 76 ++ vendor/gopkg.in/macaron.v1/router.go | 380 +++++++++ vendor/gopkg.in/macaron.v1/static.go | 230 ++++++ vendor/gopkg.in/macaron.v1/tree.go | 390 ++++++++++ vendor/gopkg.in/macaron.v1/util_go17.go | 25 + vendor/gopkg.in/macaron.v1/util_go18.go | 24 + vendor/modules.txt | 143 +--- 38 files changed, 4219 insertions(+), 138 deletions(-) mode change 100755 => 100644 go.mod mode change 100755 => 100644 go.sum mode change 100644 => 100755 modules/context/auth.go create mode 100644 vendor/github.com/go-macaron/auth/LICENSE create mode 100644 vendor/github.com/go-macaron/auth/README.md create mode 100644 vendor/github.com/go-macaron/auth/basic.go create mode 100644 vendor/github.com/go-macaron/auth/bearer.go create mode 100644 vendor/github.com/go-macaron/auth/util.go create mode 100644 vendor/github.com/go-macaron/auth/wercker.yml create mode 100644 vendor/github.com/go-macaron/inject/.travis.yml create mode 100644 vendor/github.com/go-macaron/inject/LICENSE create mode 100644 vendor/github.com/go-macaron/inject/README.md create mode 100644 vendor/github.com/go-macaron/inject/inject.go create mode 100644 vendor/gopkg.in/macaron.v1/.gitignore create mode 100644 vendor/gopkg.in/macaron.v1/LICENSE create mode 100644 vendor/gopkg.in/macaron.v1/README.md create mode 100644 vendor/gopkg.in/macaron.v1/codecov.yml create mode 100644 vendor/gopkg.in/macaron.v1/context.go create mode 100644 vendor/gopkg.in/macaron.v1/go.mod create mode 100644 vendor/gopkg.in/macaron.v1/go.sum create mode 100644 vendor/gopkg.in/macaron.v1/logger.go create mode 100644 vendor/gopkg.in/macaron.v1/macaron.go create mode 100644 vendor/gopkg.in/macaron.v1/macaronlogo.png create mode 100644 vendor/gopkg.in/macaron.v1/recovery.go create mode 100644 vendor/gopkg.in/macaron.v1/render.go create mode 100644 vendor/gopkg.in/macaron.v1/response_writer.go create mode 100644 vendor/gopkg.in/macaron.v1/return_handler.go create mode 100644 vendor/gopkg.in/macaron.v1/router.go create mode 100644 vendor/gopkg.in/macaron.v1/static.go create mode 100644 vendor/gopkg.in/macaron.v1/tree.go create mode 100644 vendor/gopkg.in/macaron.v1/util_go17.go create mode 100644 vendor/gopkg.in/macaron.v1/util_go18.go diff --git a/custom/conf/app.ini.sample b/custom/conf/app.ini.sample index f25ef8b8f..f166912aa 100755 --- a/custom/conf/app.ini.sample +++ b/custom/conf/app.ini.sample @@ -1049,6 +1049,9 @@ RESULT_BACKEND = redis://localhost:6379 HOST = http://192.168.204.24 USERNAME = PASSWORD = +; cloudbrain visit opendata +USER = cW4cMtH24eoWPE7X +PWD = 4BPmgvK2hb2Eywwyp4YZRY4B7yQf4DAC [decompress] HOST = http://192.168.207.34:39987 diff --git a/go.mod b/go.mod old mode 100755 new mode 100644 index 684fcf879..e74bdf513 --- a/go.mod +++ b/go.mod @@ -42,6 +42,7 @@ require ( github.com/go-git/go-billy/v5 v5.0.0 github.com/go-git/go-git/v5 v5.0.0 github.com/go-ini/ini v1.56.0 // indirect + github.com/go-macaron/auth v0.0.0-20161228062157-884c0e6c9b92 github.com/go-openapi/jsonreference v0.19.3 // indirect github.com/go-redis/redis v6.15.2+incompatible github.com/go-resty/resty/v2 v2.3.0 @@ -121,6 +122,7 @@ require ( gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/ini.v1 v1.52.0 gopkg.in/ldap.v3 v3.0.2 + gopkg.in/macaron.v1 v1.3.9 // indirect gopkg.in/testfixtures.v2 v2.5.0 gopkg.in/yaml.v2 v2.2.8 mvdan.cc/xurls/v2 v2.1.0 diff --git a/go.sum b/go.sum old mode 100755 new mode 100644 index 7ed818a6a..35b7cd295 --- a/go.sum +++ b/go.sum @@ -234,6 +234,10 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-macaron/auth v0.0.0-20161228062157-884c0e6c9b92 h1:71YL0o1ch/APk7bmW9jWKBY7ibd9U8NQX2bQ25EJMHI= +github.com/go-macaron/auth v0.0.0-20161228062157-884c0e6c9b92/go.mod h1:saPJfEeea+kiiZJF4GG7TSOao0T/Yrm5rpsqYglss6k= +github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191 h1:NjHlg70DuOkcAMqgt0+XA+NHwtu66MkTVVgR4fFWbcI= +github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191/go.mod h1:VFI2o2q9kYsC4o7VP1HrEVosiZZTd+MVT3YZx4gqvJw= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= @@ -1032,6 +1036,8 @@ gopkg.in/ini.v1 v1.52.0 h1:j+Lt/M1oPPejkniCg1TkWE2J3Eh1oZTsHSXzMTzUXn4= gopkg.in/ini.v1 v1.52.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ldap.v3 v3.0.2 h1:R6RBtabK6e1GO0eQKtkyOFbAHO73QesLzI2w2DZ6b9w= gopkg.in/ldap.v3 v3.0.2/go.mod h1:oxD7NyBuxchC+SgJDE1Q5Od05eGt29SDQVBmV+HYbzw= +gopkg.in/macaron.v1 v1.3.9 h1:Dw+DDRYdXgQyEsPlfAfKz+UA5qVUrH3KPD7JhmZ9MFc= +gopkg.in/macaron.v1 v1.3.9/go.mod h1:uMZCFccv9yr5TipIalVOyAyZQuOH3OkmXvgcWwhJuP4= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/testfixtures.v2 v2.5.0 h1:N08B7l2GzFQenyYbzqthDnKAA+cmb17iAZhhFxr7JHw= diff --git a/models/attachment.go b/models/attachment.go index afb2bbe16..be0507d37 100755 --- a/models/attachment.go +++ b/models/attachment.go @@ -346,3 +346,12 @@ func getUnDecompressAttachments(e Engine) ([]*Attachment, error) { attachments := make([]*Attachment, 0, 10) return attachments, e.Where("decompress_state = ? and dataset_id != 0 and name like '%.zip'", DecompressStateInit).Find(&attachments) } + +func GetAllPublicAttachments() ([]*Attachment, error) { + return getAllPublicAttachments(x) +} + +func getAllPublicAttachments(e Engine) ([]*Attachment, error) { + attachments := make([]*Attachment, 0, 10) + return attachments, e.Where("is_private = true ").Find(&attachments) +} diff --git a/modules/context/auth.go b/modules/context/auth.go old mode 100644 new mode 100755 index 7410db75a..34b74523d --- a/modules/context/auth.go +++ b/modules/context/auth.go @@ -10,9 +10,13 @@ import ( "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "encoding/base64" + "net/http" "gitea.com/macaron/csrf" "gitea.com/macaron/macaron" + + marc_auth "github.com/go-macaron/auth" ) // ToggleOptions contains required or check options @@ -21,6 +25,7 @@ type ToggleOptions struct { SignOutRequired bool AdminRequired bool DisableCSRF bool + BasicAuthRequired bool } // Toggle returns toggle options as middleware @@ -130,5 +135,29 @@ func Toggle(options *ToggleOptions) macaron.Handler { } ctx.Data["PageIsAdmin"] = true } + + if options.BasicAuthRequired { + if !basicAuth(ctx) { + basicUnauthorized(ctx.Resp) + return + } + } } } + +func basicAuth(ctx *Context) bool { + var siteAuth = base64.StdEncoding.EncodeToString([]byte(setting.CBAuthUser + ":" + setting.CBAuthPassword)) + auth := ctx.Req.Header.Get("Authorization") + + if !marc_auth.SecureCompare(auth, "Basic " + siteAuth) { + return false + } + + return true + +} + +func basicUnauthorized(res http.ResponseWriter) { + res.Header().Set("WWW-Authenticate", "Basic realm=\"" + marc_auth.BasicRealm + "\"") + http.Error(res, "Not Authorized", http.StatusUnauthorized) +} diff --git a/modules/setting/setting.go b/modules/setting/setting.go index d103fb796..a1b72594e 100755 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -430,6 +430,10 @@ var ( DecompressAddress string AuthUser string AuthPassword string + + //cloudbrain config + CBAuthUser string + CBAuthPassword string ) // DateLang transforms standard language locale name to corresponding value in datetime plugin. @@ -1097,6 +1101,10 @@ func NewContext() { DecompressAddress = sec.Key("HOST").MustString("http://192.168.207.34:39987") AuthUser = sec.Key("USER").MustString("cW4cMtH24eoWPE7X") AuthPassword = sec.Key("PASSWORD").MustString("4BPmgvK2hb2Eywwyp4YZRY4B7yQf4DAC") + + sec = Cfg.Section("cloudbrain") + CBAuthUser = sec.Key("USER").MustString("cW4cMtH24eoWPE7X") + CBAuthPassword = sec.Key("PWD").MustString("4BPmgvK2hb2Eywwyp4YZRY4B7yQf4DAC") } func loadInternalToken(sec *ini.Section) string { diff --git a/routers/repo/attachment.go b/routers/repo/attachment.go index 71446f1d4..c500a8867 100755 --- a/routers/repo/attachment.go +++ b/routers/repo/attachment.go @@ -6,6 +6,7 @@ package repo import ( contexExt "context" + "encoding/json" "fmt" "net/http" "strconv" @@ -29,6 +30,11 @@ const ( DecompressFailed = "1" ) +type PublicDataset struct { + Name string `json:"name"` + Path string `json:"path"` +} + func RenderAttachmentSettings(ctx *context.Context) { renderAttachmentSettings(ctx) } @@ -374,7 +380,7 @@ func GetSuccessChunks(ctx *context.Context) { chunks, err = storage.GetPartInfos(fileChunk.UUID, fileChunk.UploadID) if err != nil { - ctx.ServerError("json.Marshal failed", err) + ctx.ServerError("GetPartInfos failed", err) return } } @@ -601,3 +607,39 @@ func HandleUnDecompressAttachment() { return } + +func QueryAllPublicDataset(ctx *context.Context){ + log.Info("QueryAllPublicDataset") + + attachs, err := models.GetAllPublicAttachments() + if err != nil { + ctx.JSON(200, map[string]string{ + "result_code": "-1", + "data": "", + }) + return + } + + var publicDatasets []PublicDataset + for _, attch := range attachs { + publicDatasets = append(publicDatasets, PublicDataset{attch.Name, + models.AttachmentRelativePath(attch.UUID)}) + } + + data,err := json.Marshal(publicDatasets) + if err != nil { + log.Error("json.Marshal failed:", err.Error()) + ctx.JSON(200, map[string]string{ + "result_code": "-1", + "data": "", + }) + return + } + + log.Info(string(data)) + + ctx.JSON(200, map[string]string{ + "result_code": "0", + "data": string(data), + }) +} diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 0ef12c581..5d6e015f1 100755 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -245,6 +245,7 @@ func RegisterRoutes(m *macaron.Macaron) { ignSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: setting.Service.RequireSignInView}) ignSignInAndCsrf := context.Toggle(&context.ToggleOptions{DisableCSRF: true}) reqSignOut := context.Toggle(&context.ToggleOptions{SignOutRequired: true}) + reqBasicAuth := context.Toggle(&context.ToggleOptions{BasicAuthRequired:true}) bindIgnErr := binding.BindIgnErr validation.AddBindingRules() @@ -533,6 +534,10 @@ func RegisterRoutes(m *macaron.Macaron) { m.Post("/decompress_done_notify", repo.UpdateAttachmentDecompressState) }) + m.Group("/attachments/public", func() { + m.Get("/query", repo.QueryAllPublicDataset) + }, reqBasicAuth) + m.Group("/:username", func() { m.Post("/action/:action", user.Action) }, reqSignIn) diff --git a/vendor/github.com/go-macaron/auth/LICENSE b/vendor/github.com/go-macaron/auth/LICENSE new file mode 100644 index 000000000..eb68a0e05 --- /dev/null +++ b/vendor/github.com/go-macaron/auth/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Jeremy Saenz + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/go-macaron/auth/README.md b/vendor/github.com/go-macaron/auth/README.md new file mode 100644 index 000000000..da39231a5 --- /dev/null +++ b/vendor/github.com/go-macaron/auth/README.md @@ -0,0 +1,68 @@ +# auth +Macaron middleware/handler for http basic authentication. Modified from + +[API Reference](http://godoc.org/github.com/go-macaron/auth) + +## Simple Usage + +Use `auth.Basic` to authenticate against a pre-defined username and password: + +~~~ go +import ( + "gopkg.in/macaron.v1" + "github.com/go-macaron/auth" +) + +func main() { + m := macaron.Classic() + // authenticate every request + m.Use(auth.Basic("username", "secretpassword")) + m.Run() +} +~~~ + +## Advanced Usage + +Using `auth.BasicFunc` lets you authenticate on a per-user level, by checking +the username and password in the callback function: + +~~~ go +import ( + "gopkg.in/macaron.v1" + "github.com/go-macaron/auth" +) + +func main() { + m := macaron.Classic() + // authenticate every request + m.Use(auth.BasicFunc(func(username, password string) bool { + return username == "admin" && password == "guessme" + })) + m.Run() +} +~~~ + +Note that checking usernames and passwords with string comparison might be +susceptible to timing attacks. To avoid that, use `auth.SecureCompare` instead: + +~~~ go + m.Use(auth.BasicFunc(func(username, password string) bool { + return auth.SecureCompare(username, "admin") && auth.SecureCompare(password, "guessme") + })) +} +~~~ + +Upon successful authentication, the username is available to all subsequent +handlers via the `auth.User` type: + +~~~ go + m.Get("/", func(user auth.User) string { + return "Welcome, " + string(user) + }) +} +~~~ + +## Authors +* [Jeremy Saenz](https://github.com/codegangsta) +* [Brendon Murphy](https://github.com/bemurphy) +* [codeskyblue](https://github.com/codeskyblue) diff --git a/vendor/github.com/go-macaron/auth/basic.go b/vendor/github.com/go-macaron/auth/basic.go new file mode 100644 index 000000000..3516d8a36 --- /dev/null +++ b/vendor/github.com/go-macaron/auth/basic.go @@ -0,0 +1,59 @@ +package auth + +import ( + "encoding/base64" + "net/http" + "strings" + + "gopkg.in/macaron.v1" +) + +// User is the authenticated username that was extracted from the request. +type User string + +// BasicRealm is used when setting the WWW-Authenticate response header. +var BasicRealm = "Authorization Required" +var basicPrefix = "Basic " + +// Basic returns a Handler that authenticates via Basic Auth. Writes a http.StatusUnauthorized +// if authentication fails. +func Basic(username string, password string) macaron.Handler { + var siteAuth = base64.StdEncoding.EncodeToString([]byte(username + ":" + password)) + return func(res http.ResponseWriter, req *http.Request, c *macaron.Context) { + auth := req.Header.Get("Authorization") + if !SecureCompare(auth, basicPrefix+siteAuth) { + basicUnauthorized(res) + return + } + c.Map(User(username)) + } +} + +// BasicFunc returns a Handler that authenticates via Basic Auth using the provided function. +// The function should return true for a valid username/password combination. +func BasicFunc(authfn func(string, string) bool) macaron.Handler { + return func(res http.ResponseWriter, req *http.Request, c *macaron.Context) { + auth := req.Header.Get("Authorization") + n := len(basicPrefix) + if len(auth) < n || auth[:n] != basicPrefix { + basicUnauthorized(res) + return + } + b, err := base64.StdEncoding.DecodeString(auth[n:]) + if err != nil { + basicUnauthorized(res) + return + } + tokens := strings.SplitN(string(b), ":", 2) + if len(tokens) != 2 || !authfn(tokens[0], tokens[1]) { + basicUnauthorized(res) + return + } + c.Map(User(tokens[0])) + } +} + +func basicUnauthorized(res http.ResponseWriter) { + res.Header().Set("WWW-Authenticate", "Basic realm=\""+BasicRealm+"\"") + http.Error(res, "Not Authorized", http.StatusUnauthorized) +} diff --git a/vendor/github.com/go-macaron/auth/bearer.go b/vendor/github.com/go-macaron/auth/bearer.go new file mode 100644 index 000000000..5415ddfd4 --- /dev/null +++ b/vendor/github.com/go-macaron/auth/bearer.go @@ -0,0 +1,44 @@ +package auth + +import ( + "net/http" + + "gopkg.in/macaron.v1" +) + +var bearerPrefix = "Bearer " + +// Bearer returns a Handler that authenticates via Bearer Auth. Writes a http.StatusUnauthorized +// if authentication fails. +func Bearer(token string) macaron.Handler { + return func(res http.ResponseWriter, req *http.Request, c *macaron.Context) { + auth := req.Header.Get("Authorization") + if !SecureCompare(auth, bearerPrefix+token) { + bearerUnauthorized(res) + return + } + c.Map(User("")) + } +} + +// BearerFunc returns a Handler that authenticates via Bearer Auth using the provided function. +// The function should return true for a valid bearer token. +func BearerFunc(authfn func(string) bool) macaron.Handler { + return func(res http.ResponseWriter, req *http.Request, c *macaron.Context) { + auth := req.Header.Get("Authorization") + n := len(bearerPrefix) + if len(auth) < n || auth[:n] != bearerPrefix { + bearerUnauthorized(res) + return + } + if !authfn(auth[n:]) { + bearerUnauthorized(res) + return + } + c.Map(User("")) + } +} + +func bearerUnauthorized(res http.ResponseWriter) { + http.Error(res, "Not Authorized", http.StatusUnauthorized) +} diff --git a/vendor/github.com/go-macaron/auth/util.go b/vendor/github.com/go-macaron/auth/util.go new file mode 100644 index 000000000..9ee606cc4 --- /dev/null +++ b/vendor/github.com/go-macaron/auth/util.go @@ -0,0 +1,14 @@ +package auth + +import ( + "crypto/sha512" + "crypto/subtle" +) + +// SecureCompare performs a constant time compare of two strings to limit timing attacks. +func SecureCompare(given string, actual string) bool { + givenSha := sha512.Sum512([]byte(given)) + actualSha := sha512.Sum512([]byte(actual)) + + return subtle.ConstantTimeCompare(givenSha[:], actualSha[:]) == 1 +} diff --git a/vendor/github.com/go-macaron/auth/wercker.yml b/vendor/github.com/go-macaron/auth/wercker.yml new file mode 100644 index 000000000..f8bf918a8 --- /dev/null +++ b/vendor/github.com/go-macaron/auth/wercker.yml @@ -0,0 +1 @@ +box: wercker/golang@1.1.1 \ No newline at end of file diff --git a/vendor/github.com/go-macaron/inject/.travis.yml b/vendor/github.com/go-macaron/inject/.travis.yml new file mode 100644 index 000000000..2774fb35d --- /dev/null +++ b/vendor/github.com/go-macaron/inject/.travis.yml @@ -0,0 +1,14 @@ +sudo: false +language: go + +go: + - 1.3 + - 1.4 + - 1.5 + - tip + +script: go test -v -cover -race + +notifications: + email: + - u@gogs.io diff --git a/vendor/github.com/go-macaron/inject/LICENSE b/vendor/github.com/go-macaron/inject/LICENSE new file mode 100644 index 000000000..37ec93a14 --- /dev/null +++ b/vendor/github.com/go-macaron/inject/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/go-macaron/inject/README.md b/vendor/github.com/go-macaron/inject/README.md new file mode 100644 index 000000000..c65c76955 --- /dev/null +++ b/vendor/github.com/go-macaron/inject/README.md @@ -0,0 +1,11 @@ +# inject [![Build Status](https://travis-ci.org/go-macaron/inject.svg?branch=master)](https://travis-ci.org/go-macaron/inject) [![](http://gocover.io/_badge/github.com/go-macaron/inject)](http://gocover.io/github.com/go-macaron/inject) + +Package inject provides utilities for mapping and injecting dependencies in various ways. + +**This a modified version of [codegangsta/inject](https://github.com/codegangsta/inject) for special purpose of Macaron** + +**Please use the original version if you need dependency injection feature** + +## License + +This project is under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for the full license text. \ No newline at end of file diff --git a/vendor/github.com/go-macaron/inject/inject.go b/vendor/github.com/go-macaron/inject/inject.go new file mode 100644 index 000000000..1c1f98eaa --- /dev/null +++ b/vendor/github.com/go-macaron/inject/inject.go @@ -0,0 +1,262 @@ +// Copyright 2013 Jeremy Saenz +// Copyright 2015 The Macaron Authors +// +// 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 +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +// Package inject provides utilities for mapping and injecting dependencies in various ways. +package inject + +import ( + "fmt" + "reflect" +) + +// Injector represents an interface for mapping and injecting dependencies into structs +// and function arguments. +type Injector interface { + Applicator + Invoker + TypeMapper + // SetParent sets the parent of the injector. If the injector cannot find a + // dependency in its Type map it will check its parent before returning an + // error. + SetParent(Injector) +} + +// Applicator represents an interface for mapping dependencies to a struct. +type Applicator interface { + // Maps dependencies in the Type map to each field in the struct + // that is tagged with 'inject'. Returns an error if the injection + // fails. + Apply(interface{}) error +} + +// Invoker represents an interface for calling functions via reflection. +type Invoker interface { + // Invoke attempts to call the interface{} provided as a function, + // providing dependencies for function arguments based on Type. Returns + // a slice of reflect.Value representing the returned values of the function. + // Returns an error if the injection fails. + Invoke(interface{}) ([]reflect.Value, error) +} + +// FastInvoker represents an interface in order to avoid the calling function via reflection. +// +// example: +// type handlerFuncHandler func(http.ResponseWriter, *http.Request) error +// func (f handlerFuncHandler)Invoke([]interface{}) ([]reflect.Value, error){ +// ret := f(p[0].(http.ResponseWriter), p[1].(*http.Request)) +// return []reflect.Value{reflect.ValueOf(ret)}, nil +// } +// +// type funcHandler func(int, string) +// func (f funcHandler)Invoke([]interface{}) ([]reflect.Value, error){ +// f(p[0].(int), p[1].(string)) +// return nil, nil +// } +type FastInvoker interface { + // Invoke attempts to call the ordinary functions. If f is a function + // with the appropriate signature, f.Invoke([]interface{}) is a Call that calls f. + // Returns a slice of reflect.Value representing the returned values of the function. + // Returns an error if the injection fails. + Invoke([]interface{}) ([]reflect.Value, error) +} + +// IsFastInvoker check interface is FastInvoker +func IsFastInvoker(h interface{}) bool { + _, ok := h.(FastInvoker) + return ok +} + +// TypeMapper represents an interface for mapping interface{} values based on type. +type TypeMapper interface { + // Maps the interface{} value based on its immediate type from reflect.TypeOf. + Map(interface{}) TypeMapper + // Maps the interface{} value based on the pointer of an Interface provided. + // This is really only useful for mapping a value as an interface, as interfaces + // cannot at this time be referenced directly without a pointer. + MapTo(interface{}, interface{}) TypeMapper + // Provides a possibility to directly insert a mapping based on type and value. + // This makes it possible to directly map type arguments not possible to instantiate + // with reflect like unidirectional channels. + Set(reflect.Type, reflect.Value) TypeMapper + // Returns the Value that is mapped to the current type. Returns a zeroed Value if + // the Type has not been mapped. + GetVal(reflect.Type) reflect.Value +} + +type injector struct { + values map[reflect.Type]reflect.Value + parent Injector +} + +// InterfaceOf dereferences a pointer to an Interface type. +// It panics if value is not an pointer to an interface. +func InterfaceOf(value interface{}) reflect.Type { + t := reflect.TypeOf(value) + + for t.Kind() == reflect.Ptr { + t = t.Elem() + } + + if t.Kind() != reflect.Interface { + panic("Called inject.InterfaceOf with a value that is not a pointer to an interface. (*MyInterface)(nil)") + } + + return t +} + +// New returns a new Injector. +func New() Injector { + return &injector{ + values: make(map[reflect.Type]reflect.Value), + } +} + +// Invoke attempts to call the interface{} provided as a function, +// providing dependencies for function arguments based on Type. +// Returns a slice of reflect.Value representing the returned values of the function. +// Returns an error if the injection fails. +// It panics if f is not a function +func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) { + t := reflect.TypeOf(f) + switch v := f.(type) { + case FastInvoker: + return inj.fastInvoke(v, t, t.NumIn()) + default: + return inj.callInvoke(f, t, t.NumIn()) + } +} + +func (inj *injector) fastInvoke(f FastInvoker, t reflect.Type, numIn int) ([]reflect.Value, error) { + var in []interface{} + if numIn > 0 { + in = make([]interface{}, numIn) // Panic if t is not kind of Func + var argType reflect.Type + var val reflect.Value + for i := 0; i < numIn; i++ { + argType = t.In(i) + val = inj.GetVal(argType) + if !val.IsValid() { + return nil, fmt.Errorf("Value not found for type %v", argType) + } + + in[i] = val.Interface() + } + } + return f.Invoke(in) +} + +// callInvoke reflect.Value.Call +func (inj *injector) callInvoke(f interface{}, t reflect.Type, numIn int) ([]reflect.Value, error) { + var in []reflect.Value + if numIn > 0 { + in = make([]reflect.Value, numIn) + var argType reflect.Type + var val reflect.Value + for i := 0; i < numIn; i++ { + argType = t.In(i) + val = inj.GetVal(argType) + if !val.IsValid() { + return nil, fmt.Errorf("Value not found for type %v", argType) + } + + in[i] = val + } + } + return reflect.ValueOf(f).Call(in), nil +} + +// Maps dependencies in the Type map to each field in the struct +// that is tagged with 'inject'. +// Returns an error if the injection fails. +func (inj *injector) Apply(val interface{}) error { + v := reflect.ValueOf(val) + + for v.Kind() == reflect.Ptr { + v = v.Elem() + } + + if v.Kind() != reflect.Struct { + return nil // Should not panic here ? + } + + t := v.Type() + + for i := 0; i < v.NumField(); i++ { + f := v.Field(i) + structField := t.Field(i) + if f.CanSet() && (structField.Tag == "inject" || structField.Tag.Get("inject") != "") { + ft := f.Type() + v := inj.GetVal(ft) + if !v.IsValid() { + return fmt.Errorf("Value not found for type %v", ft) + } + + f.Set(v) + } + + } + + return nil +} + +// Maps the concrete value of val to its dynamic type using reflect.TypeOf, +// It returns the TypeMapper registered in. +func (i *injector) Map(val interface{}) TypeMapper { + i.values[reflect.TypeOf(val)] = reflect.ValueOf(val) + return i +} + +func (i *injector) MapTo(val interface{}, ifacePtr interface{}) TypeMapper { + i.values[InterfaceOf(ifacePtr)] = reflect.ValueOf(val) + return i +} + +// Maps the given reflect.Type to the given reflect.Value and returns +// the Typemapper the mapping has been registered in. +func (i *injector) Set(typ reflect.Type, val reflect.Value) TypeMapper { + i.values[typ] = val + return i +} + +func (i *injector) GetVal(t reflect.Type) reflect.Value { + val := i.values[t] + + if val.IsValid() { + return val + } + + // no concrete types found, try to find implementors + // if t is an interface + if t.Kind() == reflect.Interface { + for k, v := range i.values { + if k.Implements(t) { + val = v + break + } + } + } + + // Still no type found, try to look it up on the parent + if !val.IsValid() && i.parent != nil { + val = i.parent.GetVal(t) + } + + return val + +} + +func (i *injector) SetParent(parent Injector) { + i.parent = parent +} diff --git a/vendor/gopkg.in/macaron.v1/.gitignore b/vendor/gopkg.in/macaron.v1/.gitignore new file mode 100644 index 000000000..fc5aca3e4 --- /dev/null +++ b/vendor/gopkg.in/macaron.v1/.gitignore @@ -0,0 +1,3 @@ +macaron.sublime-project +macaron.sublime-workspace +.idea diff --git a/vendor/gopkg.in/macaron.v1/LICENSE b/vendor/gopkg.in/macaron.v1/LICENSE new file mode 100644 index 000000000..c8a16eb2e --- /dev/null +++ b/vendor/gopkg.in/macaron.v1/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright 2014 The Macaron Authors + + 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 a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/gopkg.in/macaron.v1/README.md b/vendor/gopkg.in/macaron.v1/README.md new file mode 100644 index 000000000..22856be35 --- /dev/null +++ b/vendor/gopkg.in/macaron.v1/README.md @@ -0,0 +1,96 @@ +# Macaron + +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/go-macaron/macaron/Go?logo=github&style=for-the-badge)](https://github.com/go-macaron/macaron/actions?query=workflow%3AGo) +[![codecov](https://img.shields.io/codecov/c/github/go-macaron/macaron/master?logo=codecov&style=for-the-badge)](https://codecov.io/gh/go-macaron/macaron) +[![GoDoc](https://img.shields.io/badge/GoDoc-Reference-blue?style=for-the-badge&logo=go)](https://pkg.go.dev/gopkg.in/macaron.v1?tab=doc) +[![Sourcegraph](https://img.shields.io/badge/view%20on-Sourcegraph-brightgreen.svg?style=for-the-badge&logo=sourcegraph)](https://sourcegraph.com/github.com/go-macaron/macaron) + +![Macaron Logo](https://raw.githubusercontent.com/go-macaron/macaron/v1/macaronlogo.png) + +Package macaron is a high productive and modular web framework in Go. + +## Getting Started + +The minimum requirement of Go is **1.6**. + +To install Macaron: + + go get gopkg.in/macaron.v1 + +The very basic usage of Macaron: + +```go +package main + +import "gopkg.in/macaron.v1" + +func main() { + m := macaron.Classic() + m.Get("/", func() string { + return "Hello world!" + }) + m.Run() +} +``` + +## Features + +- Powerful routing with suburl. +- Flexible routes combinations. +- 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. + +## Middlewares + +Middlewares allow you easily plugin/unplugin features for your Macaron applications. + +There are already many [middlewares](https://github.com/go-macaron) to simplify your work: + +- render - Go template engine +- static - Serves static files +- [gzip](https://github.com/go-macaron/gzip) - Gzip compression to all responses +- [binding](https://github.com/go-macaron/binding) - Request data binding and validation +- [i18n](https://github.com/go-macaron/i18n) - Internationalization and Localization +- [cache](https://github.com/go-macaron/cache) - Cache manager +- [session](https://github.com/go-macaron/session) - Session manager +- [csrf](https://github.com/go-macaron/csrf) - Generates and validates csrf tokens +- [captcha](https://github.com/go-macaron/captcha) - Captcha service +- [pongo2](https://github.com/go-macaron/pongo2) - Pongo2 template engine support +- [sockets](https://github.com/go-macaron/sockets) - WebSockets channels binding +- [bindata](https://github.com/go-macaron/bindata) - Embed binary data as static and template files +- [toolbox](https://github.com/go-macaron/toolbox) - Health check, pprof, profile and statistic services +- [oauth2](https://github.com/go-macaron/oauth2) - OAuth 2.0 backend +- [authz](https://github.com/go-macaron/authz) - ACL/RBAC/ABAC authorization based on Casbin +- [switcher](https://github.com/go-macaron/switcher) - Multiple-site support +- [method](https://github.com/go-macaron/method) - HTTP method override +- [permissions2](https://github.com/xyproto/permissions2) - Cookies, users and permissions +- [renders](https://github.com/go-macaron/renders) - Beego-like render engine(Macaron has built-in template engine, this is another option) +- [piwik](https://github.com/veecue/piwik-middleware) - Server-side piwik analytics + +## Use Cases + +- [Gogs](https://gogs.io): A painless self-hosted Git Service +- [Grafana](http://grafana.org/): The open platform for beautiful analytics and monitoring +- [Peach](https://peachdocs.org): A modern web documentation server +- [Go Walker](https://gowalker.org): Go online API documentation +- [Critical Stack Intel](https://intel.criticalstack.com/): A 100% free intel marketplace from Critical Stack, Inc. + +## Getting Help + +- [API Reference](https://gowalker.org/gopkg.in/macaron.v1) +- [Documentation](https://go-macaron.com) +- [FAQs](https://go-macaron.com/docs/faqs) + +## Credits + +- Basic design of [Martini](https://github.com/go-martini/martini). +- Logo is modified by [@insionng](https://github.com/insionng) based on [Tribal Dragon](http://xtremeyamazaki.deviantart.com/art/Tribal-Dragon-27005087). + +## License + +This project is under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for the full license text. diff --git a/vendor/gopkg.in/macaron.v1/codecov.yml b/vendor/gopkg.in/macaron.v1/codecov.yml new file mode 100644 index 000000000..fc947f230 --- /dev/null +++ b/vendor/gopkg.in/macaron.v1/codecov.yml @@ -0,0 +1,9 @@ +coverage: + range: "60...95" + status: + project: + default: + threshold: 1% + +comment: + layout: 'diff, files' diff --git a/vendor/gopkg.in/macaron.v1/context.go b/vendor/gopkg.in/macaron.v1/context.go new file mode 100644 index 000000000..be94d3501 --- /dev/null +++ b/vendor/gopkg.in/macaron.v1/context.go @@ -0,0 +1,537 @@ +// Copyright 2014 The Macaron Authors +// +// 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 +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package macaron + +import ( + "crypto/sha256" + "encoding/hex" + "html/template" + "io" + "io/ioutil" + "mime/multipart" + "net/http" + "net/url" + "os" + "path" + "path/filepath" + "reflect" + "strconv" + "strings" + "time" + + "github.com/go-macaron/inject" + "github.com/unknwon/com" + "golang.org/x/crypto/pbkdf2" +) + +// Locale reprents a localization interface. +type Locale interface { + Language() string + Tr(string, ...interface{}) string +} + +// 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 +} + +// Request represents an HTTP request received by a server or to be sent by a client. +type Request struct { + *http.Request +} + +func (r *Request) Body() *RequestBody { + return &RequestBody{r.Request.Body} +} + +// ContextInvoker is an inject.FastInvoker wrapper of func(ctx *Context). +type ContextInvoker func(ctx *Context) + +func (invoke ContextInvoker) Invoke(params []interface{}) ([]reflect.Value, error) { + invoke(params[0].(*Context)) + return nil, nil +} + +// Context represents the runtime context of current request of Macaron instance. +// It is the integration of most frequently used middlewares and helper methods. +type Context struct { + inject.Injector + handlers []Handler + action Handler + index int + + *Router + Req Request + Resp ResponseWriter + params Params + Render + Locale + Data map[string]interface{} +} + +func (c *Context) handler() Handler { + if c.index < len(c.handlers) { + return c.handlers[c.index] + } + if c.index == len(c.handlers) { + return c.action + } + panic("invalid index for context handler") +} + +func (c *Context) Next() { + c.index += 1 + c.run() +} + +func (c *Context) Written() bool { + return c.Resp.Written() +} + +func (c *Context) run() { + for c.index <= len(c.handlers) { + vals, err := c.Invoke(c.handler()) + if err != nil { + panic(err) + } + c.index += 1 + + // if the handler returned something, write it to the http response + if len(vals) > 0 { + ev := c.GetVal(reflect.TypeOf(ReturnHandler(nil))) + handleReturn := ev.Interface().(ReturnHandler) + handleReturn(c, vals) + } + + if c.Written() { + return + } + } +} + +// RemoteAddr returns more real IP address. +func (ctx *Context) RemoteAddr() string { + addr := ctx.Req.Header.Get("X-Real-IP") + if len(addr) == 0 { + addr = ctx.Req.Header.Get("X-Forwarded-For") + if addr == "" { + addr = ctx.Req.RemoteAddr + if i := strings.LastIndex(addr, ":"); i > -1 { + addr = addr[:i] + } + } + } + return addr +} + +func (ctx *Context) renderHTML(status int, setName, tplName string, data ...interface{}) { + if len(data) <= 0 { + ctx.Render.HTMLSet(status, setName, tplName, ctx.Data) + } else if len(data) == 1 { + ctx.Render.HTMLSet(status, setName, tplName, data[0]) + } else { + ctx.Render.HTMLSet(status, setName, tplName, data[0], data[1].(HTMLOptions)) + } +} + +// HTML renders the HTML with default template set. +func (ctx *Context) HTML(status int, name string, data ...interface{}) { + ctx.renderHTML(status, DEFAULT_TPL_SET_NAME, name, data...) +} + +// HTMLSet renders the HTML with given template set name. +func (ctx *Context) HTMLSet(status int, setName, tplName string, data ...interface{}) { + ctx.renderHTML(status, setName, tplName, data...) +} + +func (ctx *Context) Redirect(location string, status ...int) { + code := http.StatusFound + if len(status) == 1 { + code = status[0] + } + + http.Redirect(ctx.Resp, ctx.Req.Request, location, code) +} + +// Maximum amount of memory to use when parsing a multipart form. +// Set this to whatever value you prefer; default is 10 MB. +var MaxMemory = int64(1024 * 1024 * 10) + +func (ctx *Context) parseForm() { + if ctx.Req.Form != nil { + return + } + + contentType := ctx.Req.Header.Get(_CONTENT_TYPE) + if (ctx.Req.Method == "POST" || ctx.Req.Method == "PUT") && + len(contentType) > 0 && strings.Contains(contentType, "multipart/form-data") { + _ = ctx.Req.ParseMultipartForm(MaxMemory) + } else { + _ = ctx.Req.ParseForm() + } +} + +// Query querys form parameter. +func (ctx *Context) Query(name string) string { + ctx.parseForm() + return ctx.Req.Form.Get(name) +} + +// QueryTrim querys and trims spaces form parameter. +func (ctx *Context) QueryTrim(name string) string { + return strings.TrimSpace(ctx.Query(name)) +} + +// QueryStrings returns a list of results by given query name. +func (ctx *Context) QueryStrings(name string) []string { + ctx.parseForm() + + vals, ok := ctx.Req.Form[name] + if !ok { + return []string{} + } + return vals +} + +// QueryEscape returns escapred query result. +func (ctx *Context) QueryEscape(name string) string { + return template.HTMLEscapeString(ctx.Query(name)) +} + +// QueryBool returns query result in bool type. +func (ctx *Context) QueryBool(name string) bool { + v, _ := strconv.ParseBool(ctx.Query(name)) + return v +} + +// QueryInt returns query result in int type. +func (ctx *Context) QueryInt(name string) int { + return com.StrTo(ctx.Query(name)).MustInt() +} + +// QueryInt64 returns query result in int64 type. +func (ctx *Context) QueryInt64(name string) int64 { + return com.StrTo(ctx.Query(name)).MustInt64() +} + +// QueryFloat64 returns query result in float64 type. +func (ctx *Context) QueryFloat64(name string) float64 { + v, _ := strconv.ParseFloat(ctx.Query(name), 64) + return v +} + +// Params returns value of given param name. +// e.g. ctx.Params(":uid") or ctx.Params("uid") +func (ctx *Context) Params(name string) string { + if len(name) == 0 { + return "" + } + if len(name) > 1 && name[0] != ':' { + name = ":" + name + } + return ctx.params[name] +} + +// AllParams returns all params. +func (ctx *Context) AllParams() Params { + return ctx.params +} + +// SetParams sets value of param with given name. +func (ctx *Context) SetParams(name, val string) { + if name != "*" && !strings.HasPrefix(name, ":") { + name = ":" + name + } + ctx.params[name] = val +} + +// ReplaceAllParams replace all current params with given params +func (ctx *Context) ReplaceAllParams(params Params) { + ctx.params = params +} + +// ParamsEscape returns escapred params result. +// e.g. ctx.ParamsEscape(":uname") +func (ctx *Context) ParamsEscape(name string) string { + return template.HTMLEscapeString(ctx.Params(name)) +} + +// ParamsInt returns params result in int type. +// e.g. ctx.ParamsInt(":uid") +func (ctx *Context) ParamsInt(name string) int { + return com.StrTo(ctx.Params(name)).MustInt() +} + +// ParamsInt64 returns params result in int64 type. +// e.g. ctx.ParamsInt64(":uid") +func (ctx *Context) ParamsInt64(name string) int64 { + return com.StrTo(ctx.Params(name)).MustInt64() +} + +// ParamsFloat64 returns params result in int64 type. +// e.g. ctx.ParamsFloat64(":uid") +func (ctx *Context) ParamsFloat64(name string) float64 { + v, _ := strconv.ParseFloat(ctx.Params(name), 64) + return v +} + +// GetFile returns information about user upload file by given form field name. +func (ctx *Context) GetFile(name string) (multipart.File, *multipart.FileHeader, error) { + return ctx.Req.FormFile(name) +} + +// SaveToFile reads a file from request by field name and saves to given path. +func (ctx *Context) SaveToFile(name, savePath string) error { + fr, _, err := ctx.GetFile(name) + if err != nil { + return err + } + defer fr.Close() + + fw, err := os.OpenFile(savePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) + if err != nil { + return err + } + defer fw.Close() + + _, err = io.Copy(fw, fr) + return err +} + +// SetCookie sets given cookie value to response header. +// FIXME: IE support? http://golanghome.com/post/620#reply2 +func (ctx *Context) SetCookie(name string, value string, others ...interface{}) { + cookie := http.Cookie{} + cookie.Name = name + cookie.Value = url.QueryEscape(value) + + if len(others) > 0 { + switch v := others[0].(type) { + case int: + cookie.MaxAge = v + case int64: + cookie.MaxAge = int(v) + case int32: + cookie.MaxAge = int(v) + } + } + + cookie.Path = "/" + if len(others) > 1 { + if v, ok := others[1].(string); ok && len(v) > 0 { + cookie.Path = v + } + } + + if len(others) > 2 { + if v, ok := others[2].(string); ok && len(v) > 0 { + cookie.Domain = v + } + } + + if len(others) > 3 { + switch v := others[3].(type) { + case bool: + cookie.Secure = v + default: + if others[3] != nil { + cookie.Secure = true + } + } + } + + if len(others) > 4 { + if v, ok := others[4].(bool); ok && v { + cookie.HttpOnly = true + } + } + + if len(others) > 5 { + if v, ok := others[5].(time.Time); ok { + cookie.Expires = v + cookie.RawExpires = v.Format(time.UnixDate) + } + } + + ctx.Resp.Header().Add("Set-Cookie", cookie.String()) +} + +// GetCookie returns given cookie value from request header. +func (ctx *Context) GetCookie(name string) string { + cookie, err := ctx.Req.Cookie(name) + if err != nil { + return "" + } + val, _ := url.QueryUnescape(cookie.Value) + return val +} + +// GetCookieInt returns cookie result in int type. +func (ctx *Context) GetCookieInt(name string) int { + return com.StrTo(ctx.GetCookie(name)).MustInt() +} + +// GetCookieInt64 returns cookie result in int64 type. +func (ctx *Context) GetCookieInt64(name string) int64 { + return com.StrTo(ctx.GetCookie(name)).MustInt64() +} + +// GetCookieFloat64 returns cookie result in float64 type. +func (ctx *Context) GetCookieFloat64(name string) float64 { + v, _ := strconv.ParseFloat(ctx.GetCookie(name), 64) + return v +} + +var defaultCookieSecret string + +// SetDefaultCookieSecret sets global default secure cookie secret. +func (m *Macaron) SetDefaultCookieSecret(secret string) { + defaultCookieSecret = secret +} + +// SetSecureCookie sets given cookie value to response header with default secret string. +func (ctx *Context) SetSecureCookie(name, value string, others ...interface{}) { + ctx.SetSuperSecureCookie(defaultCookieSecret, name, value, others...) +} + +// GetSecureCookie returns given cookie value from request header with default secret string. +func (ctx *Context) GetSecureCookie(key string) (string, bool) { + return ctx.GetSuperSecureCookie(defaultCookieSecret, key) +} + +// SetSuperSecureCookie sets given cookie value to response header with secret string. +func (ctx *Context) SetSuperSecureCookie(secret, name, value string, others ...interface{}) { + key := pbkdf2.Key([]byte(secret), []byte(secret), 1000, 16, sha256.New) + text, err := com.AESGCMEncrypt(key, []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, name string) (string, bool) { + val := ctx.GetCookie(name) + if val == "" { + return "", false + } + + text, err := hex.DecodeString(val) + if err != nil { + return "", false + } + + key := pbkdf2.Key([]byte(secret), []byte(secret), 1000, 16, sha256.New) + text, err = com.AESGCMDecrypt(key, text) + return string(text), err == nil +} + +func (ctx *Context) setRawContentHeader() { + ctx.Resp.Header().Set("Content-Description", "Raw content") + ctx.Resp.Header().Set("Content-Type", "text/plain") + ctx.Resp.Header().Set("Expires", "0") + ctx.Resp.Header().Set("Cache-Control", "must-revalidate") + ctx.Resp.Header().Set("Pragma", "public") +} + +// ServeContent serves given content to response. +func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interface{}) { + modtime := time.Now() + for _, p := range params { + switch v := p.(type) { + case time.Time: + modtime = v + } + } + + ctx.setRawContentHeader() + http.ServeContent(ctx.Resp, ctx.Req.Request, name, modtime, r) +} + +// ServeFileContent serves given file as content to response. +func (ctx *Context) ServeFileContent(file string, names ...string) { + var name string + if len(names) > 0 { + name = names[0] + } else { + name = path.Base(file) + } + + f, err := os.Open(file) + if err != nil { + if Env == PROD { + http.Error(ctx.Resp, "Internal Server Error", 500) + } else { + http.Error(ctx.Resp, err.Error(), 500) + } + return + } + defer f.Close() + + ctx.setRawContentHeader() + http.ServeContent(ctx.Resp, ctx.Req.Request, name, time.Now(), f) +} + +// ServeFile serves given file to response. +func (ctx *Context) ServeFile(file string, names ...string) { + var name string + if len(names) > 0 { + name = names[0] + } else { + name = path.Base(file) + } + ctx.Resp.Header().Set("Content-Description", "File Transfer") + ctx.Resp.Header().Set("Content-Type", "application/octet-stream") + ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+name) + ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary") + ctx.Resp.Header().Set("Expires", "0") + ctx.Resp.Header().Set("Cache-Control", "must-revalidate") + ctx.Resp.Header().Set("Pragma", "public") + http.ServeFile(ctx.Resp, ctx.Req.Request, file) +} + +// ChangeStaticPath changes static path from old to new one. +func (ctx *Context) ChangeStaticPath(oldPath, newPath string) { + if !filepath.IsAbs(oldPath) { + oldPath = filepath.Join(Root, oldPath) + } + dir := statics.Get(oldPath) + if dir != nil { + statics.Delete(oldPath) + + if !filepath.IsAbs(newPath) { + newPath = filepath.Join(Root, newPath) + } + *dir = http.Dir(newPath) + statics.Set(dir) + } +} diff --git a/vendor/gopkg.in/macaron.v1/go.mod b/vendor/gopkg.in/macaron.v1/go.mod new file mode 100644 index 000000000..e82c77846 --- /dev/null +++ b/vendor/gopkg.in/macaron.v1/go.mod @@ -0,0 +1,13 @@ +module gopkg.in/macaron.v1 + +go 1.12 + +require ( + github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191 + github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c // indirect + github.com/smartystreets/assertions v1.0.1 // indirect + github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 + github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e + golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 + gopkg.in/ini.v1 v1.46.0 +) diff --git a/vendor/gopkg.in/macaron.v1/go.sum b/vendor/gopkg.in/macaron.v1/go.sum new file mode 100644 index 000000000..23cb914eb --- /dev/null +++ b/vendor/gopkg.in/macaron.v1/go.sum @@ -0,0 +1,32 @@ +github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191 h1:NjHlg70DuOkcAMqgt0+XA+NHwtu66MkTVVgR4fFWbcI= +github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191/go.mod h1:VFI2o2q9kYsC4o7VP1HrEVosiZZTd+MVT3YZx4gqvJw= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4= +github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE= +github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY= +github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w= +github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= +github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w= +github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e h1:GSGeB9EAKY2spCABz6xOX5DbxZEXolK+nBSvmsQwRjM= +github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +gopkg.in/ini.v1 v1.46.0 h1:VeDZbLYGaupuvIrsYCEOe/L/2Pcs5n7hdO1ZTjporag= +gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= diff --git a/vendor/gopkg.in/macaron.v1/logger.go b/vendor/gopkg.in/macaron.v1/logger.go new file mode 100644 index 000000000..34178d78a --- /dev/null +++ b/vendor/gopkg.in/macaron.v1/logger.go @@ -0,0 +1,73 @@ +// Copyright 2013 Martini Authors +// Copyright 2014 The Macaron Authors +// +// 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 +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package macaron + +import ( + "fmt" + "log" + "net/http" + "reflect" + "runtime" + "time" +) + +var ( + ColorLog = true + LogTimeFormat = "2006-01-02 15:04:05" +) + +func init() { + ColorLog = runtime.GOOS != "windows" +} + +// LoggerInvoker is an inject.FastInvoker wrapper of func(ctx *Context, log *log.Logger). +type LoggerInvoker func(ctx *Context, log *log.Logger) + +func (invoke LoggerInvoker) Invoke(params []interface{}) ([]reflect.Value, error) { + invoke(params[0].(*Context), params[1].(*log.Logger)) + return nil, nil +} + +// 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(ctx *Context, log *log.Logger) { + start := time.Now() + + log.Printf("%s: Started %s %s for %s", time.Now().Format(LogTimeFormat), ctx.Req.Method, ctx.Req.RequestURI, ctx.RemoteAddr()) + + rw := ctx.Resp.(ResponseWriter) + ctx.Next() + + content := fmt.Sprintf("%s: Completed %s %s %v %s in %v", time.Now().Format(LogTimeFormat), ctx.Req.Method, ctx.Req.RequestURI, rw.Status(), http.StatusText(rw.Status()), time.Since(start)) + if ColorLog { + switch rw.Status() { + 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: + content = fmt.Sprintf("\033[1;36m%s\033[0m", content) + } + } + log.Println(content) + } +} diff --git a/vendor/gopkg.in/macaron.v1/macaron.go b/vendor/gopkg.in/macaron.v1/macaron.go new file mode 100644 index 000000000..8b9297a3a --- /dev/null +++ b/vendor/gopkg.in/macaron.v1/macaron.go @@ -0,0 +1,334 @@ +// +build go1.3 + +// Copyright 2014 The Macaron Authors +// +// 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 +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +// Package macaron is a high productive and modular web framework in Go. +package macaron // import "gopkg.in/macaron.v1" + +import ( + "io" + "log" + "net/http" + "os" + "reflect" + "strings" + "sync" + + "github.com/unknwon/com" + "gopkg.in/ini.v1" + + "github.com/go-macaron/inject" +) + +const _VERSION = "1.3.4.0805" + +func Version() string { + return _VERSION +} + +// Handler can be any callable function. +// Macaron attempts to inject services into the handler's argument list, +// and panics if an argument could not be fullfilled via dependency injection. +type Handler interface{} + +// handlerFuncInvoker is an inject.FastInvoker wrapper of func(http.ResponseWriter, *http.Request). +type handlerFuncInvoker func(http.ResponseWriter, *http.Request) + +func (invoke handlerFuncInvoker) Invoke(params []interface{}) ([]reflect.Value, error) { + invoke(params[0].(http.ResponseWriter), params[1].(*http.Request)) + return nil, nil +} + +// internalServerErrorInvoker is an inject.FastInvoker wrapper of func(rw http.ResponseWriter, err error). +type internalServerErrorInvoker func(rw http.ResponseWriter, err error) + +func (invoke internalServerErrorInvoker) Invoke(params []interface{}) ([]reflect.Value, error) { + invoke(params[0].(http.ResponseWriter), params[1].(error)) + return nil, nil +} + +// validateAndWrapHandler makes sure a handler is a callable function, it panics if not. +// When the handler is also potential to be any built-in inject.FastInvoker, +// it wraps the handler automatically to have some performance gain. +func validateAndWrapHandler(h Handler) Handler { + if reflect.TypeOf(h).Kind() != reflect.Func { + panic("Macaron handler must be a callable function") + } + + if !inject.IsFastInvoker(h) { + switch v := h.(type) { + case func(*Context): + return ContextInvoker(v) + case func(*Context, *log.Logger): + return LoggerInvoker(v) + case func(http.ResponseWriter, *http.Request): + return handlerFuncInvoker(v) + case func(http.ResponseWriter, error): + return internalServerErrorInvoker(v) + } + } + return h +} + +// validateAndWrapHandlers preforms validation and wrapping for each input handler. +// It accepts an optional wrapper function to perform custom wrapping on handlers. +func validateAndWrapHandlers(handlers []Handler, wrappers ...func(Handler) Handler) []Handler { + var wrapper func(Handler) Handler + if len(wrappers) > 0 { + wrapper = wrappers[0] + } + + wrappedHandlers := make([]Handler, len(handlers)) + for i, h := range handlers { + h = validateAndWrapHandler(h) + if wrapper != nil && !inject.IsFastInvoker(h) { + h = wrapper(h) + } + wrappedHandlers[i] = h + } + + return wrappedHandlers +} + +// Macaron represents the top level web application. +// inject.Injector methods can be invoked to map services on a global level. +type Macaron struct { + inject.Injector + befores []BeforeHandler + handlers []Handler + action Handler + + hasURLPrefix bool + urlPrefix string // For suburl support. + *Router + + logger *log.Logger +} + +// NewWithLogger creates a bare bones Macaron instance. +// Use this method if you want to have full control over the middleware that is used. +// You can specify logger output writer with this function. +func NewWithLogger(out io.Writer) *Macaron { + m := &Macaron{ + Injector: inject.New(), + action: func() {}, + Router: NewRouter(), + logger: log.New(out, "[Macaron] ", 0), + } + m.Router.m = m + m.Map(m.logger) + m.Map(defaultReturnHandler()) + m.NotFound(http.NotFound) + m.InternalServerError(func(rw http.ResponseWriter, err error) { + http.Error(rw, err.Error(), 500) + }) + return m +} + +// New creates a bare bones Macaron instance. +// Use this method if you want to have full control over the middleware that is used. +func New() *Macaron { + return NewWithLogger(os.Stdout) +} + +// Classic creates a classic Macaron with some basic default middleware: +// macaron.Logger, macaron.Recovery and macaron.Static. +func Classic() *Macaron { + m := New() + m.Use(Logger()) + m.Use(Recovery()) + m.Use(Static("public")) + return m +} + +// Handlers sets the entire middleware stack with the given Handlers. +// This will clear any current middleware handlers, +// and panics if any of the handlers is not a callable function +func (m *Macaron) Handlers(handlers ...Handler) { + m.handlers = make([]Handler, 0) + for _, handler := range handlers { + m.Use(handler) + } +} + +// Action sets the handler that will be called after all the middleware has been invoked. +// This is set to macaron.Router in a macaron.Classic(). +func (m *Macaron) Action(handler Handler) { + handler = validateAndWrapHandler(handler) + m.action = handler +} + +// BeforeHandler represents a handler executes at beginning of every request. +// Macaron stops future process when it returns true. +type BeforeHandler func(rw http.ResponseWriter, req *http.Request) bool + +func (m *Macaron) Before(handler BeforeHandler) { + m.befores = append(m.befores, handler) +} + +// Use adds a middleware Handler to the stack, +// and panics if the handler is not a callable func. +// Middleware Handlers are invoked in the order that they are added. +func (m *Macaron) Use(handler Handler) { + handler = validateAndWrapHandler(handler) + m.handlers = append(m.handlers, handler) +} + +func (m *Macaron) createContext(rw http.ResponseWriter, req *http.Request) *Context { + c := &Context{ + Injector: inject.New(), + handlers: m.handlers, + action: m.action, + index: 0, + Router: m.Router, + Req: Request{req}, + Resp: NewResponseWriter(req.Method, rw), + Render: &DummyRender{rw}, + Data: make(map[string]interface{}), + } + c.SetParent(m) + c.Map(c) + c.MapTo(c.Resp, (*http.ResponseWriter)(nil)) + c.Map(req) + return c +} + +// ServeHTTP is the HTTP Entry point for a Macaron instance. +// Useful if you want to control your own HTTP server. +// Be aware that none of middleware will run without registering any router. +func (m *Macaron) ServeHTTP(rw http.ResponseWriter, req *http.Request) { + if m.hasURLPrefix { + req.URL.Path = strings.TrimPrefix(req.URL.Path, m.urlPrefix) + } + for _, h := range m.befores { + if h(rw, req) { + return + } + } + m.Router.ServeHTTP(rw, req) +} + +func GetDefaultListenInfo() (string, int) { + host := os.Getenv("HOST") + if len(host) == 0 { + host = "0.0.0.0" + } + port := com.StrTo(os.Getenv("PORT")).MustInt() + if port == 0 { + port = 4000 + } + return host, port +} + +// Run the http server. Listening on os.GetEnv("PORT") or 4000 by default. +func (m *Macaron) Run(args ...interface{}) { + host, port := GetDefaultListenInfo() + if len(args) == 1 { + switch arg := args[0].(type) { + case string: + host = arg + case int: + port = arg + } + } else if len(args) >= 2 { + if arg, ok := args[0].(string); ok { + host = arg + } + if arg, ok := args[1].(int); ok { + port = arg + } + } + + addr := host + ":" + com.ToStr(port) + logger := m.GetVal(reflect.TypeOf(m.logger)).Interface().(*log.Logger) + logger.Printf("listening on %s (%s)\n", addr, safeEnv()) + logger.Fatalln(http.ListenAndServe(addr, m)) +} + +// SetURLPrefix sets URL prefix of router layer, so that it support suburl. +func (m *Macaron) SetURLPrefix(prefix string) { + m.urlPrefix = prefix + m.hasURLPrefix = len(m.urlPrefix) > 0 +} + +// ____ ____ .__ ___. .__ +// \ \ / /____ _______|__|____ \_ |__ | | ____ ______ +// \ Y /\__ \\_ __ \ \__ \ | __ \| | _/ __ \ / ___/ +// \ / / __ \| | \/ |/ __ \| \_\ \ |_\ ___/ \___ \ +// \___/ (____ /__| |__(____ /___ /____/\___ >____ > +// \/ \/ \/ \/ \/ + +const ( + DEV = "development" + PROD = "production" + TEST = "test" +) + +var ( + // Env is the environment that Macaron is executing in. + // The MACARON_ENV is read on initialization to set this variable. + Env = DEV + envLock sync.Mutex + + // Path of work directory. + Root string + + // Flash applies to current request. + FlashNow bool + + // Configuration convention object. + cfg *ini.File +) + +func setENV(e string) { + envLock.Lock() + defer envLock.Unlock() + + if len(e) > 0 { + Env = e + } +} + +func safeEnv() string { + envLock.Lock() + defer envLock.Unlock() + + return Env +} + +func init() { + setENV(os.Getenv("MACARON_ENV")) + + var err error + Root, err = os.Getwd() + if err != nil { + panic("error getting work directory: " + err.Error()) + } +} + +// SetConfig sets data sources for configuration. +func SetConfig(source interface{}, others ...interface{}) (_ *ini.File, err error) { + cfg, err = ini.Load(source, others...) + return Config(), 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.Empty() + } + return cfg +} diff --git a/vendor/gopkg.in/macaron.v1/macaronlogo.png b/vendor/gopkg.in/macaron.v1/macaronlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..399759769a8d7f5549d550baccdd2ea5716e2bac GIT binary patch literal 88924 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

QJx8u(c;{O@hHqa*uT;M%@1 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 literal 0 HcmV?d00001 diff --git a/vendor/gopkg.in/macaron.v1/recovery.go b/vendor/gopkg.in/macaron.v1/recovery.go new file mode 100644 index 000000000..c45c54c94 --- /dev/null +++ b/vendor/gopkg.in/macaron.v1/recovery.go @@ -0,0 +1,163 @@ +// Copyright 2013 Martini Authors +// Copyright 2014 The Macaron Authors +// +// 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 +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package macaron + +import ( + "bytes" + "fmt" + "io/ioutil" + "log" + "net/http" + "runtime" + + "github.com/go-macaron/inject" +) + +const ( + panicHtml = ` +PANIC: %s + + + +

PANIC

+
%s
+
%s
+ +` +) + +var ( + dunno = []byte("???") + centerDot = []byte("·") + dot = []byte(".") + slash = []byte("/") +) + +// stack returns a nicely formated stack frame, skipping skip frames +func stack(skip int) []byte { + buf := new(bytes.Buffer) // the returned data + // As we loop, we open files and read them. These variables record the currently + // loaded file. + var lines [][]byte + var lastFile string + for i := skip; ; i++ { // Skip the expected number of frames + pc, file, line, ok := runtime.Caller(i) + if !ok { + break + } + // Print this much at least. If we can't find the source, it won't show. + fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc) + if file != lastFile { + data, err := ioutil.ReadFile(file) + if err != nil { + continue + } + lines = bytes.Split(data, []byte{'\n'}) + lastFile = file + } + fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line)) + } + return buf.Bytes() +} + +// source returns a space-trimmed slice of the n'th line. +func source(lines [][]byte, n int) []byte { + n-- // in stack trace, lines are 1-indexed but our array is 0-indexed + if n < 0 || n >= len(lines) { + return dunno + } + return bytes.TrimSpace(lines[n]) +} + +// function returns, if possible, the name of the function containing the PC. +func function(pc uintptr) []byte { + fn := runtime.FuncForPC(pc) + if fn == nil { + return dunno + } + name := []byte(fn.Name()) + // The name includes the path name to the package, which is unnecessary + // since the file name is already included. Plus, it has center dots. + // That is, we see + // runtime/debug.*T·ptrmethod + // and want + // *T.ptrmethod + // Also the package path might contains dot (e.g. code.google.com/...), + // so first eliminate the path prefix + if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 { + name = name[lastslash+1:] + } + if period := bytes.Index(name, dot); period >= 0 { + name = name[period+1:] + } + name = bytes.Replace(name, centerDot, dot, -1) + return name +} + +// Recovery returns a middleware that recovers from any panics and writes a 500 if there was one. +// While Martini is in development mode, Recovery will also output the panic as HTML. +func Recovery() Handler { + return func(c *Context, log *log.Logger) { + defer func() { + if err := recover(); err != nil { + stack := stack(3) + log.Printf("PANIC: %s\n%s", err, stack) + + // Lookup the current responsewriter + val := c.GetVal(inject.InterfaceOf((*http.ResponseWriter)(nil))) + res := val.Interface().(http.ResponseWriter) + + // respond with panic message while in development mode + var body []byte + if Env == DEV { + res.Header().Set("Content-Type", "text/html") + body = []byte(fmt.Sprintf(panicHtml, err, err, stack)) + } + + res.WriteHeader(http.StatusInternalServerError) + if nil != body { + _, _ = res.Write(body) + } + } + }() + + c.Next() + } +} diff --git a/vendor/gopkg.in/macaron.v1/render.go b/vendor/gopkg.in/macaron.v1/render.go new file mode 100644 index 000000000..04687c4f4 --- /dev/null +++ b/vendor/gopkg.in/macaron.v1/render.go @@ -0,0 +1,724 @@ +// Copyright 2013 Martini Authors +// Copyright 2014 The Macaron Authors +// +// 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 +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package macaron + +import ( + "bytes" + "encoding/json" + "encoding/xml" + "fmt" + "html/template" + "io" + "io/ioutil" + "net/http" + "os" + "path" + "path/filepath" + "strings" + "sync" + "time" + + "github.com/unknwon/com" +) + +const ( + _CONTENT_TYPE = "Content-Type" + _CONTENT_BINARY = "application/octet-stream" + _CONTENT_JSON = "application/json" + _CONTENT_HTML = "text/html" + _CONTENT_PLAIN = "text/plain" + _CONTENT_XHTML = "application/xhtml+xml" + _CONTENT_XML = "text/xml" + _DEFAULT_CHARSET = "UTF-8" +) + +var ( + // Provides a temporary buffer to execute templates into and catch errors. + bufpool = sync.Pool{ + New: func() interface{} { return new(bytes.Buffer) }, + } + + // Included helper functions for use when rendering html + helperFuncs = template.FuncMap{ + "yield": func() (string, error) { + return "", fmt.Errorf("yield called with no layout defined") + }, + "current": func() (string, error) { + return "", nil + }, + } +) + +type ( + // TemplateFile represents a interface of template file that has name and can be read. + TemplateFile interface { + Name() string + Data() []byte + Ext() string + } + // TemplateFileSystem represents a interface of template file system that able to list all files. + TemplateFileSystem interface { + ListFiles() []TemplateFile + Get(string) (io.Reader, error) + } + + // Delims represents a set of Left and Right delimiters for HTML template rendering + Delims struct { + // Left delimiter, defaults to {{ + Left string + // Right delimiter, defaults to }} + Right string + } + + // RenderOptions represents a struct for specifying configuration options for the Render middleware. + RenderOptions struct { + // Directory to load templates. Default is "templates". + Directory string + // Addtional directories to overwite templates. + AppendDirectories []string + // Layout template name. Will not render a layout if "". Default is to "". + Layout string + // Extensions to parse template files from. Defaults are [".tmpl", ".html"]. + Extensions []string + // Funcs is a slice of FuncMaps to apply to the template upon compilation. This is useful for helper functions. Default is []. + Funcs []template.FuncMap + // Delims sets the action delimiters to the specified strings in the Delims struct. + Delims Delims + // Appends the given charset to the Content-Type header. Default is "UTF-8". + Charset string + // Outputs human readable JSON. + IndentJSON bool + // Outputs human readable XML. + IndentXML bool + // Prefixes the JSON output with the given bytes. + PrefixJSON []byte + // Prefixes the XML output with the given bytes. + PrefixXML []byte + // Allows changing of output to XHTML instead of HTML. Default is "text/html" + HTMLContentType string + // TemplateFileSystem is the interface for supporting any implmentation of template file system. + TemplateFileSystem + } + + // HTMLOptions is a struct for overriding some rendering Options for specific HTML call + HTMLOptions struct { + // Layout template name. Overrides Options.Layout. + Layout string + } + + Render interface { + http.ResponseWriter + SetResponseWriter(http.ResponseWriter) + + JSON(int, interface{}) + JSONString(interface{}) (string, error) + RawData(int, []byte) // Serve content as binary + PlainText(int, []byte) // Serve content as plain text + HTML(int, string, interface{}, ...HTMLOptions) + HTMLSet(int, string, string, interface{}, ...HTMLOptions) + HTMLSetString(string, string, interface{}, ...HTMLOptions) (string, error) + HTMLString(string, interface{}, ...HTMLOptions) (string, error) + HTMLSetBytes(string, string, interface{}, ...HTMLOptions) ([]byte, error) + HTMLBytes(string, interface{}, ...HTMLOptions) ([]byte, error) + XML(int, interface{}) + Error(int, ...string) + Status(int) + SetTemplatePath(string, string) + HasTemplateSet(string) bool + } +) + +// TplFile implements TemplateFile interface. +type TplFile struct { + name string + data []byte + ext string +} + +// NewTplFile cerates new template file with given name and data. +func NewTplFile(name string, data []byte, ext string) *TplFile { + return &TplFile{name, data, ext} +} + +func (f *TplFile) Name() string { + return f.name +} + +func (f *TplFile) Data() []byte { + return f.data +} + +func (f *TplFile) Ext() string { + return f.ext +} + +// TplFileSystem implements TemplateFileSystem interface. +type TplFileSystem struct { + files []TemplateFile +} + +// NewTemplateFileSystem creates new template file system with given options. +func NewTemplateFileSystem(opt RenderOptions, omitData bool) TplFileSystem { + fs := TplFileSystem{} + fs.files = make([]TemplateFile, 0, 10) + + // Directories are composed in reverse order because later one overwrites previous ones, + // so once found, we can directly jump out of the loop. + dirs := make([]string, 0, len(opt.AppendDirectories)+1) + for i := len(opt.AppendDirectories) - 1; i >= 0; i-- { + dirs = append(dirs, opt.AppendDirectories[i]) + } + dirs = append(dirs, opt.Directory) + + var err error + for i := range dirs { + // Skip ones that does not exists for symlink test, + // but allow non-symlink ones added after start. + if !com.IsExist(dirs[i]) { + continue + } + + dirs[i], err = filepath.EvalSymlinks(dirs[i]) + if err != nil { + panic("EvalSymlinks(" + dirs[i] + "): " + err.Error()) + } + } + lastDir := dirs[len(dirs)-1] + + // We still walk the last (original) directory because it's non-sense we load templates not exist in original directory. + if err = filepath.Walk(lastDir, func(path string, info os.FileInfo, _ error) error { + r, err := filepath.Rel(lastDir, path) + if err != nil { + return err + } + + ext := GetExt(r) + + for _, extension := range opt.Extensions { + if ext != extension { + continue + } + + var data []byte + if !omitData { + // Loop over candidates of directory, break out once found. + // The file always exists because it's inside the walk function, + // and read original file is the worst case. + for i := range dirs { + path = filepath.Join(dirs[i], r) + if !com.IsFile(path) { + continue + } + + data, err = ioutil.ReadFile(path) + if err != nil { + return err + } + break + } + } + + name := filepath.ToSlash((r[0 : len(r)-len(ext)])) + fs.files = append(fs.files, NewTplFile(name, data, ext)) + } + + return nil + }); err != nil { + panic("NewTemplateFileSystem: " + err.Error()) + } + + return fs +} + +func (fs TplFileSystem) ListFiles() []TemplateFile { + return fs.files +} + +func (fs TplFileSystem) Get(name string) (io.Reader, error) { + for i := range fs.files { + if fs.files[i].Name()+fs.files[i].Ext() == name { + return bytes.NewReader(fs.files[i].Data()), nil + } + } + return nil, fmt.Errorf("file '%s' not found", name) +} + +func PrepareCharset(charset string) string { + if len(charset) != 0 { + return "; charset=" + charset + } + + return "; charset=" + _DEFAULT_CHARSET +} + +func GetExt(s string) string { + index := strings.Index(s, ".") + if index == -1 { + return "" + } + return s[index:] +} + +func compile(opt RenderOptions) *template.Template { + t := template.New(opt.Directory) + t.Delims(opt.Delims.Left, opt.Delims.Right) + // Parse an initial template in case we don't have any. + template.Must(t.Parse("Macaron")) + + if opt.TemplateFileSystem == nil { + opt.TemplateFileSystem = NewTemplateFileSystem(opt, false) + } + + for _, f := range opt.TemplateFileSystem.ListFiles() { + tmpl := t.New(f.Name()) + for _, funcs := range opt.Funcs { + tmpl.Funcs(funcs) + } + // Bomb out if parse fails. We don't want any silent server starts. + template.Must(tmpl.Funcs(helperFuncs).Parse(string(f.Data()))) + } + + return t +} + +const ( + DEFAULT_TPL_SET_NAME = "DEFAULT" +) + +// TemplateSet represents a template set of type *template.Template. +type TemplateSet struct { + lock sync.RWMutex + sets map[string]*template.Template + dirs map[string]string +} + +// NewTemplateSet initializes a new empty template set. +func NewTemplateSet() *TemplateSet { + return &TemplateSet{ + sets: make(map[string]*template.Template), + dirs: make(map[string]string), + } +} + +func (ts *TemplateSet) Set(name string, opt *RenderOptions) *template.Template { + t := compile(*opt) + + ts.lock.Lock() + defer ts.lock.Unlock() + + ts.sets[name] = t + ts.dirs[name] = opt.Directory + return t +} + +func (ts *TemplateSet) Get(name string) *template.Template { + ts.lock.RLock() + defer ts.lock.RUnlock() + + return ts.sets[name] +} + +func (ts *TemplateSet) GetDir(name string) string { + ts.lock.RLock() + defer ts.lock.RUnlock() + + return ts.dirs[name] +} + +func prepareRenderOptions(options []RenderOptions) RenderOptions { + var opt RenderOptions + if len(options) > 0 { + opt = options[0] + } + + // Defaults. + if len(opt.Directory) == 0 { + opt.Directory = "templates" + } + if len(opt.Extensions) == 0 { + opt.Extensions = []string{".tmpl", ".html"} + } + if len(opt.HTMLContentType) == 0 { + opt.HTMLContentType = _CONTENT_HTML + } + + return opt +} + +func ParseTplSet(tplSet string) (tplName string, tplDir string) { + tplSet = strings.TrimSpace(tplSet) + if len(tplSet) == 0 { + panic("empty template set argument") + } + infos := strings.Split(tplSet, ":") + if len(infos) == 1 { + tplDir = infos[0] + tplName = path.Base(tplDir) + } else { + tplName = infos[0] + tplDir = infos[1] + } + + if !com.IsDir(tplDir) { + panic("template set path does not exist or is not a directory") + } + return tplName, tplDir +} + +func renderHandler(opt RenderOptions, tplSets []string) Handler { + cs := PrepareCharset(opt.Charset) + ts := NewTemplateSet() + ts.Set(DEFAULT_TPL_SET_NAME, &opt) + + var tmpOpt RenderOptions + for _, tplSet := range tplSets { + tplName, tplDir := ParseTplSet(tplSet) + tmpOpt = opt + tmpOpt.Directory = tplDir + ts.Set(tplName, &tmpOpt) + } + + return func(ctx *Context) { + r := &TplRender{ + ResponseWriter: ctx.Resp, + TemplateSet: ts, + Opt: &opt, + CompiledCharset: cs, + } + ctx.Data["TmplLoadTimes"] = func() string { + if r.startTime.IsZero() { + return "" + } + return fmt.Sprint(time.Since(r.startTime).Nanoseconds()/1e6) + "ms" + } + + ctx.Render = r + ctx.MapTo(r, (*Render)(nil)) + } +} + +// Renderer is a Middleware that maps a macaron.Render service into the Macaron handler chain. +// An single variadic macaron.RenderOptions struct can be optionally provided to configure +// HTML rendering. The default directory for templates is "templates" and the default +// file extension is ".tmpl" and ".html". +// +// If MACARON_ENV is set to "" or "development" then templates will be recompiled on every request. For more performance, set the +// MACARON_ENV environment variable to "production". +func Renderer(options ...RenderOptions) Handler { + return renderHandler(prepareRenderOptions(options), []string{}) +} + +func Renderers(options RenderOptions, tplSets ...string) Handler { + return renderHandler(prepareRenderOptions([]RenderOptions{options}), tplSets) +} + +type TplRender struct { + http.ResponseWriter + *TemplateSet + Opt *RenderOptions + CompiledCharset string + + startTime time.Time +} + +func (r *TplRender) SetResponseWriter(rw http.ResponseWriter) { + r.ResponseWriter = rw +} + +func (r *TplRender) JSON(status int, v interface{}) { + var ( + result []byte + err error + ) + if r.Opt.IndentJSON { + result, err = json.MarshalIndent(v, "", " ") + } else { + result, err = json.Marshal(v) + } + if err != nil { + http.Error(r, err.Error(), 500) + return + } + + // json rendered fine, write out the result + r.Header().Set(_CONTENT_TYPE, _CONTENT_JSON+r.CompiledCharset) + r.WriteHeader(status) + if len(r.Opt.PrefixJSON) > 0 { + _, _ = r.Write(r.Opt.PrefixJSON) + } + _, _ = r.Write(result) +} + +func (r *TplRender) JSONString(v interface{}) (string, error) { + var result []byte + var err error + if r.Opt.IndentJSON { + result, err = json.MarshalIndent(v, "", " ") + } else { + result, err = json.Marshal(v) + } + if err != nil { + return "", err + } + return string(result), nil +} + +func (r *TplRender) XML(status int, v interface{}) { + var result []byte + var err error + if r.Opt.IndentXML { + result, err = xml.MarshalIndent(v, "", " ") + } else { + result, err = xml.Marshal(v) + } + if err != nil { + http.Error(r, err.Error(), 500) + return + } + + // XML rendered fine, write out the result + r.Header().Set(_CONTENT_TYPE, _CONTENT_XML+r.CompiledCharset) + r.WriteHeader(status) + if len(r.Opt.PrefixXML) > 0 { + _, _ = r.Write(r.Opt.PrefixXML) + } + _, _ = r.Write(result) +} + +func (r *TplRender) data(status int, contentType string, v []byte) { + if r.Header().Get(_CONTENT_TYPE) == "" { + r.Header().Set(_CONTENT_TYPE, contentType) + } + r.WriteHeader(status) + _, _ = r.Write(v) +} + +func (r *TplRender) RawData(status int, v []byte) { + r.data(status, _CONTENT_BINARY, v) +} + +func (r *TplRender) PlainText(status int, v []byte) { + r.data(status, _CONTENT_PLAIN, v) +} + +func (r *TplRender) execute(t *template.Template, name string, data interface{}) (*bytes.Buffer, error) { + buf := bufpool.Get().(*bytes.Buffer) + return buf, t.ExecuteTemplate(buf, name, data) +} + +func (r *TplRender) addYield(t *template.Template, tplName string, data interface{}) { + funcs := template.FuncMap{ + "yield": func() (template.HTML, error) { + buf, err := r.execute(t, tplName, data) + // return safe html here since we are rendering our own template + return template.HTML(buf.String()), err + }, + "current": func() (string, error) { + return tplName, nil + }, + } + t.Funcs(funcs) +} + +func (r *TplRender) renderBytes(setName, tplName string, data interface{}, htmlOpt ...HTMLOptions) (*bytes.Buffer, error) { + t := r.TemplateSet.Get(setName) + if Env == DEV { + opt := *r.Opt + opt.Directory = r.TemplateSet.GetDir(setName) + t = r.TemplateSet.Set(setName, &opt) + } + if t == nil { + return nil, fmt.Errorf("html/template: template \"%s\" is undefined", tplName) + } + + opt := r.prepareHTMLOptions(htmlOpt) + + if len(opt.Layout) > 0 { + r.addYield(t, tplName, data) + tplName = opt.Layout + } + + out, err := r.execute(t, tplName, data) + if err != nil { + return nil, err + } + + return out, nil +} + +func (r *TplRender) renderHTML(status int, setName, tplName string, data interface{}, htmlOpt ...HTMLOptions) { + r.startTime = time.Now() + + out, err := r.renderBytes(setName, tplName, data, htmlOpt...) + if err != nil { + http.Error(r, err.Error(), http.StatusInternalServerError) + return + } + + r.Header().Set(_CONTENT_TYPE, r.Opt.HTMLContentType+r.CompiledCharset) + r.WriteHeader(status) + + if _, err := out.WriteTo(r); err != nil { + out.Reset() + } + bufpool.Put(out) +} + +func (r *TplRender) HTML(status int, name string, data interface{}, htmlOpt ...HTMLOptions) { + r.renderHTML(status, DEFAULT_TPL_SET_NAME, name, data, htmlOpt...) +} + +func (r *TplRender) HTMLSet(status int, setName, tplName string, data interface{}, htmlOpt ...HTMLOptions) { + r.renderHTML(status, setName, tplName, data, htmlOpt...) +} + +func (r *TplRender) HTMLSetBytes(setName, tplName string, data interface{}, htmlOpt ...HTMLOptions) ([]byte, error) { + out, err := r.renderBytes(setName, tplName, data, htmlOpt...) + if err != nil { + return []byte(""), err + } + return out.Bytes(), nil +} + +func (r *TplRender) HTMLBytes(name string, data interface{}, htmlOpt ...HTMLOptions) ([]byte, error) { + return r.HTMLSetBytes(DEFAULT_TPL_SET_NAME, name, data, htmlOpt...) +} + +func (r *TplRender) HTMLSetString(setName, tplName string, data interface{}, htmlOpt ...HTMLOptions) (string, error) { + p, err := r.HTMLSetBytes(setName, tplName, data, htmlOpt...) + return string(p), err +} + +func (r *TplRender) HTMLString(name string, data interface{}, htmlOpt ...HTMLOptions) (string, error) { + p, err := r.HTMLBytes(name, data, htmlOpt...) + return string(p), err +} + +// Error writes the given HTTP status to the current ResponseWriter +func (r *TplRender) Error(status int, message ...string) { + r.WriteHeader(status) + if len(message) > 0 { + _, _ = r.Write([]byte(message[0])) + } +} + +func (r *TplRender) Status(status int) { + r.WriteHeader(status) +} + +func (r *TplRender) prepareHTMLOptions(htmlOpt []HTMLOptions) HTMLOptions { + if len(htmlOpt) > 0 { + return htmlOpt[0] + } + + return HTMLOptions{ + Layout: r.Opt.Layout, + } +} + +func (r *TplRender) SetTemplatePath(setName, dir string) { + if len(setName) == 0 { + setName = DEFAULT_TPL_SET_NAME + } + opt := *r.Opt + opt.Directory = dir + r.TemplateSet.Set(setName, &opt) +} + +func (r *TplRender) HasTemplateSet(name string) bool { + return r.TemplateSet.Get(name) != nil +} + +// DummyRender is used when user does not choose any real render to use. +// This way, we can print out friendly message which asks them to register one, +// instead of ugly and confusing 'nil pointer' panic. +type DummyRender struct { + http.ResponseWriter +} + +func renderNotRegistered() { + panic("middleware render hasn't been registered") +} + +func (r *DummyRender) SetResponseWriter(http.ResponseWriter) { + renderNotRegistered() +} + +func (r *DummyRender) JSON(int, interface{}) { + renderNotRegistered() +} + +func (r *DummyRender) JSONString(interface{}) (string, error) { + renderNotRegistered() + return "", nil +} + +func (r *DummyRender) RawData(int, []byte) { + renderNotRegistered() +} + +func (r *DummyRender) PlainText(int, []byte) { + renderNotRegistered() +} + +func (r *DummyRender) HTML(int, string, interface{}, ...HTMLOptions) { + renderNotRegistered() +} + +func (r *DummyRender) HTMLSet(int, string, string, interface{}, ...HTMLOptions) { + renderNotRegistered() +} + +func (r *DummyRender) HTMLSetString(string, string, interface{}, ...HTMLOptions) (string, error) { + renderNotRegistered() + return "", nil +} + +func (r *DummyRender) HTMLString(string, interface{}, ...HTMLOptions) (string, error) { + renderNotRegistered() + return "", nil +} + +func (r *DummyRender) HTMLSetBytes(string, string, interface{}, ...HTMLOptions) ([]byte, error) { + renderNotRegistered() + return nil, nil +} + +func (r *DummyRender) HTMLBytes(string, interface{}, ...HTMLOptions) ([]byte, error) { + renderNotRegistered() + return nil, nil +} + +func (r *DummyRender) XML(int, interface{}) { + renderNotRegistered() +} + +func (r *DummyRender) Error(int, ...string) { + renderNotRegistered() +} + +func (r *DummyRender) Status(int) { + renderNotRegistered() +} + +func (r *DummyRender) SetTemplatePath(string, string) { + renderNotRegistered() +} + +func (r *DummyRender) HasTemplateSet(string) bool { + renderNotRegistered() + return false +} diff --git a/vendor/gopkg.in/macaron.v1/response_writer.go b/vendor/gopkg.in/macaron.v1/response_writer.go new file mode 100644 index 000000000..eeb35f642 --- /dev/null +++ b/vendor/gopkg.in/macaron.v1/response_writer.go @@ -0,0 +1,124 @@ +// Copyright 2013 Martini Authors +// +// 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 +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package macaron + +import ( + "bufio" + "errors" + "net" + "net/http" +) + +// ResponseWriter is a wrapper around http.ResponseWriter that provides extra information about +// the response. It is recommended that middleware handlers use this construct to wrap a responsewriter +// if the functionality calls for it. +type ResponseWriter interface { + http.ResponseWriter + http.Flusher + http.Pusher + // Status returns the status code of the response or 0 if the response has not been written. + Status() int + // Written returns whether or not the ResponseWriter has been written. + Written() bool + // Size returns the size of the response body. + Size() int + // Before allows for a function to be called before the ResponseWriter has been written to. This is + // useful for setting headers or any other operations that must happen before a response has been written. + Before(BeforeFunc) +} + +// BeforeFunc is a function that is called before the ResponseWriter has been written to. +type BeforeFunc func(ResponseWriter) + +// NewResponseWriter creates a ResponseWriter that wraps an http.ResponseWriter +func NewResponseWriter(method string, rw http.ResponseWriter) ResponseWriter { + return &responseWriter{method, rw, 0, 0, nil} +} + +type responseWriter struct { + method string + http.ResponseWriter + status int + size int + beforeFuncs []BeforeFunc +} + +func (rw *responseWriter) WriteHeader(s int) { + rw.callBefore() + rw.ResponseWriter.WriteHeader(s) + rw.status = s +} + +func (rw *responseWriter) Write(b []byte) (size int, err error) { + if !rw.Written() { + // The status will be StatusOK if WriteHeader has not been called yet + rw.WriteHeader(http.StatusOK) + } + if rw.method != "HEAD" { + size, err = rw.ResponseWriter.Write(b) + rw.size += size + } + return size, err +} + +func (rw *responseWriter) Status() int { + return rw.status +} + +func (rw *responseWriter) Size() int { + return rw.size +} + +func (rw *responseWriter) Written() bool { + return rw.status != 0 +} + +func (rw *responseWriter) Before(before BeforeFunc) { + rw.beforeFuncs = append(rw.beforeFuncs, before) +} + +func (rw *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { + hijacker, ok := rw.ResponseWriter.(http.Hijacker) + if !ok { + return nil, nil, errors.New("the ResponseWriter doesn't support the Hijacker interface") + } + return hijacker.Hijack() +} + +//nolint +func (rw *responseWriter) CloseNotify() <-chan bool { + return rw.ResponseWriter.(http.CloseNotifier).CloseNotify() +} + +func (rw *responseWriter) callBefore() { + for i := len(rw.beforeFuncs) - 1; i >= 0; i-- { + rw.beforeFuncs[i](rw) + } +} + +func (rw *responseWriter) Flush() { + flusher, ok := rw.ResponseWriter.(http.Flusher) + if ok { + flusher.Flush() + } +} + +func (rw *responseWriter) Push(target string, opts *http.PushOptions) error { + pusher, ok := rw.ResponseWriter.(http.Pusher) + if !ok { + return errors.New("the ResponseWriter doesn't support the Pusher interface") + } + return pusher.Push(target, opts) +} diff --git a/vendor/gopkg.in/macaron.v1/return_handler.go b/vendor/gopkg.in/macaron.v1/return_handler.go new file mode 100644 index 000000000..33b7fd2be --- /dev/null +++ b/vendor/gopkg.in/macaron.v1/return_handler.go @@ -0,0 +1,76 @@ +// Copyright 2013 Martini Authors +// Copyright 2014 The Macaron Authors +// +// 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 +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package macaron + +import ( + "net/http" + "reflect" + + "github.com/go-macaron/inject" +) + +// ReturnHandler is a service that Martini provides that is called +// when a route handler returns something. The ReturnHandler is +// responsible for writing to the ResponseWriter based on the values +// that are passed into this function. +type ReturnHandler func(*Context, []reflect.Value) + +func canDeref(val reflect.Value) bool { + return val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr +} + +func isError(val reflect.Value) bool { + _, ok := val.Interface().(error) + return ok +} + +func isByteSlice(val reflect.Value) bool { + return val.Kind() == reflect.Slice && val.Type().Elem().Kind() == reflect.Uint8 +} + +func defaultReturnHandler() ReturnHandler { + return func(ctx *Context, vals []reflect.Value) { + rv := ctx.GetVal(inject.InterfaceOf((*http.ResponseWriter)(nil))) + resp := rv.Interface().(http.ResponseWriter) + var respVal reflect.Value + if len(vals) > 1 && vals[0].Kind() == reflect.Int { + resp.WriteHeader(int(vals[0].Int())) + respVal = vals[1] + } else if len(vals) > 0 { + respVal = vals[0] + + if isError(respVal) { + err := respVal.Interface().(error) + if err != nil { + ctx.internalServerError(ctx, err) + } + return + } else if canDeref(respVal) { + if respVal.IsNil() { + return // Ignore nil error + } + } + } + if canDeref(respVal) { + respVal = respVal.Elem() + } + if isByteSlice(respVal) { + _, _ = resp.Write(respVal.Bytes()) + } else { + _, _ = resp.Write([]byte(respVal.String())) + } + } +} diff --git a/vendor/gopkg.in/macaron.v1/router.go b/vendor/gopkg.in/macaron.v1/router.go new file mode 100644 index 000000000..df593d669 --- /dev/null +++ b/vendor/gopkg.in/macaron.v1/router.go @@ -0,0 +1,380 @@ +// Copyright 2014 The Macaron Authors +// +// 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 +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package macaron + +import ( + "net/http" + "strings" + "sync" +) + +var ( + // Known HTTP methods. + _HTTP_METHODS = map[string]bool{ + "GET": true, + "POST": true, + "PUT": true, + "DELETE": true, + "PATCH": true, + "OPTIONS": true, + "HEAD": true, + } +) + +// routeMap represents a thread-safe map for route tree. +type routeMap struct { + lock sync.RWMutex + routes map[string]map[string]*Leaf +} + +// NewRouteMap initializes and returns a new routeMap. +func NewRouteMap() *routeMap { + rm := &routeMap{ + routes: make(map[string]map[string]*Leaf), + } + for m := range _HTTP_METHODS { + rm.routes[m] = make(map[string]*Leaf) + } + return rm +} + +// getLeaf returns Leaf object if a route has been registered. +func (rm *routeMap) getLeaf(method, pattern string) *Leaf { + rm.lock.RLock() + defer rm.lock.RUnlock() + + return rm.routes[method][pattern] +} + +// add adds new route to route tree map. +func (rm *routeMap) add(method, pattern string, leaf *Leaf) { + rm.lock.Lock() + defer rm.lock.Unlock() + + rm.routes[method][pattern] = leaf +} + +type group struct { + pattern string + handlers []Handler +} + +// Router represents a Macaron router layer. +type Router struct { + m *Macaron + autoHead bool + routers map[string]*Tree + *routeMap + namedRoutes map[string]*Leaf + + groups []group + notFound http.HandlerFunc + internalServerError func(*Context, error) + + // handlerWrapper is used to wrap arbitrary function from Handler to inject.FastInvoker. + handlerWrapper func(Handler) Handler +} + +func NewRouter() *Router { + return &Router{ + routers: make(map[string]*Tree), + routeMap: NewRouteMap(), + namedRoutes: make(map[string]*Leaf), + } +} + +// SetAutoHead sets the value who determines whether add HEAD method automatically +// when GET method is added. +func (r *Router) SetAutoHead(v bool) { + r.autoHead = v +} + +type Params map[string]string + +// Handle is a function that can be registered to a route to handle HTTP requests. +// Like http.HandlerFunc, but has a third parameter for the values of wildcards (variables). +type Handle func(http.ResponseWriter, *http.Request, Params) + +// Route represents a wrapper of leaf route and upper level router. +type Route struct { + router *Router + leaf *Leaf +} + +// Name sets name of route. +func (r *Route) Name(name string) { + if len(name) == 0 { + panic("route name cannot be empty") + } else if r.router.namedRoutes[name] != nil { + panic("route with given name already exists: " + name) + } + r.router.namedRoutes[name] = r.leaf +} + +// handle adds new route to the router tree. +func (r *Router) handle(method, pattern string, handle Handle) *Route { + method = strings.ToUpper(method) + + var leaf *Leaf + // Prevent duplicate routes. + if leaf = r.getLeaf(method, pattern); leaf != nil { + return &Route{r, leaf} + } + + // Validate HTTP methods. + if !_HTTP_METHODS[method] && method != "*" { + panic("unknown HTTP method: " + method) + } + + // Generate methods need register. + methods := make(map[string]bool) + if method == "*" { + for m := range _HTTP_METHODS { + methods[m] = true + } + } else { + methods[method] = true + } + + // Add to router tree. + for m := range methods { + if t, ok := r.routers[m]; ok { + leaf = t.Add(pattern, handle) + } else { + t := NewTree() + leaf = t.Add(pattern, handle) + r.routers[m] = t + } + r.add(m, pattern, leaf) + } + return &Route{r, leaf} +} + +// Handle registers a new request handle with the given pattern, method and handlers. +func (r *Router) Handle(method string, pattern string, handlers []Handler) *Route { + if len(r.groups) > 0 { + groupPattern := "" + h := make([]Handler, 0) + for _, g := range r.groups { + groupPattern += g.pattern + h = append(h, g.handlers...) + } + + pattern = groupPattern + pattern + h = append(h, handlers...) + handlers = h + } + handlers = validateAndWrapHandlers(handlers, r.handlerWrapper) + + return r.handle(method, pattern, func(resp http.ResponseWriter, req *http.Request, params Params) { + c := r.m.createContext(resp, req) + c.params = params + c.handlers = make([]Handler, 0, len(r.m.handlers)+len(handlers)) + c.handlers = append(c.handlers, r.m.handlers...) + c.handlers = append(c.handlers, handlers...) + c.run() + }) +} + +func (r *Router) Group(pattern string, fn func(), h ...Handler) { + r.groups = append(r.groups, group{pattern, h}) + fn() + r.groups = r.groups[:len(r.groups)-1] +} + +// Get is a shortcut for r.Handle("GET", pattern, handlers) +func (r *Router) Get(pattern string, h ...Handler) (leaf *Route) { + leaf = r.Handle("GET", pattern, h) + if r.autoHead { + r.Head(pattern, h...) + } + return leaf +} + +// Patch is a shortcut for r.Handle("PATCH", pattern, handlers) +func (r *Router) Patch(pattern string, h ...Handler) *Route { + return r.Handle("PATCH", pattern, h) +} + +// Post is a shortcut for r.Handle("POST", pattern, handlers) +func (r *Router) Post(pattern string, h ...Handler) *Route { + return r.Handle("POST", pattern, h) +} + +// Put is a shortcut for r.Handle("PUT", pattern, handlers) +func (r *Router) Put(pattern string, h ...Handler) *Route { + return r.Handle("PUT", pattern, h) +} + +// Delete is a shortcut for r.Handle("DELETE", pattern, handlers) +func (r *Router) Delete(pattern string, h ...Handler) *Route { + return r.Handle("DELETE", pattern, h) +} + +// Options is a shortcut for r.Handle("OPTIONS", pattern, handlers) +func (r *Router) Options(pattern string, h ...Handler) *Route { + return r.Handle("OPTIONS", pattern, h) +} + +// Head is a shortcut for r.Handle("HEAD", pattern, handlers) +func (r *Router) Head(pattern string, h ...Handler) *Route { + return r.Handle("HEAD", pattern, h) +} + +// Any is a shortcut for r.Handle("*", pattern, handlers) +func (r *Router) Any(pattern string, h ...Handler) *Route { + return r.Handle("*", pattern, h) +} + +// Route is a shortcut for same handlers but different HTTP methods. +// +// Example: +// m.Route("/", "GET,POST", h) +func (r *Router) Route(pattern, methods string, h ...Handler) (route *Route) { + for _, m := range strings.Split(methods, ",") { + route = r.Handle(strings.TrimSpace(m), pattern, h) + } + return route +} + +// Combo returns a combo router. +func (r *Router) Combo(pattern string, h ...Handler) *ComboRouter { + return &ComboRouter{r, pattern, h, map[string]bool{}, nil} +} + +// NotFound configurates http.HandlerFunc which is called when no matching route is +// found. If it is not set, http.NotFound is used. +// Be sure to set 404 response code in your handler. +func (r *Router) NotFound(handlers ...Handler) { + handlers = validateAndWrapHandlers(handlers) + r.notFound = func(rw http.ResponseWriter, req *http.Request) { + c := r.m.createContext(rw, req) + c.handlers = make([]Handler, 0, len(r.m.handlers)+len(handlers)) + c.handlers = append(c.handlers, r.m.handlers...) + c.handlers = append(c.handlers, handlers...) + c.run() + } +} + +// InternalServerError configurates handler which is called when route handler returns +// error. If it is not set, default handler is used. +// Be sure to set 500 response code in your handler. +func (r *Router) InternalServerError(handlers ...Handler) { + handlers = validateAndWrapHandlers(handlers) + r.internalServerError = func(c *Context, err error) { + c.index = 0 + c.handlers = handlers + c.Map(err) + c.run() + } +} + +// SetHandlerWrapper sets handlerWrapper for the router. +func (r *Router) SetHandlerWrapper(f func(Handler) Handler) { + r.handlerWrapper = f +} + +func (r *Router) ServeHTTP(rw http.ResponseWriter, req *http.Request) { + if t, ok := r.routers[req.Method]; ok { + // Fast match for static routes + leaf := r.getLeaf(req.Method, req.URL.Path) + if leaf != nil { + leaf.handle(rw, req, nil) + return + } + + h, p, ok := t.Match(req.URL.EscapedPath()) + if ok { + if splat, ok := p["*0"]; ok { + p["*"] = splat // Easy name. + } + h(rw, req, p) + return + } + } + + r.notFound(rw, req) +} + +// URLFor builds path part of URL by given pair values. +func (r *Router) URLFor(name string, pairs ...string) string { + leaf, ok := r.namedRoutes[name] + if !ok { + panic("route with given name does not exists: " + name) + } + return leaf.URLPath(pairs...) +} + +// ComboRouter represents a combo router. +type ComboRouter struct { + router *Router + pattern string + handlers []Handler + methods map[string]bool // Registered methods. + + lastRoute *Route +} + +func (cr *ComboRouter) checkMethod(name string) { + if cr.methods[name] { + panic("method '" + name + "' has already been registered") + } + cr.methods[name] = true +} + +func (cr *ComboRouter) route(fn func(string, ...Handler) *Route, method string, h ...Handler) *ComboRouter { + cr.checkMethod(method) + cr.lastRoute = fn(cr.pattern, append(cr.handlers, h...)...) + return cr +} + +func (cr *ComboRouter) Get(h ...Handler) *ComboRouter { + if cr.router.autoHead { + cr.Head(h...) + } + return cr.route(cr.router.Get, "GET", h...) +} + +func (cr *ComboRouter) Patch(h ...Handler) *ComboRouter { + return cr.route(cr.router.Patch, "PATCH", h...) +} + +func (cr *ComboRouter) Post(h ...Handler) *ComboRouter { + return cr.route(cr.router.Post, "POST", h...) +} + +func (cr *ComboRouter) Put(h ...Handler) *ComboRouter { + return cr.route(cr.router.Put, "PUT", h...) +} + +func (cr *ComboRouter) Delete(h ...Handler) *ComboRouter { + return cr.route(cr.router.Delete, "DELETE", h...) +} + +func (cr *ComboRouter) Options(h ...Handler) *ComboRouter { + return cr.route(cr.router.Options, "OPTIONS", h...) +} + +func (cr *ComboRouter) Head(h ...Handler) *ComboRouter { + return cr.route(cr.router.Head, "HEAD", h...) +} + +// Name sets name of ComboRouter route. +func (cr *ComboRouter) Name(name string) { + if cr.lastRoute == nil { + panic("no corresponding route to be named") + } + cr.lastRoute.Name(name) +} diff --git a/vendor/gopkg.in/macaron.v1/static.go b/vendor/gopkg.in/macaron.v1/static.go new file mode 100644 index 000000000..f1c0c75af --- /dev/null +++ b/vendor/gopkg.in/macaron.v1/static.go @@ -0,0 +1,230 @@ +// Copyright 2013 Martini Authors +// Copyright 2014 The Macaron Authors +// +// 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 +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package macaron + +import ( + "encoding/base64" + "log" + "net/http" + "path" + "path/filepath" + "strings" + "sync" +) + +// StaticOptions is a struct for specifying configuration options for the macaron.Static middleware. +type StaticOptions struct { + // Prefix is the optional prefix used to serve the static directory content + Prefix string + // SkipLogging will disable [Static] log messages when a static file is served. + SkipLogging bool + // IndexFile defines which file to serve as index if it exists. + IndexFile string + // Expires defines which user-defined function to use for producing a HTTP Expires Header + // https://developers.google.com/speed/docs/insights/LeverageBrowserCaching + Expires func() string + // ETag defines if we should add an ETag header + // https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching#validating-cached-responses-with-etags + ETag bool + // FileSystem is the interface for supporting any implmentation of file system. + FileSystem http.FileSystem +} + +// FIXME: to be deleted. +type staticMap struct { + lock sync.RWMutex + data map[string]*http.Dir +} + +func (sm *staticMap) Set(dir *http.Dir) { + sm.lock.Lock() + defer sm.lock.Unlock() + + sm.data[string(*dir)] = dir +} + +func (sm *staticMap) Get(name string) *http.Dir { + sm.lock.RLock() + defer sm.lock.RUnlock() + + return sm.data[name] +} + +func (sm *staticMap) Delete(name string) { + sm.lock.Lock() + defer sm.lock.Unlock() + + delete(sm.data, name) +} + +var statics = staticMap{sync.RWMutex{}, map[string]*http.Dir{}} + +// staticFileSystem implements http.FileSystem interface. +type staticFileSystem struct { + dir *http.Dir +} + +func newStaticFileSystem(directory string) staticFileSystem { + if !filepath.IsAbs(directory) { + directory = filepath.Join(Root, directory) + } + dir := http.Dir(directory) + statics.Set(&dir) + return staticFileSystem{&dir} +} + +func (fs staticFileSystem) Open(name string) (http.File, error) { + return fs.dir.Open(name) +} + +func prepareStaticOption(dir string, opt StaticOptions) StaticOptions { + // Defaults + if len(opt.IndexFile) == 0 { + opt.IndexFile = "index.html" + } + // Normalize the prefix if provided + if opt.Prefix != "" { + // Ensure we have a leading '/' + if opt.Prefix[0] != '/' { + opt.Prefix = "/" + opt.Prefix + } + // Remove any trailing '/' + opt.Prefix = strings.TrimRight(opt.Prefix, "/") + } + if opt.FileSystem == nil { + opt.FileSystem = newStaticFileSystem(dir) + } + return opt +} + +func prepareStaticOptions(dir string, options []StaticOptions) StaticOptions { + var opt StaticOptions + if len(options) > 0 { + opt = options[0] + } + return prepareStaticOption(dir, opt) +} + +func staticHandler(ctx *Context, log *log.Logger, opt StaticOptions) bool { + if ctx.Req.Method != "GET" && ctx.Req.Method != "HEAD" { + return false + } + + file := ctx.Req.URL.Path + // if we have a prefix, filter requests by stripping the prefix + if opt.Prefix != "" { + if !strings.HasPrefix(file, opt.Prefix) { + return false + } + file = file[len(opt.Prefix):] + if file != "" && file[0] != '/' { + return false + } + } + + f, err := opt.FileSystem.Open(file) + if err != nil { + return false + } + defer f.Close() + + fi, err := f.Stat() + if err != nil { + return true // File exists but fail to open. + } + + // Try to serve index file + if fi.IsDir() { + redirPath := path.Clean(ctx.Req.URL.Path) + // path.Clean removes the trailing slash, so we need to add it back when + // the original path has it. + if strings.HasSuffix(ctx.Req.URL.Path, "/") { + redirPath = redirPath + "/" + } + // Redirect if missing trailing slash. + if !strings.HasSuffix(redirPath, "/") { + http.Redirect(ctx.Resp, ctx.Req.Request, redirPath+"/", http.StatusFound) + return true + } + + file = path.Join(file, opt.IndexFile) + f, err = opt.FileSystem.Open(file) + if err != nil { + return false // Discard error. + } + defer f.Close() + + fi, err = f.Stat() + if err != nil || fi.IsDir() { + return true + } + } + + if !opt.SkipLogging { + log.Println("[Static] Serving " + file) + } + + // Add an Expires header to the static content + if opt.Expires != nil { + ctx.Resp.Header().Set("Expires", opt.Expires()) + } + + if opt.ETag { + tag := `"` + GenerateETag(string(fi.Size()), fi.Name(), fi.ModTime().UTC().Format(http.TimeFormat)) + `"` + ctx.Resp.Header().Set("ETag", tag) + if ctx.Req.Header.Get("If-None-Match") == tag { + ctx.Resp.WriteHeader(http.StatusNotModified) + return true + } + } + + http.ServeContent(ctx.Resp, ctx.Req.Request, file, fi.ModTime(), f) + return true +} + +// GenerateETag generates an ETag based on size, filename and file modification time +func GenerateETag(fileSize, fileName, modTime string) string { + etag := fileSize + fileName + modTime + return base64.StdEncoding.EncodeToString([]byte(etag)) +} + +// Static returns a middleware handler that serves static files in the given directory. +func Static(directory string, staticOpt ...StaticOptions) Handler { + opt := prepareStaticOptions(directory, staticOpt) + + return func(ctx *Context, log *log.Logger) { + staticHandler(ctx, log, opt) + } +} + +// 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") + } + opts := make([]StaticOptions, len(dirs)) + for i := range dirs { + opts[i] = prepareStaticOption(dirs[i], opt) + } + + return func(ctx *Context, log *log.Logger) { + for i := range opts { + if staticHandler(ctx, log, opts[i]) { + return + } + } + } +} diff --git a/vendor/gopkg.in/macaron.v1/tree.go b/vendor/gopkg.in/macaron.v1/tree.go new file mode 100644 index 000000000..0ab094dd6 --- /dev/null +++ b/vendor/gopkg.in/macaron.v1/tree.go @@ -0,0 +1,390 @@ +// Copyright 2015 The Macaron Authors +// +// 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 +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package macaron + +import ( + "regexp" + "strings" + + "github.com/unknwon/com" +) + +type patternType int8 + +const ( + _PATTERN_STATIC patternType = iota // /home + _PATTERN_REGEXP // /:id([0-9]+) + _PATTERN_PATH_EXT // /*.* + _PATTERN_HOLDER // /:user + _PATTERN_MATCH_ALL // /* +) + +// Leaf represents a leaf route information. +type Leaf struct { + parent *Tree + + typ patternType + pattern string + rawPattern string // Contains wildcard instead of regexp + wildcards []string + reg *regexp.Regexp + optional bool + + handle Handle +} + +var wildcardPattern = regexp.MustCompile(`:[a-zA-Z0-9]+`) + +func isSpecialRegexp(pattern, regStr string, pos []int) bool { + return len(pattern) >= pos[1]+len(regStr) && pattern[pos[1]:pos[1]+len(regStr)] == regStr +} + +// getNextWildcard tries to find next wildcard and update pattern with corresponding regexp. +func getNextWildcard(pattern string) (wildcard, _ string) { + pos := wildcardPattern.FindStringIndex(pattern) + if pos == nil { + return "", pattern + } + wildcard = pattern[pos[0]:pos[1]] + + // Reach last character or no regexp is given. + if len(pattern) == pos[1] { + return wildcard, strings.Replace(pattern, wildcard, `(.+)`, 1) + } else if pattern[pos[1]] != '(' { + switch { + case isSpecialRegexp(pattern, ":int", pos): + pattern = strings.Replace(pattern, ":int", "([0-9]+)", 1) + case isSpecialRegexp(pattern, ":string", pos): + pattern = strings.Replace(pattern, ":string", "([\\w]+)", 1) + default: + return wildcard, strings.Replace(pattern, wildcard, `(.+)`, 1) + } + } + + // Cut out placeholder directly. + return wildcard, pattern[:pos[0]] + pattern[pos[1]:] +} + +func getWildcards(pattern string) (string, []string) { + wildcards := make([]string, 0, 2) + + // Keep getting next wildcard until nothing is left. + var wildcard string + for { + wildcard, pattern = getNextWildcard(pattern) + if len(wildcard) > 0 { + wildcards = append(wildcards, wildcard) + } else { + break + } + } + + return pattern, wildcards +} + +// getRawPattern removes all regexp but keeps wildcards for building URL path. +func getRawPattern(rawPattern string) string { + rawPattern = strings.Replace(rawPattern, ":int", "", -1) + rawPattern = strings.Replace(rawPattern, ":string", "", -1) + + for { + startIdx := strings.Index(rawPattern, "(") + if startIdx == -1 { + break + } + + closeIdx := strings.Index(rawPattern, ")") + if closeIdx > -1 { + rawPattern = rawPattern[:startIdx] + rawPattern[closeIdx+1:] + } + } + return rawPattern +} + +func checkPattern(pattern string) (typ patternType, rawPattern string, wildcards []string, reg *regexp.Regexp) { + pattern = strings.TrimLeft(pattern, "?") + rawPattern = getRawPattern(pattern) + + if pattern == "*" { + typ = _PATTERN_MATCH_ALL + } else if pattern == "*.*" { + typ = _PATTERN_PATH_EXT + } else if strings.Contains(pattern, ":") { + typ = _PATTERN_REGEXP + pattern, wildcards = getWildcards(pattern) + if pattern == "(.+)" { + typ = _PATTERN_HOLDER + } else { + reg = regexp.MustCompile(pattern) + } + } + return typ, rawPattern, wildcards, reg +} + +func NewLeaf(parent *Tree, pattern string, handle Handle) *Leaf { + typ, rawPattern, wildcards, reg := checkPattern(pattern) + optional := false + if len(pattern) > 0 && pattern[0] == '?' { + optional = true + } + return &Leaf{parent, typ, pattern, rawPattern, wildcards, reg, optional, handle} +} + +// URLPath build path part of URL by given pair values. +func (l *Leaf) URLPath(pairs ...string) string { + if len(pairs)%2 != 0 { + panic("number of pairs does not match") + } + + urlPath := l.rawPattern + parent := l.parent + for parent != nil { + urlPath = parent.rawPattern + "/" + urlPath + parent = parent.parent + } + for i := 0; i < len(pairs); i += 2 { + if len(pairs[i]) == 0 { + panic("pair value cannot be empty: " + com.ToStr(i)) + } else if pairs[i][0] != ':' && pairs[i] != "*" && pairs[i] != "*.*" { + pairs[i] = ":" + pairs[i] + } + urlPath = strings.Replace(urlPath, pairs[i], pairs[i+1], 1) + } + return urlPath +} + +// Tree represents a router tree in Macaron. +type Tree struct { + parent *Tree + + typ patternType + pattern string + rawPattern string + wildcards []string + reg *regexp.Regexp + + subtrees []*Tree + leaves []*Leaf +} + +func NewSubtree(parent *Tree, pattern string) *Tree { + typ, rawPattern, wildcards, reg := checkPattern(pattern) + return &Tree{parent, typ, pattern, rawPattern, wildcards, reg, make([]*Tree, 0, 5), make([]*Leaf, 0, 5)} +} + +func NewTree() *Tree { + return NewSubtree(nil, "") +} + +func (t *Tree) addLeaf(pattern string, handle Handle) *Leaf { + for i := 0; i < len(t.leaves); i++ { + if t.leaves[i].pattern == pattern { + return t.leaves[i] + } + } + + leaf := NewLeaf(t, pattern, handle) + + // Add exact same leaf to grandparent/parent level without optional. + if leaf.optional { + parent := leaf.parent + if parent.parent != nil { + parent.parent.addLeaf(parent.pattern, handle) + } else { + parent.addLeaf("", handle) // Root tree can add as empty pattern. + } + } + + i := 0 + for ; i < len(t.leaves); i++ { + if leaf.typ < t.leaves[i].typ { + break + } + } + + if i == len(t.leaves) { + t.leaves = append(t.leaves, leaf) + } else { + t.leaves = append(t.leaves[:i], append([]*Leaf{leaf}, t.leaves[i:]...)...) + } + return leaf +} + +func (t *Tree) addSubtree(segment, pattern string, handle Handle) *Leaf { + for i := 0; i < len(t.subtrees); i++ { + if t.subtrees[i].pattern == segment { + return t.subtrees[i].addNextSegment(pattern, handle) + } + } + + subtree := NewSubtree(t, segment) + i := 0 + for ; i < len(t.subtrees); i++ { + if subtree.typ < t.subtrees[i].typ { + break + } + } + + if i == len(t.subtrees) { + t.subtrees = append(t.subtrees, subtree) + } else { + t.subtrees = append(t.subtrees[:i], append([]*Tree{subtree}, t.subtrees[i:]...)...) + } + return subtree.addNextSegment(pattern, handle) +} + +func (t *Tree) addNextSegment(pattern string, handle Handle) *Leaf { + pattern = strings.TrimPrefix(pattern, "/") + + i := strings.Index(pattern, "/") + if i == -1 { + return t.addLeaf(pattern, handle) + } + return t.addSubtree(pattern[:i], pattern[i+1:], handle) +} + +func (t *Tree) Add(pattern string, handle Handle) *Leaf { + pattern = strings.TrimSuffix(pattern, "/") + return t.addNextSegment(pattern, handle) +} + +func (t *Tree) matchLeaf(globLevel int, url string, params Params) (Handle, bool) { + url, err := PathUnescape(url) + if err != nil { + return nil, false + } + for i := 0; i < len(t.leaves); i++ { + switch t.leaves[i].typ { + case _PATTERN_STATIC: + if t.leaves[i].pattern == url { + return t.leaves[i].handle, true + } + case _PATTERN_REGEXP: + results := t.leaves[i].reg.FindStringSubmatch(url) + // Number of results and wildcasrd should be exact same. + if len(results)-1 != len(t.leaves[i].wildcards) { + break + } + + for j := 0; j < len(t.leaves[i].wildcards); j++ { + params[t.leaves[i].wildcards[j]] = results[j+1] + } + return t.leaves[i].handle, true + case _PATTERN_PATH_EXT: + j := strings.LastIndex(url, ".") + if j > -1 { + params[":path"] = url[:j] + params[":ext"] = url[j+1:] + } else { + params[":path"] = url + } + return t.leaves[i].handle, true + case _PATTERN_HOLDER: + params[t.leaves[i].wildcards[0]] = url + return t.leaves[i].handle, true + case _PATTERN_MATCH_ALL: + params["*"] = url + params["*"+com.ToStr(globLevel)] = url + return t.leaves[i].handle, true + } + } + return nil, false +} + +func (t *Tree) matchSubtree(globLevel int, segment, url string, params Params) (Handle, bool) { + unescapedSegment, err := PathUnescape(segment) + if err != nil { + return nil, false + } + for i := 0; i < len(t.subtrees); i++ { + switch t.subtrees[i].typ { + case _PATTERN_STATIC: + if t.subtrees[i].pattern == unescapedSegment { + if handle, ok := t.subtrees[i].matchNextSegment(globLevel, url, params); ok { + return handle, true + } + } + case _PATTERN_REGEXP: + results := t.subtrees[i].reg.FindStringSubmatch(unescapedSegment) + if len(results)-1 != len(t.subtrees[i].wildcards) { + break + } + + for j := 0; j < len(t.subtrees[i].wildcards); j++ { + params[t.subtrees[i].wildcards[j]] = results[j+1] + } + if handle, ok := t.subtrees[i].matchNextSegment(globLevel, url, params); ok { + return handle, true + } + case _PATTERN_HOLDER: + if handle, ok := t.subtrees[i].matchNextSegment(globLevel+1, url, params); ok { + params[t.subtrees[i].wildcards[0]] = unescapedSegment + return handle, true + } + case _PATTERN_MATCH_ALL: + if handle, ok := t.subtrees[i].matchNextSegment(globLevel+1, url, params); ok { + params["*"+com.ToStr(globLevel)] = unescapedSegment + return handle, true + } + } + } + + if len(t.leaves) > 0 { + leaf := t.leaves[len(t.leaves)-1] + unescapedURL, err := PathUnescape(segment + "/" + url) + if err != nil { + return nil, false + } + if leaf.typ == _PATTERN_PATH_EXT { + j := strings.LastIndex(unescapedURL, ".") + if j > -1 { + params[":path"] = unescapedURL[:j] + params[":ext"] = unescapedURL[j+1:] + } else { + params[":path"] = unescapedURL + } + return leaf.handle, true + } else if leaf.typ == _PATTERN_MATCH_ALL { + params["*"] = unescapedURL + params["*"+com.ToStr(globLevel)] = unescapedURL + return leaf.handle, true + } + } + return nil, false +} + +func (t *Tree) matchNextSegment(globLevel int, url string, params Params) (Handle, bool) { + i := strings.Index(url, "/") + if i == -1 { + return t.matchLeaf(globLevel, url, params) + } + return t.matchSubtree(globLevel, url[:i], url[i+1:], params) +} + +func (t *Tree) Match(url string) (Handle, Params, bool) { + url = strings.TrimPrefix(url, "/") + url = strings.TrimSuffix(url, "/") + params := make(Params) + handle, ok := t.matchNextSegment(0, url, params) + return handle, params, ok +} + +// MatchTest returns true if given URL is matched by given pattern. +func MatchTest(pattern, url string) bool { + t := NewTree() + t.Add(pattern, nil) + _, _, ok := t.Match(url) + return ok +} diff --git a/vendor/gopkg.in/macaron.v1/util_go17.go b/vendor/gopkg.in/macaron.v1/util_go17.go new file mode 100644 index 000000000..a80c696c7 --- /dev/null +++ b/vendor/gopkg.in/macaron.v1/util_go17.go @@ -0,0 +1,25 @@ +// +build !go1.8 + +// Copyright 2017 The Macaron Authors +// +// 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 +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package macaron + +import "net/url" + +// PathUnescape unescapes a path. Ideally, this function would use +// url.PathUnescape(..), but the function was not introduced until go1.8. +func PathUnescape(s string) (string, error) { + return url.QueryUnescape(s) +} diff --git a/vendor/gopkg.in/macaron.v1/util_go18.go b/vendor/gopkg.in/macaron.v1/util_go18.go new file mode 100644 index 000000000..d5eb1dfb2 --- /dev/null +++ b/vendor/gopkg.in/macaron.v1/util_go18.go @@ -0,0 +1,24 @@ +// +build go1.8 + +// Copyright 2017 The Macaron Authors +// +// 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 +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package macaron + +import "net/url" + +// PathUnescape unescapes a path. +func PathUnescape(s string) (string, error) { + return url.PathUnescape(s) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 8f60c2382..b0360e8ed 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,5 +1,4 @@ # cloud.google.com/go v0.45.0 -## explicit cloud.google.com/go/compute/metadata cloud.google.com/go/iam cloud.google.com/go/internal/optional @@ -8,43 +7,31 @@ cloud.google.com/go/pubsub cloud.google.com/go/pubsub/apiv1 cloud.google.com/go/pubsub/internal/distribution # gitea.com/jolheiser/gitea-vet v0.1.0 -## explicit gitea.com/jolheiser/gitea-vet gitea.com/jolheiser/gitea-vet/checks # gitea.com/lunny/levelqueue v0.3.0 -## explicit gitea.com/lunny/levelqueue # gitea.com/macaron/binding v0.0.0-20190822013154-a5f53841ed2b -## explicit gitea.com/macaron/binding # gitea.com/macaron/cache v0.0.0-20190822004001-a6e7fee4ee76 -## explicit gitea.com/macaron/cache gitea.com/macaron/cache/memcache gitea.com/macaron/cache/redis # gitea.com/macaron/captcha v0.0.0-20190822015246-daa973478bae -## explicit gitea.com/macaron/captcha # gitea.com/macaron/cors v0.0.0-20190826180238-95aec09ea8b4 -## explicit gitea.com/macaron/cors # gitea.com/macaron/csrf v0.0.0-20190822024205-3dc5a4474439 -## explicit gitea.com/macaron/csrf # gitea.com/macaron/gzip v0.0.0-20191118041502-506895b47aae -## explicit gitea.com/macaron/gzip # gitea.com/macaron/i18n v0.0.0-20190822004228-474e714e2223 -## explicit gitea.com/macaron/i18n # gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a -## explicit gitea.com/macaron/inject # gitea.com/macaron/macaron v1.4.0 -## explicit gitea.com/macaron/macaron # gitea.com/macaron/session v0.0.0-20191207215012-613cebf0674d -## explicit gitea.com/macaron/session gitea.com/macaron/session/couchbase gitea.com/macaron/session/memcache @@ -53,13 +40,10 @@ gitea.com/macaron/session/nodb gitea.com/macaron/session/postgres gitea.com/macaron/session/redis # gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7 -## explicit gitea.com/macaron/toolbox # github.com/BurntSushi/toml v0.3.1 -## explicit github.com/BurntSushi/toml # github.com/PuerkitoBio/goquery v1.5.0 -## explicit github.com/PuerkitoBio/goquery # github.com/PuerkitoBio/purell v1.1.1 github.com/PuerkitoBio/purell @@ -68,7 +52,6 @@ github.com/PuerkitoBio/urlesc # github.com/RichardKnop/logging v0.0.0-20181101035820-b1d5d44c82d6 github.com/RichardKnop/logging # github.com/RichardKnop/machinery v1.6.9 -## explicit github.com/RichardKnop/machinery/v1 github.com/RichardKnop/machinery/v1/backends/amqp github.com/RichardKnop/machinery/v1/backends/dynamodb @@ -95,7 +78,6 @@ github.com/RichardKnop/machinery/v1/tracing # github.com/RichardKnop/redsync v1.2.0 github.com/RichardKnop/redsync # github.com/RoaringBitmap/roaring v0.4.23 -## explicit github.com/RoaringBitmap/roaring # github.com/andybalholm/cascadia v1.0.0 github.com/andybalholm/cascadia @@ -147,10 +129,7 @@ github.com/aws/aws-sdk-go/service/sts/stsiface github.com/aymerick/douceur/css # github.com/beorn7/perks v1.0.1 github.com/beorn7/perks/quantile -# github.com/bgentry/speakeasy v0.1.0 -## explicit # github.com/blevesearch/bleve v1.0.7 -## explicit github.com/blevesearch/bleve github.com/blevesearch/bleve/analysis github.com/blevesearch/bleve/analysis/analyzer/custom @@ -212,7 +191,6 @@ github.com/bradfitz/gomemcache/memcache # github.com/chris-ramon/douceur v0.2.0 github.com/chris-ramon/douceur/parser # github.com/couchbase/gomemcached v0.0.0-20191004160342-7b5da2ec40b2 -## explicit github.com/couchbase/gomemcached github.com/couchbase/gomemcached/client # github.com/couchbase/goutils v0.0.0-20191018232750-b49639060d85 @@ -227,31 +205,20 @@ github.com/couchbase/vellum/utf8 github.com/couchbaselabs/go-couchbase # github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d github.com/cpuguy83/go-md2man/v2/md2man -# github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d -## explicit -# github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 -## explicit -# github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 -## explicit # github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew/spew # github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc -## explicit github.com/denisenkom/go-mssqldb github.com/denisenkom/go-mssqldb/internal/cp github.com/denisenkom/go-mssqldb/internal/decimal github.com/denisenkom/go-mssqldb/internal/querytext # github.com/dgrijalva/jwt-go v3.2.0+incompatible -## explicit github.com/dgrijalva/jwt-go # github.com/dustin/go-humanize v1.0.0 -## explicit github.com/dustin/go-humanize # github.com/editorconfig/editorconfig-core-go/v2 v2.1.1 -## explicit github.com/editorconfig/editorconfig-core-go/v2 # github.com/emirpasic/gods v1.12.0 -## explicit github.com/emirpasic/gods/containers github.com/emirpasic/gods/lists github.com/emirpasic/gods/lists/arraylist @@ -259,14 +226,7 @@ github.com/emirpasic/gods/trees github.com/emirpasic/gods/trees/binaryheap github.com/emirpasic/gods/utils # github.com/ethantkoenig/rupture v0.0.0-20180203182544-0a76f03a811a -## explicit github.com/ethantkoenig/rupture -# github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 -## explicit -# github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 -## explicit -# github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 -## explicit # github.com/fatih/color v1.9.0 github.com/fatih/color # github.com/fatih/structtag v1.2.0 @@ -274,13 +234,10 @@ github.com/fatih/structtag # github.com/fsnotify/fsnotify v1.4.7 github.com/fsnotify/fsnotify # github.com/gliderlabs/ssh v0.2.2 -## explicit github.com/gliderlabs/ssh # github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a -## explicit github.com/glycerine/go-unsnap-stream # github.com/go-enry/go-enry/v2 v2.3.0 -## explicit github.com/go-enry/go-enry/v2 github.com/go-enry/go-enry/v2/data github.com/go-enry/go-enry/v2/data/rule @@ -295,14 +252,12 @@ github.com/go-git/gcfg/scanner github.com/go-git/gcfg/token github.com/go-git/gcfg/types # github.com/go-git/go-billy/v5 v5.0.0 -## explicit github.com/go-git/go-billy/v5 github.com/go-git/go-billy/v5/helper/chroot github.com/go-git/go-billy/v5/helper/polyfill github.com/go-git/go-billy/v5/osfs github.com/go-git/go-billy/v5/util # github.com/go-git/go-git/v5 v5.0.0 -## explicit github.com/go-git/go-git/v5 github.com/go-git/go-git/v5/config github.com/go-git/go-git/v5/internal/revision @@ -347,8 +302,11 @@ github.com/go-git/go-git/v5/utils/merkletrie/index github.com/go-git/go-git/v5/utils/merkletrie/internal/frame github.com/go-git/go-git/v5/utils/merkletrie/noder # github.com/go-ini/ini v1.56.0 -## explicit github.com/go-ini/ini +# github.com/go-macaron/auth v0.0.0-20161228062157-884c0e6c9b92 +github.com/go-macaron/auth +# github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191 +github.com/go-macaron/inject # github.com/go-openapi/analysis v0.19.5 github.com/go-openapi/analysis github.com/go-openapi/analysis/internal @@ -359,7 +317,6 @@ github.com/go-openapi/inflect # github.com/go-openapi/jsonpointer v0.19.3 github.com/go-openapi/jsonpointer # github.com/go-openapi/jsonreference v0.19.3 -## explicit github.com/go-openapi/jsonreference # github.com/go-openapi/loads v0.19.3 github.com/go-openapi/loads @@ -381,7 +338,6 @@ github.com/go-openapi/swag # github.com/go-openapi/validate v0.19.3 github.com/go-openapi/validate # github.com/go-redis/redis v6.15.2+incompatible -## explicit github.com/go-redis/redis github.com/go-redis/redis/internal github.com/go-redis/redis/internal/consistenthash @@ -390,15 +346,12 @@ github.com/go-redis/redis/internal/pool github.com/go-redis/redis/internal/proto github.com/go-redis/redis/internal/util # github.com/go-resty/resty/v2 v2.3.0 -## explicit github.com/go-resty/resty/v2 # github.com/go-sql-driver/mysql v1.4.1 -## explicit github.com/go-sql-driver/mysql # github.com/go-stack/stack v1.8.0 github.com/go-stack/stack # github.com/go-swagger/go-swagger v0.21.0 -## explicit github.com/go-swagger/go-swagger/cmd/swagger github.com/go-swagger/go-swagger/cmd/swagger/commands github.com/go-swagger/go-swagger/cmd/swagger/commands/diff @@ -408,7 +361,6 @@ github.com/go-swagger/go-swagger/codescan github.com/go-swagger/go-swagger/generator github.com/go-swagger/go-swagger/scan # github.com/gobwas/glob v0.2.3 -## explicit github.com/gobwas/glob github.com/gobwas/glob/compiler github.com/gobwas/glob/match @@ -418,17 +370,14 @@ github.com/gobwas/glob/syntax/lexer github.com/gobwas/glob/util/runes github.com/gobwas/glob/util/strings # github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28 -## explicit github.com/gogs/chardet # github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14 -## explicit github.com/gogs/cron # github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe github.com/golang-sql/civil # github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 github.com/golang/groupcache/lru # github.com/golang/protobuf v1.4.1 -## explicit github.com/golang/protobuf/proto github.com/golang/protobuf/protoc-gen-go/descriptor github.com/golang/protobuf/ptypes @@ -442,7 +391,6 @@ github.com/golang/snappy github.com/gomodule/redigo/internal github.com/gomodule/redigo/redis # github.com/google/go-github/v24 v24.0.1 -## explicit github.com/google/go-github/v24/github # github.com/google/go-querystring v1.0.0 github.com/google/go-querystring/query @@ -451,7 +399,6 @@ github.com/google/uuid # github.com/googleapis/gax-go/v2 v2.0.5 github.com/googleapis/gax-go/v2 # github.com/gorilla/context v1.1.1 -## explicit github.com/gorilla/context # github.com/gorilla/css v1.0.0 github.com/gorilla/css/scanner @@ -466,7 +413,6 @@ github.com/gorilla/sessions # github.com/hashicorp/go-cleanhttp v0.5.1 github.com/hashicorp/go-cleanhttp # github.com/hashicorp/go-retryablehttp v0.6.6 -## explicit github.com/hashicorp/go-retryablehttp # github.com/hashicorp/hcl v1.0.0 github.com/hashicorp/hcl @@ -480,15 +426,10 @@ github.com/hashicorp/hcl/json/parser github.com/hashicorp/hcl/json/scanner github.com/hashicorp/hcl/json/token # github.com/huandu/xstrings v1.3.0 -## explicit github.com/huandu/xstrings -# github.com/issue9/assert v1.3.2 -## explicit # github.com/issue9/identicon v1.0.1 -## explicit github.com/issue9/identicon # github.com/jaytaylor/html2text v0.0.0-20160923191438-8fb95d837f7d -## explicit github.com/jaytaylor/html2text # github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 github.com/jbenet/go-context/io @@ -496,21 +437,15 @@ github.com/jbenet/go-context/io github.com/jessevdk/go-flags # github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af github.com/jmespath/go-jmespath -# github.com/jmhodges/levigo v1.0.0 -## explicit -# github.com/joho/godotenv v1.3.0 -## explicit # github.com/json-iterator/go v1.1.9 github.com/json-iterator/go # github.com/kballard/go-shellquote v0.0.0-20170619183022-cd60e84ee657 -## explicit github.com/kballard/go-shellquote # github.com/kelseyhightower/envconfig v1.3.0 github.com/kelseyhightower/envconfig # github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd github.com/kevinburke/ssh_config # github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4 -## explicit github.com/keybase/go-crypto/brainpool github.com/keybase/go-crypto/cast5 github.com/keybase/go-crypto/curve25519 @@ -525,7 +460,6 @@ github.com/keybase/go-crypto/openpgp/packet github.com/keybase/go-crypto/openpgp/s2k github.com/keybase/go-crypto/rsa # github.com/klauspost/compress v1.10.2 -## explicit github.com/klauspost/compress/flate github.com/klauspost/compress/gzip # github.com/klauspost/cpuid v1.2.3 @@ -535,16 +469,13 @@ github.com/kr/pretty # github.com/kr/text v0.2.0 github.com/kr/text # github.com/lafriks/xormstore v1.3.2 -## explicit github.com/lafriks/xormstore github.com/lafriks/xormstore/util # github.com/lib/pq v1.2.0 -## explicit github.com/lib/pq github.com/lib/pq/oid github.com/lib/pq/scram # github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 -## explicit github.com/lunny/dingtalk_webhook # github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de github.com/lunny/log @@ -557,13 +488,11 @@ github.com/lunny/nodb/store/goleveldb # github.com/magiconair/properties v1.8.1 github.com/magiconair/properties # github.com/mailru/easyjson v0.7.0 -## explicit github.com/mailru/easyjson github.com/mailru/easyjson/buffer github.com/mailru/easyjson/jlexer github.com/mailru/easyjson/jwriter # github.com/markbates/goth v1.61.2 -## explicit github.com/markbates/goth github.com/markbates/goth/gothic github.com/markbates/goth/providers/bitbucket @@ -581,35 +510,26 @@ github.com/markbates/goth/providers/yandex # github.com/mattn/go-colorable v0.1.4 github.com/mattn/go-colorable # github.com/mattn/go-isatty v0.0.11 -## explicit github.com/mattn/go-isatty -# github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d -## explicit # github.com/mattn/go-runewidth v0.0.7 github.com/mattn/go-runewidth # github.com/mattn/go-sqlite3 v1.11.0 -## explicit github.com/mattn/go-sqlite3 # github.com/matttproud/golang_protobuf_extensions v1.0.1 github.com/matttproud/golang_protobuf_extensions/pbutil # github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75 -## explicit github.com/mcuadros/go-version # github.com/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81 -## explicit github.com/mgechev/dots # github.com/mgechev/revive v1.0.2 -## explicit github.com/mgechev/revive/formatter github.com/mgechev/revive/lint github.com/mgechev/revive/rule # github.com/microcosm-cc/bluemonday v1.0.3-0.20191119130333-0a75d7616912 -## explicit github.com/microcosm-cc/bluemonday # github.com/minio/md5-simd v1.1.0 github.com/minio/md5-simd # github.com/minio/minio-go v6.0.14+incompatible -## explicit github.com/minio/minio-go github.com/minio/minio-go/pkg/credentials github.com/minio/minio-go/pkg/encrypt @@ -617,7 +537,6 @@ github.com/minio/minio-go/pkg/s3signer github.com/minio/minio-go/pkg/s3utils github.com/minio/minio-go/pkg/set # github.com/minio/minio-go/v6 v6.0.57 -## explicit github.com/minio/minio-go/v6 github.com/minio/minio-go/v6/pkg/credentials github.com/minio/minio-go/v6/pkg/encrypt @@ -628,7 +547,6 @@ github.com/minio/minio-go/v6/pkg/tags # github.com/minio/sha256-simd v0.1.1 github.com/minio/sha256-simd # github.com/mitchellh/go-homedir v1.1.0 -## explicit github.com/mitchellh/go-homedir # github.com/mitchellh/mapstructure v1.1.2 github.com/mitchellh/mapstructure @@ -641,21 +559,16 @@ github.com/mrjones/oauth # github.com/mschoch/smat v0.2.0 github.com/mschoch/smat # github.com/msteinert/pam v0.0.0-20151204160544-02ccfbfaf0cc -## explicit github.com/msteinert/pam # github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5 -## explicit github.com/nfnt/resize # github.com/niklasfasching/go-org v0.1.9 -## explicit github.com/niklasfasching/go-org/org # github.com/olekukonko/tablewriter v0.0.4 github.com/olekukonko/tablewriter # github.com/oliamb/cutter v0.2.2 -## explicit github.com/oliamb/cutter # github.com/olivere/elastic/v7 v7.0.9 -## explicit github.com/olivere/elastic/v7 github.com/olivere/elastic/v7/config github.com/olivere/elastic/v7/uritemplates @@ -668,53 +581,41 @@ github.com/pelletier/go-toml # github.com/philhofer/fwd v1.0.0 github.com/philhofer/fwd # github.com/pkg/errors v0.9.1 -## explicit github.com/pkg/errors # github.com/pmezard/go-difflib v1.0.0 github.com/pmezard/go-difflib/difflib # github.com/pquerna/otp v1.2.0 -## explicit github.com/pquerna/otp github.com/pquerna/otp/hotp github.com/pquerna/otp/totp # github.com/prometheus/client_golang v1.1.0 -## explicit github.com/prometheus/client_golang/prometheus github.com/prometheus/client_golang/prometheus/internal github.com/prometheus/client_golang/prometheus/promhttp # github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 -## explicit github.com/prometheus/client_model/go # github.com/prometheus/common v0.6.0 github.com/prometheus/common/expfmt github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg github.com/prometheus/common/model # github.com/prometheus/procfs v0.0.4 -## explicit github.com/prometheus/procfs github.com/prometheus/procfs/internal/fs github.com/prometheus/procfs/internal/util # github.com/quasoft/websspi v1.0.0 -## explicit github.com/quasoft/websspi github.com/quasoft/websspi/secctx -# github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001 -## explicit # github.com/russross/blackfriday/v2 v2.0.1 github.com/russross/blackfriday/v2 # github.com/satori/go.uuid v1.2.0 -## explicit github.com/satori/go.uuid # github.com/sergi/go-diff v1.1.0 -## explicit github.com/sergi/go-diff/diffmatchpatch # github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b -## explicit github.com/shurcooL/httpfs/vfsutil # github.com/shurcooL/sanitized_anchor_name v1.0.0 github.com/shurcooL/sanitized_anchor_name # github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd -## explicit github.com/shurcooL/vfsgen # github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d github.com/siddontang/go-snappy/snappy @@ -734,7 +635,6 @@ github.com/steveyen/gtreap # github.com/streadway/amqp v0.0.0-20190214183023-884228600bc9 github.com/streadway/amqp # github.com/stretchr/testify v1.4.0 -## explicit github.com/stretchr/testify/assert github.com/stretchr/testify/require # github.com/syndtr/goleveldb v1.0.0 @@ -750,38 +650,28 @@ github.com/syndtr/goleveldb/leveldb/opt github.com/syndtr/goleveldb/leveldb/storage github.com/syndtr/goleveldb/leveldb/table github.com/syndtr/goleveldb/leveldb/util -# github.com/tecbot/gorocksdb v0.0.0-20181010114359-8752a9433481 -## explicit # github.com/tinylib/msgp v1.1.2 -## explicit github.com/tinylib/msgp/msgp # github.com/toqueteos/trie v1.0.0 github.com/toqueteos/trie # github.com/toqueteos/webbrowser v1.2.0 github.com/toqueteos/webbrowser # github.com/tstranex/u2f v1.0.0 -## explicit github.com/tstranex/u2f # github.com/unknwon/cae v1.0.0 -## explicit github.com/unknwon/cae github.com/unknwon/cae/zip # github.com/unknwon/com v1.0.1 -## explicit github.com/unknwon/com # github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6 -## explicit github.com/unknwon/i18n # github.com/unknwon/paginater v0.0.0-20151104151617-7748a72e0141 -## explicit github.com/unknwon/paginater # github.com/urfave/cli v1.22.1 -## explicit github.com/urfave/cli # github.com/willf/bitset v1.1.10 github.com/willf/bitset # github.com/xanzy/go-gitlab v0.31.0 -## explicit github.com/xanzy/go-gitlab # github.com/xanzy/ssh-agent v0.2.1 github.com/xanzy/ssh-agent @@ -790,10 +680,8 @@ github.com/xdg/scram # github.com/xdg/stringprep v1.0.0 github.com/xdg/stringprep # github.com/yohcop/openid-go v1.0.0 -## explicit github.com/yohcop/openid-go # github.com/yuin/goldmark v1.1.27 -## explicit github.com/yuin/goldmark github.com/yuin/goldmark/ast github.com/yuin/goldmark/extension @@ -804,7 +692,6 @@ github.com/yuin/goldmark/renderer/html github.com/yuin/goldmark/text github.com/yuin/goldmark/util # github.com/yuin/goldmark-meta v0.0.0-20191126180153-f0638e958b60 -## explicit github.com/yuin/goldmark-meta # go.etcd.io/bbolt v1.3.4 go.etcd.io/bbolt @@ -856,7 +743,6 @@ go.opencensus.io/trace/internal go.opencensus.io/trace/propagation go.opencensus.io/trace/tracestate # golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 -## explicit golang.org/x/crypto/acme golang.org/x/crypto/acme/autocert golang.org/x/crypto/argon2 @@ -884,11 +770,9 @@ golang.org/x/crypto/ssh/agent golang.org/x/crypto/ssh/internal/bcrypt_pbkdf golang.org/x/crypto/ssh/knownhosts # golang.org/x/mod v0.3.0 -## explicit golang.org/x/mod/module golang.org/x/mod/semver # golang.org/x/net v0.0.0-20200513185701-a91f0712d120 -## explicit golang.org/x/net/context golang.org/x/net/context/ctxhttp golang.org/x/net/html @@ -904,7 +788,6 @@ golang.org/x/net/proxy golang.org/x/net/publicsuffix golang.org/x/net/trace # golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d -## explicit golang.org/x/oauth2 golang.org/x/oauth2/google golang.org/x/oauth2/internal @@ -914,7 +797,6 @@ golang.org/x/oauth2/jwt golang.org/x/sync/errgroup golang.org/x/sync/semaphore # golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f -## explicit golang.org/x/sys/cpu golang.org/x/sys/internal/unsafeheader golang.org/x/sys/unix @@ -922,7 +804,6 @@ golang.org/x/sys/windows golang.org/x/sys/windows/svc golang.org/x/sys/windows/svc/debug # golang.org/x/text v0.3.2 -## explicit golang.org/x/text/encoding golang.org/x/text/encoding/charmap golang.org/x/text/encoding/htmlindex @@ -945,10 +826,8 @@ golang.org/x/text/unicode/bidi golang.org/x/text/unicode/norm golang.org/x/text/width # golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 -## explicit golang.org/x/time/rate # golang.org/x/tools v0.0.0-20200515220128-d3bf790afa53 -## explicit golang.org/x/tools/cover golang.org/x/tools/go/analysis golang.org/x/tools/go/analysis/internal/analysisflags @@ -988,7 +867,6 @@ google.golang.org/api/transport/grpc google.golang.org/api/transport/http google.golang.org/api/transport/http/internal/propagation # google.golang.org/appengine v1.6.5 -## explicit google.golang.org/appengine google.golang.org/appengine/cloudsql google.golang.org/appengine/internal @@ -1087,41 +965,32 @@ google.golang.org/protobuf/types/known/durationpb google.golang.org/protobuf/types/known/emptypb google.golang.org/protobuf/types/known/timestamppb # gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc -## explicit gopkg.in/alexcesaro/quotedprintable.v3 # gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 -## explicit gopkg.in/asn1-ber.v1 # gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df -## explicit gopkg.in/gomail.v2 # gopkg.in/ini.v1 v1.52.0 -## explicit gopkg.in/ini.v1 # gopkg.in/ldap.v3 v3.0.2 -## explicit gopkg.in/ldap.v3 +# gopkg.in/macaron.v1 v1.3.9 +gopkg.in/macaron.v1 # gopkg.in/testfixtures.v2 v2.5.0 -## explicit gopkg.in/testfixtures.v2 # gopkg.in/toqueteos/substring.v1 v1.0.2 gopkg.in/toqueteos/substring.v1 # gopkg.in/warnings.v0 v0.1.2 gopkg.in/warnings.v0 # gopkg.in/yaml.v2 v2.2.8 -## explicit gopkg.in/yaml.v2 # mvdan.cc/xurls/v2 v2.1.0 -## explicit mvdan.cc/xurls/v2 # strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 -## explicit strk.kbt.io/projects/go/libravatar # xorm.io/builder v0.3.7 -## explicit xorm.io/builder # xorm.io/xorm v1.0.1 -## explicit xorm.io/xorm xorm.io/xorm/caches xorm.io/xorm/contexts