diff --git a/cmd/commands/pack/pack.go b/cmd/commands/pack/pack.go
index becddcc..834b063 100644
--- a/cmd/commands/pack/pack.go
+++ b/cmd/commands/pack/pack.go
@@ -18,6 +18,7 @@ import (
"syscall"
"time"
+ _ "github.com/astaxie/beego"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/version"
beeLogger "github.com/beego/bee/logger"
diff --git a/vendor/github.com/astaxie/beego/CONTRIBUTING.md b/vendor/github.com/astaxie/beego/CONTRIBUTING.md
new file mode 100644
index 0000000..9d51161
--- /dev/null
+++ b/vendor/github.com/astaxie/beego/CONTRIBUTING.md
@@ -0,0 +1,52 @@
+# Contributing to beego
+
+beego is an open source project.
+
+It is the work of hundreds of contributors. We appreciate your help!
+
+Here are instructions to get you started. They are probably not perfect,
+please let us know if anything feels wrong or incomplete.
+
+## Contribution guidelines
+
+### Pull requests
+
+First of all. beego follow the gitflow. So please send you pull request
+to **develop** branch. We will close the pull request to master branch.
+
+We are always happy to receive pull requests, and do our best to
+review them as fast as possible. Not sure if that typo is worth a pull
+request? Do it! We will appreciate it.
+
+If your pull request is not accepted on the first try, don't be
+discouraged! Sometimes we can make a mistake, please do more explaining
+for us. We will appreciate it.
+
+We're trying very hard to keep beego simple and fast. We don't want it
+to do everything for everybody. This means that we might decide against
+incorporating a new feature. But we will give you some advice on how to
+do it in other way.
+
+### Create issues
+
+Any significant improvement should be documented as [a GitHub
+issue](https://github.com/astaxie/beego/issues) before anybody
+starts working on it.
+
+Also when filing an issue, make sure to answer these five questions:
+
+- What version of beego are you using (bee version)?
+- What operating system and processor architecture are you using?
+- What did you do?
+- What did you expect to see?
+- What did you see instead?
+
+### but check existing issues and docs first!
+
+Please take a moment to check that an issue doesn't already exist
+documenting your bug report or improvement proposal. If it does, it
+never hurts to add a quick "+1" or "I have this problem too". This will
+help prioritize the most common problems and requests.
+
+Also if you don't know how to use it. please make sure you have read though
+the docs in http://beego.me/docs
\ No newline at end of file
diff --git a/vendor/github.com/astaxie/beego/LICENSE b/vendor/github.com/astaxie/beego/LICENSE
new file mode 100644
index 0000000..5dbd424
--- /dev/null
+++ b/vendor/github.com/astaxie/beego/LICENSE
@@ -0,0 +1,13 @@
+Copyright 2014 astaxie
+
+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.
\ No newline at end of file
diff --git a/vendor/github.com/astaxie/beego/README.md b/vendor/github.com/astaxie/beego/README.md
new file mode 100644
index 0000000..aa5d8e1
--- /dev/null
+++ b/vendor/github.com/astaxie/beego/README.md
@@ -0,0 +1,61 @@
+# Beego [![Build Status](https://travis-ci.org/astaxie/beego.svg?branch=master)](https://travis-ci.org/astaxie/beego) [![GoDoc](http://godoc.org/github.com/astaxie/beego?status.svg)](http://godoc.org/github.com/astaxie/beego) [![Foundation](https://img.shields.io/badge/Golang-Foundation-green.svg)](http://golangfoundation.org) [![Go Report Card](https://goreportcard.com/badge/github.com/astaxie/beego)](https://goreportcard.com/report/github.com/astaxie/beego)
+
+
+beego is used for rapid development of RESTful APIs, web apps and backend services in Go.
+It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific features such as interfaces and struct embedding.
+
+###### More info at [beego.me](http://beego.me).
+
+## Quick Start
+
+#### Download and install
+
+ go get github.com/astaxie/beego
+
+#### Create file `hello.go`
+```go
+package main
+
+import "github.com/astaxie/beego"
+
+func main(){
+ beego.Run()
+}
+```
+#### Build and run
+
+ go build hello.go
+ ./hello
+
+#### Go to [http://localhost:8080](http://localhost:8080)
+
+Congratulations! You've just built your first **beego** app.
+
+###### Please see [Documentation](http://beego.me/docs) for more.
+
+## Features
+
+* RESTful support
+* MVC architecture
+* Modularity
+* Auto API documents
+* Annotation router
+* Namespace
+* Powerful development tools
+* Full stack for Web & API
+
+## Documentation
+
+* [English](http://beego.me/docs/intro/)
+* [中文文档](http://beego.me/docs/intro/)
+* [Русский](http://beego.me/docs/intro/)
+
+## Community
+
+* [http://beego.me/community](http://beego.me/community)
+* Welcome to join us in Slack: [https://beego.slack.com](https://beego.slack.com), you can get invited from [here](https://github.com/beego/beedoc/issues/232)
+
+## License
+
+beego source code is licensed under the Apache Licence, Version 2.0
+(http://www.apache.org/licenses/LICENSE-2.0.html).
diff --git a/vendor/github.com/astaxie/beego/admin.go b/vendor/github.com/astaxie/beego/admin.go
new file mode 100644
index 0000000..0688dcb
--- /dev/null
+++ b/vendor/github.com/astaxie/beego/admin.go
@@ -0,0 +1,404 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// 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 beego
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "os"
+ "text/template"
+ "time"
+
+ "reflect"
+
+ "github.com/astaxie/beego/grace"
+ "github.com/astaxie/beego/logs"
+ "github.com/astaxie/beego/toolbox"
+ "github.com/astaxie/beego/utils"
+)
+
+// BeeAdminApp is the default adminApp used by admin module.
+var beeAdminApp *adminApp
+
+// FilterMonitorFunc is default monitor filter when admin module is enable.
+// if this func returns, admin module records qbs for this request by condition of this function logic.
+// usage:
+// func MyFilterMonitor(method, requestPath string, t time.Duration, pattern string, statusCode int) bool {
+// if method == "POST" {
+// return false
+// }
+// if t.Nanoseconds() < 100 {
+// return false
+// }
+// if strings.HasPrefix(requestPath, "/astaxie") {
+// return false
+// }
+// return true
+// }
+// beego.FilterMonitorFunc = MyFilterMonitor.
+var FilterMonitorFunc func(string, string, time.Duration, string, int) bool
+
+func init() {
+ beeAdminApp = &adminApp{
+ routers: make(map[string]http.HandlerFunc),
+ }
+ beeAdminApp.Route("/", adminIndex)
+ beeAdminApp.Route("/qps", qpsIndex)
+ beeAdminApp.Route("/prof", profIndex)
+ beeAdminApp.Route("/healthcheck", healthcheck)
+ beeAdminApp.Route("/task", taskStatus)
+ beeAdminApp.Route("/listconf", listConf)
+ FilterMonitorFunc = func(string, string, time.Duration, string, int) bool { return true }
+}
+
+// AdminIndex is the default http.Handler for admin module.
+// it matches url pattern "/".
+func adminIndex(rw http.ResponseWriter, r *http.Request) {
+ execTpl(rw, map[interface{}]interface{}{}, indexTpl, defaultScriptsTpl)
+}
+
+// QpsIndex is the http.Handler for writing qbs statistics map result info in http.ResponseWriter.
+// it's registered with url pattern "/qbs" in admin module.
+func qpsIndex(rw http.ResponseWriter, r *http.Request) {
+ data := make(map[interface{}]interface{})
+ data["Content"] = toolbox.StatisticsMap.GetMap()
+ execTpl(rw, data, qpsTpl, defaultScriptsTpl)
+}
+
+// ListConf is the http.Handler of displaying all beego configuration values as key/value pair.
+// it's registered with url pattern "/listconf" in admin module.
+func listConf(rw http.ResponseWriter, r *http.Request) {
+ r.ParseForm()
+ command := r.Form.Get("command")
+ if command == "" {
+ rw.Write([]byte("command not support"))
+ return
+ }
+
+ data := make(map[interface{}]interface{})
+ switch command {
+ case "conf":
+ m := make(map[string]interface{})
+ list("BConfig", BConfig, m)
+ m["AppConfigPath"] = appConfigPath
+ m["AppConfigProvider"] = appConfigProvider
+ tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
+ tmpl = template.Must(tmpl.Parse(configTpl))
+ tmpl = template.Must(tmpl.Parse(defaultScriptsTpl))
+
+ data["Content"] = m
+
+ tmpl.Execute(rw, data)
+
+ case "router":
+ content := PrintTree()
+ content["Fields"] = []string{
+ "Router Pattern",
+ "Methods",
+ "Controller",
+ }
+ data["Content"] = content
+ data["Title"] = "Routers"
+ execTpl(rw, data, routerAndFilterTpl, defaultScriptsTpl)
+ case "filter":
+ var (
+ content = map[string]interface{}{
+ "Fields": []string{
+ "Router Pattern",
+ "Filter Function",
+ },
+ }
+ filterTypes = []string{}
+ filterTypeData = make(map[string]interface{})
+ )
+
+ if BeeApp.Handlers.enableFilter {
+ var filterType string
+ for k, fr := range map[int]string{
+ BeforeStatic: "Before Static",
+ BeforeRouter: "Before Router",
+ BeforeExec: "Before Exec",
+ AfterExec: "After Exec",
+ FinishRouter: "Finish Router"} {
+ if bf := BeeApp.Handlers.filters[k]; len(bf) > 0 {
+ filterType = fr
+ filterTypes = append(filterTypes, filterType)
+ resultList := new([][]string)
+ for _, f := range bf {
+ var result = []string{
+ f.pattern,
+ utils.GetFuncName(f.filterFunc),
+ }
+ *resultList = append(*resultList, result)
+ }
+ filterTypeData[filterType] = resultList
+ }
+ }
+ }
+
+ content["Data"] = filterTypeData
+ content["Methods"] = filterTypes
+
+ data["Content"] = content
+ data["Title"] = "Filters"
+ execTpl(rw, data, routerAndFilterTpl, defaultScriptsTpl)
+ default:
+ rw.Write([]byte("command not support"))
+ }
+}
+
+func list(root string, p interface{}, m map[string]interface{}) {
+ pt := reflect.TypeOf(p)
+ pv := reflect.ValueOf(p)
+ if pt.Kind() == reflect.Ptr {
+ pt = pt.Elem()
+ pv = pv.Elem()
+ }
+ for i := 0; i < pv.NumField(); i++ {
+ var key string
+ if root == "" {
+ key = pt.Field(i).Name
+ } else {
+ key = root + "." + pt.Field(i).Name
+ }
+ if pv.Field(i).Kind() == reflect.Struct {
+ list(key, pv.Field(i).Interface(), m)
+ } else {
+ m[key] = pv.Field(i).Interface()
+ }
+ }
+}
+
+// PrintTree prints all registered routers.
+func PrintTree() map[string]interface{} {
+ var (
+ content = map[string]interface{}{}
+ methods = []string{}
+ methodsData = make(map[string]interface{})
+ )
+ for method, t := range BeeApp.Handlers.routers {
+
+ resultList := new([][]string)
+
+ printTree(resultList, t)
+
+ methods = append(methods, method)
+ methodsData[method] = resultList
+ }
+
+ content["Data"] = methodsData
+ content["Methods"] = methods
+ return content
+}
+
+func printTree(resultList *[][]string, t *Tree) {
+ for _, tr := range t.fixrouters {
+ printTree(resultList, tr)
+ }
+ if t.wildcard != nil {
+ printTree(resultList, t.wildcard)
+ }
+ for _, l := range t.leaves {
+ if v, ok := l.runObject.(*ControllerInfo); ok {
+ if v.routerType == routerTypeBeego {
+ var result = []string{
+ v.pattern,
+ fmt.Sprintf("%s", v.methods),
+ v.controllerType.String(),
+ }
+ *resultList = append(*resultList, result)
+ } else if v.routerType == routerTypeRESTFul {
+ var result = []string{
+ v.pattern,
+ fmt.Sprintf("%s", v.methods),
+ "",
+ }
+ *resultList = append(*resultList, result)
+ } else if v.routerType == routerTypeHandler {
+ var result = []string{
+ v.pattern,
+ "",
+ "",
+ }
+ *resultList = append(*resultList, result)
+ }
+ }
+ }
+}
+
+// ProfIndex is a http.Handler for showing profile command.
+// it's in url pattern "/prof" in admin module.
+func profIndex(rw http.ResponseWriter, r *http.Request) {
+ r.ParseForm()
+ command := r.Form.Get("command")
+ if command == "" {
+ return
+ }
+
+ var (
+ format = r.Form.Get("format")
+ data = make(map[interface{}]interface{})
+ result bytes.Buffer
+ )
+ toolbox.ProcessInput(command, &result)
+ data["Content"] = result.String()
+
+ if format == "json" && command == "gc summary" {
+ dataJSON, err := json.Marshal(data)
+ if err != nil {
+ http.Error(rw, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ rw.Header().Set("Content-Type", "application/json")
+ rw.Write(dataJSON)
+ return
+ }
+
+ data["Title"] = command
+ defaultTpl := defaultScriptsTpl
+ if command == "gc summary" {
+ defaultTpl = gcAjaxTpl
+ }
+ execTpl(rw, data, profillingTpl, defaultTpl)
+}
+
+// Healthcheck is a http.Handler calling health checking and showing the result.
+// it's in "/healthcheck" pattern in admin module.
+func healthcheck(rw http.ResponseWriter, req *http.Request) {
+ var (
+ result []string
+ data = make(map[interface{}]interface{})
+ resultList = new([][]string)
+ content = map[string]interface{}{
+ "Fields": []string{"Name", "Message", "Status"},
+ }
+ )
+
+ for name, h := range toolbox.AdminCheckList {
+ if err := h.Check(); err != nil {
+ result = []string{
+ "error",
+ name,
+ err.Error(),
+ }
+ } else {
+ result = []string{
+ "success",
+ name,
+ "OK",
+ }
+ }
+ *resultList = append(*resultList, result)
+ }
+
+ content["Data"] = resultList
+ data["Content"] = content
+ data["Title"] = "Health Check"
+ execTpl(rw, data, healthCheckTpl, defaultScriptsTpl)
+}
+
+// TaskStatus is a http.Handler with running task status (task name, status and the last execution).
+// it's in "/task" pattern in admin module.
+func taskStatus(rw http.ResponseWriter, req *http.Request) {
+ data := make(map[interface{}]interface{})
+
+ // Run Task
+ req.ParseForm()
+ taskname := req.Form.Get("taskname")
+ if taskname != "" {
+ if t, ok := toolbox.AdminTaskList[taskname]; ok {
+ if err := t.Run(); err != nil {
+ data["Message"] = []string{"error", fmt.Sprintf("%s", err)}
+ }
+ data["Message"] = []string{"success", fmt.Sprintf("%s run success,Now the Status is %s", taskname, t.GetStatus())}
+ } else {
+ data["Message"] = []string{"warning", fmt.Sprintf("there's no task which named: %s", taskname)}
+ }
+ }
+
+ // List Tasks
+ content := make(map[string]interface{})
+ resultList := new([][]string)
+ var fields = []string{
+ "Task Name",
+ "Task Spec",
+ "Task Status",
+ "Last Time",
+ "",
+ }
+ for tname, tk := range toolbox.AdminTaskList {
+ result := []string{
+ tname,
+ tk.GetSpec(),
+ tk.GetStatus(),
+ tk.GetPrev().String(),
+ }
+ *resultList = append(*resultList, result)
+ }
+
+ content["Fields"] = fields
+ content["Data"] = resultList
+ data["Content"] = content
+ data["Title"] = "Tasks"
+ execTpl(rw, data, tasksTpl, defaultScriptsTpl)
+}
+
+func execTpl(rw http.ResponseWriter, data map[interface{}]interface{}, tpls ...string) {
+ tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
+ for _, tpl := range tpls {
+ tmpl = template.Must(tmpl.Parse(tpl))
+ }
+ tmpl.Execute(rw, data)
+}
+
+// adminApp is an http.HandlerFunc map used as beeAdminApp.
+type adminApp struct {
+ routers map[string]http.HandlerFunc
+}
+
+// Route adds http.HandlerFunc to adminApp with url pattern.
+func (admin *adminApp) Route(pattern string, f http.HandlerFunc) {
+ admin.routers[pattern] = f
+}
+
+// Run adminApp http server.
+// Its addr is defined in configuration file as adminhttpaddr and adminhttpport.
+func (admin *adminApp) Run() {
+ if len(toolbox.AdminTaskList) > 0 {
+ toolbox.StartTask()
+ }
+ addr := BConfig.Listen.AdminAddr
+
+ if BConfig.Listen.AdminPort != 0 {
+ addr = fmt.Sprintf("%s:%d", BConfig.Listen.AdminAddr, BConfig.Listen.AdminPort)
+ }
+ for p, f := range admin.routers {
+ http.Handle(p, f)
+ }
+ logs.Info("Admin server Running on %s", addr)
+
+ var err error
+ if BConfig.Listen.Graceful {
+ err = grace.ListenAndServe(addr, nil)
+ } else {
+ err = http.ListenAndServe(addr, nil)
+ }
+ if err != nil {
+ logs.Critical("Admin ListenAndServe: ", err, fmt.Sprintf("%d", os.Getpid()))
+ }
+}
diff --git a/vendor/github.com/astaxie/beego/adminui.go b/vendor/github.com/astaxie/beego/adminui.go
new file mode 100644
index 0000000..cdcdef3
--- /dev/null
+++ b/vendor/github.com/astaxie/beego/adminui.go
@@ -0,0 +1,356 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// 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 beego
+
+var indexTpl = `
+{{define "content"}}
+
+ {{.Content}}
+ Go Home
+
+ Powered by beego {{.BeegoVersion}}
+
+
+
+
+
+`
+
+type errorInfo struct {
+ controllerType reflect.Type
+ handler http.HandlerFunc
+ method string
+ errorType int
+}
+
+// ErrorMaps holds map of http handlers for each error string.
+// there is 10 kinds default error(40x and 50x)
+var ErrorMaps = make(map[string]*errorInfo, 10)
+
+// show 401 unauthorized error.
+func unauthorized(rw http.ResponseWriter, r *http.Request) {
+ responseError(rw, r,
+ 401,
+ " The page you have requested can't be authorized."+
+ " Perhaps you are here because:"+
+ "
"+
+ " The credentials you supplied are incorrect"+
+ " There are errors in the website address"+
+ "
",
+ )
+}
+
+// show 402 Payment Required
+func paymentRequired(rw http.ResponseWriter, r *http.Request) {
+ responseError(rw, r,
+ 402,
+ " The page you have requested Payment Required."+
+ " Perhaps you are here because:"+
+ "
"+
+ " The credentials you supplied are incorrect"+
+ " There are errors in the website address"+
+ "
",
+ )
+}
+
+// show 403 forbidden error.
+func forbidden(rw http.ResponseWriter, r *http.Request) {
+ responseError(rw, r,
+ 403,
+ " The page you have requested is forbidden."+
+ " Perhaps you are here because:"+
+ "
"+
+ " Your address may be blocked"+
+ " The site may be disabled"+
+ " You need to log in"+
+ "
",
+ )
+}
+
+// show 422 missing xsrf token
+func missingxsrf(rw http.ResponseWriter, r *http.Request) {
+ responseError(rw, r,
+ 422,
+ " The page you have requested is forbidden."+
+ " Perhaps you are here because:"+
+ "
"+
+ " '_xsrf' argument missing from POST"+
+ "
",
+ )
+}
+
+// show 417 invalid xsrf token
+func invalidxsrf(rw http.ResponseWriter, r *http.Request) {
+ responseError(rw, r,
+ 417,
+ " The page you have requested is forbidden."+
+ " Perhaps you are here because:"+
+ "
"+
+ " expected XSRF not found"+
+ "
",
+ )
+}
+
+// show 404 not found error.
+func notFound(rw http.ResponseWriter, r *http.Request) {
+ responseError(rw, r,
+ 404,
+ " The page you have requested has flown the coop."+
+ " Perhaps you are here because:"+
+ "
"+
+ " The page has moved"+
+ " The page no longer exists"+
+ " You were looking for your puppy and got lost"+
+ " You like 404 pages"+
+ "
",
+ )
+}
+
+// show 405 Method Not Allowed
+func methodNotAllowed(rw http.ResponseWriter, r *http.Request) {
+ responseError(rw, r,
+ 405,
+ " The method you have requested Not Allowed."+
+ " Perhaps you are here because:"+
+ "
"+
+ " The method specified in the Request-Line is not allowed for the resource identified by the Request-URI"+
+ " The response MUST include an Allow header containing a list of valid methods for the requested resource."+
+ "
",
+ )
+}
+
+// show 500 internal server error.
+func internalServerError(rw http.ResponseWriter, r *http.Request) {
+ responseError(rw, r,
+ 500,
+ " The page you have requested is down right now."+
+ "
"+
+ " Please try again later and report the error to the website administrator"+
+ "
",
+ )
+}
+
+// show 501 Not Implemented.
+func notImplemented(rw http.ResponseWriter, r *http.Request) {
+ responseError(rw, r,
+ 501,
+ " The page you have requested is Not Implemented."+
+ "
"+
+ " Please try again later and report the error to the website administrator"+
+ "
",
+ )
+}
+
+// show 502 Bad Gateway.
+func badGateway(rw http.ResponseWriter, r *http.Request) {
+ responseError(rw, r,
+ 502,
+ " The page you have requested is down right now."+
+ "
"+
+ " The server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulfill the request."+
+ " Please try again later and report the error to the website administrator"+
+ "
",
+ )
+}
+
+// show 503 service unavailable error.
+func serviceUnavailable(rw http.ResponseWriter, r *http.Request) {
+ responseError(rw, r,
+ 503,
+ " The page you have requested is unavailable."+
+ " Perhaps you are here because:"+
+ "
"+
+ "
The page is overloaded"+
+ " Please try again later."+
+ "
",
+ )
+}
+
+// show 504 Gateway Timeout.
+func gatewayTimeout(rw http.ResponseWriter, r *http.Request) {
+ responseError(rw, r,
+ 504,
+ " The page you have requested is unavailable"+
+ " Perhaps you are here because:"+
+ "
"+
+ "
The server, while acting as a gateway or proxy, did not receive a timely response from the upstream server specified by the URI."+
+ " Please try again later."+
+ "
",
+ )
+}
+
+func responseError(rw http.ResponseWriter, r *http.Request, errCode int, errContent string) {
+ t, _ := template.New("beegoerrortemp").Parse(errtpl)
+ data := map[string]interface{}{
+ "Title": http.StatusText(errCode),
+ "BeegoVersion": VERSION,
+ "Content": template.HTML(errContent),
+ }
+ t.Execute(rw, data)
+}
+
+// ErrorHandler registers http.HandlerFunc to each http err code string.
+// usage:
+// beego.ErrorHandler("404",NotFound)
+// beego.ErrorHandler("500",InternalServerError)
+func ErrorHandler(code string, h http.HandlerFunc) *App {
+ ErrorMaps[code] = &errorInfo{
+ errorType: errorTypeHandler,
+ handler: h,
+ method: code,
+ }
+ return BeeApp
+}
+
+// ErrorController registers ControllerInterface to each http err code string.
+// usage:
+// beego.ErrorController(&controllers.ErrorController{})
+func ErrorController(c ControllerInterface) *App {
+ reflectVal := reflect.ValueOf(c)
+ rt := reflectVal.Type()
+ ct := reflect.Indirect(reflectVal).Type()
+ for i := 0; i < rt.NumMethod(); i++ {
+ methodName := rt.Method(i).Name
+ if !utils.InSlice(methodName, exceptMethod) && strings.HasPrefix(methodName, "Error") {
+ errName := strings.TrimPrefix(methodName, "Error")
+ ErrorMaps[errName] = &errorInfo{
+ errorType: errorTypeController,
+ controllerType: ct,
+ method: methodName,
+ }
+ }
+ }
+ return BeeApp
+}
+
+// Exception Write HttpStatus with errCode and Exec error handler if exist.
+func Exception(errCode uint64, ctx *context.Context) {
+ exception(strconv.FormatUint(errCode, 10), ctx)
+}
+
+// show error string as simple text message.
+// if error string is empty, show 503 or 500 error as default.
+func exception(errCode string, ctx *context.Context) {
+ atoi := func(code string) int {
+ v, err := strconv.Atoi(code)
+ if err == nil {
+ return v
+ }
+ if ctx.Output.Status == 0 {
+ return 503
+ }
+ return ctx.Output.Status
+ }
+
+ for _, ec := range []string{errCode, "503", "500"} {
+ if h, ok := ErrorMaps[ec]; ok {
+ executeError(h, ctx, atoi(ec))
+ return
+ }
+ }
+ //if 50x error has been removed from errorMap
+ ctx.ResponseWriter.WriteHeader(atoi(errCode))
+ ctx.WriteString(errCode)
+}
+
+func executeError(err *errorInfo, ctx *context.Context, code int) {
+ if err.errorType == errorTypeHandler {
+ ctx.ResponseWriter.WriteHeader(code)
+ err.handler(ctx.ResponseWriter, ctx.Request)
+ return
+ }
+ if err.errorType == errorTypeController {
+ ctx.Output.SetStatus(code)
+ //Invoke the request handler
+ vc := reflect.New(err.controllerType)
+ execController, ok := vc.Interface().(ControllerInterface)
+ if !ok {
+ panic("controller is not ControllerInterface")
+ }
+ //call the controller init function
+ execController.Init(ctx, err.controllerType.Name(), err.method, vc.Interface())
+
+ //call prepare function
+ execController.Prepare()
+
+ execController.URLMapping()
+
+ method := vc.MethodByName(err.method)
+ method.Call([]reflect.Value{})
+
+ //render template
+ if BConfig.WebConfig.AutoRender {
+ if err := execController.Render(); err != nil {
+ panic(err)
+ }
+ }
+
+ // finish all runrouter. release resource
+ execController.Finish()
+ }
+}
diff --git a/vendor/github.com/astaxie/beego/filter.go b/vendor/github.com/astaxie/beego/filter.go
new file mode 100644
index 0000000..9cc6e91
--- /dev/null
+++ b/vendor/github.com/astaxie/beego/filter.go
@@ -0,0 +1,44 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// 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 beego
+
+import "github.com/astaxie/beego/context"
+
+// FilterFunc defines a filter function which is invoked before the controller handler is executed.
+type FilterFunc func(*context.Context)
+
+// FilterRouter defines a filter operation which is invoked before the controller handler is executed.
+// It can match the URL against a pattern, and execute a filter function
+// when a request with a matching URL arrives.
+type FilterRouter struct {
+ filterFunc FilterFunc
+ tree *Tree
+ pattern string
+ returnOnOutput bool
+ resetParams bool
+}
+
+// ValidRouter checks if the current request is matched by this filter.
+// If the request is matched, the values of the URL parameters defined
+// by the filter pattern are also returned.
+func (f *FilterRouter) ValidRouter(url string, ctx *context.Context) bool {
+ isOk := f.tree.Match(url, ctx)
+ if isOk != nil {
+ if b, ok := isOk.(bool); ok {
+ return b
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/astaxie/beego/flash.go b/vendor/github.com/astaxie/beego/flash.go
new file mode 100644
index 0000000..a6485a1
--- /dev/null
+++ b/vendor/github.com/astaxie/beego/flash.go
@@ -0,0 +1,110 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// 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 beego
+
+import (
+ "fmt"
+ "net/url"
+ "strings"
+)
+
+// FlashData is a tools to maintain data when using across request.
+type FlashData struct {
+ Data map[string]string
+}
+
+// NewFlash return a new empty FlashData struct.
+func NewFlash() *FlashData {
+ return &FlashData{
+ Data: make(map[string]string),
+ }
+}
+
+// Set message to flash
+func (fd *FlashData) Set(key string, msg string, args ...interface{}) {
+ if len(args) == 0 {
+ fd.Data[key] = msg
+ } else {
+ fd.Data[key] = fmt.Sprintf(msg, args...)
+ }
+}
+
+// Success writes success message to flash.
+func (fd *FlashData) Success(msg string, args ...interface{}) {
+ if len(args) == 0 {
+ fd.Data["success"] = msg
+ } else {
+ fd.Data["success"] = fmt.Sprintf(msg, args...)
+ }
+}
+
+// Notice writes notice message to flash.
+func (fd *FlashData) Notice(msg string, args ...interface{}) {
+ if len(args) == 0 {
+ fd.Data["notice"] = msg
+ } else {
+ fd.Data["notice"] = fmt.Sprintf(msg, args...)
+ }
+}
+
+// Warning writes warning message to flash.
+func (fd *FlashData) Warning(msg string, args ...interface{}) {
+ if len(args) == 0 {
+ fd.Data["warning"] = msg
+ } else {
+ fd.Data["warning"] = fmt.Sprintf(msg, args...)
+ }
+}
+
+// Error writes error message to flash.
+func (fd *FlashData) Error(msg string, args ...interface{}) {
+ if len(args) == 0 {
+ fd.Data["error"] = msg
+ } else {
+ fd.Data["error"] = fmt.Sprintf(msg, args...)
+ }
+}
+
+// Store does the saving operation of flash data.
+// the data are encoded and saved in cookie.
+func (fd *FlashData) Store(c *Controller) {
+ c.Data["flash"] = fd.Data
+ var flashValue string
+ for key, value := range fd.Data {
+ flashValue += "\x00" + key + "\x23" + BConfig.WebConfig.FlashSeparator + "\x23" + value + "\x00"
+ }
+ c.Ctx.SetCookie(BConfig.WebConfig.FlashName, url.QueryEscape(flashValue), 0, "/")
+}
+
+// ReadFromRequest parsed flash data from encoded values in cookie.
+func ReadFromRequest(c *Controller) *FlashData {
+ flash := NewFlash()
+ if cookie, err := c.Ctx.Request.Cookie(BConfig.WebConfig.FlashName); err == nil {
+ v, _ := url.QueryUnescape(cookie.Value)
+ vals := strings.Split(v, "\x00")
+ for _, v := range vals {
+ if len(v) > 0 {
+ kv := strings.Split(v, "\x23"+BConfig.WebConfig.FlashSeparator+"\x23")
+ if len(kv) == 2 {
+ flash.Data[kv[0]] = kv[1]
+ }
+ }
+ }
+ //read one time then delete it
+ c.Ctx.SetCookie(BConfig.WebConfig.FlashName, "", -1, "/")
+ }
+ c.Data["flash"] = flash.Data
+ return flash
+}
diff --git a/vendor/github.com/astaxie/beego/hooks.go b/vendor/github.com/astaxie/beego/hooks.go
new file mode 100644
index 0000000..c5ec8e2
--- /dev/null
+++ b/vendor/github.com/astaxie/beego/hooks.go
@@ -0,0 +1,103 @@
+package beego
+
+import (
+ "encoding/json"
+ "mime"
+ "net/http"
+ "path/filepath"
+
+ "github.com/astaxie/beego/context"
+ "github.com/astaxie/beego/logs"
+ "github.com/astaxie/beego/session"
+)
+
+//
+func registerMime() error {
+ for k, v := range mimemaps {
+ mime.AddExtensionType(k, v)
+ }
+ return nil
+}
+
+// register default error http handlers, 404,401,403,500 and 503.
+func registerDefaultErrorHandler() error {
+ m := map[string]func(http.ResponseWriter, *http.Request){
+ "401": unauthorized,
+ "402": paymentRequired,
+ "403": forbidden,
+ "404": notFound,
+ "405": methodNotAllowed,
+ "500": internalServerError,
+ "501": notImplemented,
+ "502": badGateway,
+ "503": serviceUnavailable,
+ "504": gatewayTimeout,
+ "417": invalidxsrf,
+ "422": missingxsrf,
+ }
+ for e, h := range m {
+ if _, ok := ErrorMaps[e]; !ok {
+ ErrorHandler(e, h)
+ }
+ }
+ return nil
+}
+
+func registerSession() error {
+ if BConfig.WebConfig.Session.SessionOn {
+ var err error
+ sessionConfig := AppConfig.String("sessionConfig")
+ conf := new(session.ManagerConfig)
+ if sessionConfig == "" {
+ conf.CookieName = BConfig.WebConfig.Session.SessionName
+ conf.EnableSetCookie = BConfig.WebConfig.Session.SessionAutoSetCookie
+ conf.Gclifetime = BConfig.WebConfig.Session.SessionGCMaxLifetime
+ conf.Secure = BConfig.Listen.EnableHTTPS
+ conf.CookieLifeTime = BConfig.WebConfig.Session.SessionCookieLifeTime
+ conf.ProviderConfig = filepath.ToSlash(BConfig.WebConfig.Session.SessionProviderConfig)
+ conf.DisableHTTPOnly = BConfig.WebConfig.Session.SessionDisableHTTPOnly
+ conf.Domain = BConfig.WebConfig.Session.SessionDomain
+ conf.EnableSidInHTTPHeader = BConfig.WebConfig.Session.SessionEnableSidInHTTPHeader
+ conf.SessionNameInHTTPHeader = BConfig.WebConfig.Session.SessionNameInHTTPHeader
+ conf.EnableSidInURLQuery = BConfig.WebConfig.Session.SessionEnableSidInURLQuery
+ } else {
+ if err = json.Unmarshal([]byte(sessionConfig), conf); err != nil {
+ return err
+ }
+ }
+ if GlobalSessions, err = session.NewManager(BConfig.WebConfig.Session.SessionProvider, conf); err != nil {
+ return err
+ }
+ go GlobalSessions.GC()
+ }
+ return nil
+}
+
+func registerTemplate() error {
+ defer lockViewPaths()
+ if err := AddViewPath(BConfig.WebConfig.ViewsPath); err != nil {
+ if BConfig.RunMode == DEV {
+ logs.Warn(err)
+ }
+ return err
+ }
+ return nil
+}
+
+func registerAdmin() error {
+ if BConfig.Listen.EnableAdmin {
+ go beeAdminApp.Run()
+ }
+ return nil
+}
+
+func registerGzip() error {
+ if BConfig.EnableGzip {
+ context.InitGzip(
+ AppConfig.DefaultInt("gzipMinLength", -1),
+ AppConfig.DefaultInt("gzipCompressLevel", -1),
+ AppConfig.DefaultStrings("includedMethods", []string{"GET"}),
+ )
+ }
+ return nil
+}
diff --git a/vendor/github.com/astaxie/beego/log.go b/vendor/github.com/astaxie/beego/log.go
new file mode 100644
index 0000000..e9412f9
--- /dev/null
+++ b/vendor/github.com/astaxie/beego/log.go
@@ -0,0 +1,111 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// 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 beego
+
+import (
+ "strings"
+
+ "github.com/astaxie/beego/logs"
+)
+
+// Log levels to control the logging output.
+const (
+ LevelEmergency = iota
+ LevelAlert
+ LevelCritical
+ LevelError
+ LevelWarning
+ LevelNotice
+ LevelInformational
+ LevelDebug
+)
+
+// BeeLogger references the used application logger.
+var BeeLogger = logs.GetBeeLogger()
+
+// SetLevel sets the global log level used by the simple logger.
+func SetLevel(l int) {
+ logs.SetLevel(l)
+}
+
+// SetLogFuncCall set the CallDepth, default is 3
+func SetLogFuncCall(b bool) {
+ logs.SetLogFuncCall(b)
+}
+
+// SetLogger sets a new logger.
+func SetLogger(adaptername string, config string) error {
+ return logs.SetLogger(adaptername, config)
+}
+
+// Emergency logs a message at emergency level.
+func Emergency(v ...interface{}) {
+ logs.Emergency(generateFmtStr(len(v)), v...)
+}
+
+// Alert logs a message at alert level.
+func Alert(v ...interface{}) {
+ logs.Alert(generateFmtStr(len(v)), v...)
+}
+
+// Critical logs a message at critical level.
+func Critical(v ...interface{}) {
+ logs.Critical(generateFmtStr(len(v)), v...)
+}
+
+// Error logs a message at error level.
+func Error(v ...interface{}) {
+ logs.Error(generateFmtStr(len(v)), v...)
+}
+
+// Warning logs a message at warning level.
+func Warning(v ...interface{}) {
+ logs.Warning(generateFmtStr(len(v)), v...)
+}
+
+// Warn compatibility alias for Warning()
+func Warn(v ...interface{}) {
+ logs.Warn(generateFmtStr(len(v)), v...)
+}
+
+// Notice logs a message at notice level.
+func Notice(v ...interface{}) {
+ logs.Notice(generateFmtStr(len(v)), v...)
+}
+
+// Informational logs a message at info level.
+func Informational(v ...interface{}) {
+ logs.Informational(generateFmtStr(len(v)), v...)
+}
+
+// Info compatibility alias for Warning()
+func Info(v ...interface{}) {
+ logs.Info(generateFmtStr(len(v)), v...)
+}
+
+// Debug logs a message at debug level.
+func Debug(v ...interface{}) {
+ logs.Debug(generateFmtStr(len(v)), v...)
+}
+
+// Trace logs a message at trace level.
+// compatibility alias for Warning()
+func Trace(v ...interface{}) {
+ logs.Trace(generateFmtStr(len(v)), v...)
+}
+
+func generateFmtStr(n int) string {
+ return strings.Repeat("%v ", n)
+}
diff --git a/vendor/github.com/astaxie/beego/mime.go b/vendor/github.com/astaxie/beego/mime.go
new file mode 100644
index 0000000..ca2878a
--- /dev/null
+++ b/vendor/github.com/astaxie/beego/mime.go
@@ -0,0 +1,556 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// 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 beego
+
+var mimemaps = map[string]string{
+ ".3dm": "x-world/x-3dmf",
+ ".3dmf": "x-world/x-3dmf",
+ ".7z": "application/x-7z-compressed",
+ ".a": "application/octet-stream",
+ ".aab": "application/x-authorware-bin",
+ ".aam": "application/x-authorware-map",
+ ".aas": "application/x-authorware-seg",
+ ".abc": "text/vndabc",
+ ".ace": "application/x-ace-compressed",
+ ".acgi": "text/html",
+ ".afl": "video/animaflex",
+ ".ai": "application/postscript",
+ ".aif": "audio/aiff",
+ ".aifc": "audio/aiff",
+ ".aiff": "audio/aiff",
+ ".aim": "application/x-aim",
+ ".aip": "text/x-audiosoft-intra",
+ ".alz": "application/x-alz-compressed",
+ ".ani": "application/x-navi-animation",
+ ".aos": "application/x-nokia-9000-communicator-add-on-software",
+ ".aps": "application/mime",
+ ".apk": "application/vnd.android.package-archive",
+ ".arc": "application/x-arc-compressed",
+ ".arj": "application/arj",
+ ".art": "image/x-jg",
+ ".asf": "video/x-ms-asf",
+ ".asm": "text/x-asm",
+ ".asp": "text/asp",
+ ".asx": "application/x-mplayer2",
+ ".au": "audio/basic",
+ ".avi": "video/x-msvideo",
+ ".avs": "video/avs-video",
+ ".bcpio": "application/x-bcpio",
+ ".bin": "application/mac-binary",
+ ".bmp": "image/bmp",
+ ".boo": "application/book",
+ ".book": "application/book",
+ ".boz": "application/x-bzip2",
+ ".bsh": "application/x-bsh",
+ ".bz2": "application/x-bzip2",
+ ".bz": "application/x-bzip",
+ ".c++": "text/plain",
+ ".c": "text/x-c",
+ ".cab": "application/vnd.ms-cab-compressed",
+ ".cat": "application/vndms-pkiseccat",
+ ".cc": "text/x-c",
+ ".ccad": "application/clariscad",
+ ".cco": "application/x-cocoa",
+ ".cdf": "application/cdf",
+ ".cer": "application/pkix-cert",
+ ".cha": "application/x-chat",
+ ".chat": "application/x-chat",
+ ".chrt": "application/vnd.kde.kchart",
+ ".class": "application/java",
+ ".com": "text/plain",
+ ".conf": "text/plain",
+ ".cpio": "application/x-cpio",
+ ".cpp": "text/x-c",
+ ".cpt": "application/mac-compactpro",
+ ".crl": "application/pkcs-crl",
+ ".crt": "application/pkix-cert",
+ ".crx": "application/x-chrome-extension",
+ ".csh": "text/x-scriptcsh",
+ ".css": "text/css",
+ ".csv": "text/csv",
+ ".cxx": "text/plain",
+ ".dar": "application/x-dar",
+ ".dcr": "application/x-director",
+ ".deb": "application/x-debian-package",
+ ".deepv": "application/x-deepv",
+ ".def": "text/plain",
+ ".der": "application/x-x509-ca-cert",
+ ".dif": "video/x-dv",
+ ".dir": "application/x-director",
+ ".divx": "video/divx",
+ ".dl": "video/dl",
+ ".dmg": "application/x-apple-diskimage",
+ ".doc": "application/msword",
+ ".dot": "application/msword",
+ ".dp": "application/commonground",
+ ".drw": "application/drafting",
+ ".dump": "application/octet-stream",
+ ".dv": "video/x-dv",
+ ".dvi": "application/x-dvi",
+ ".dwf": "drawing/x-dwf=(old)",
+ ".dwg": "application/acad",
+ ".dxf": "application/dxf",
+ ".dxr": "application/x-director",
+ ".el": "text/x-scriptelisp",
+ ".elc": "application/x-bytecodeelisp=(compiled=elisp)",
+ ".eml": "message/rfc822",
+ ".env": "application/x-envoy",
+ ".eps": "application/postscript",
+ ".es": "application/x-esrehber",
+ ".etx": "text/x-setext",
+ ".evy": "application/envoy",
+ ".exe": "application/octet-stream",
+ ".f77": "text/x-fortran",
+ ".f90": "text/x-fortran",
+ ".f": "text/x-fortran",
+ ".fdf": "application/vndfdf",
+ ".fif": "application/fractals",
+ ".fli": "video/fli",
+ ".flo": "image/florian",
+ ".flv": "video/x-flv",
+ ".flx": "text/vndfmiflexstor",
+ ".fmf": "video/x-atomic3d-feature",
+ ".for": "text/x-fortran",
+ ".fpx": "image/vndfpx",
+ ".frl": "application/freeloader",
+ ".funk": "audio/make",
+ ".g3": "image/g3fax",
+ ".g": "text/plain",
+ ".gif": "image/gif",
+ ".gl": "video/gl",
+ ".gsd": "audio/x-gsm",
+ ".gsm": "audio/x-gsm",
+ ".gsp": "application/x-gsp",
+ ".gss": "application/x-gss",
+ ".gtar": "application/x-gtar",
+ ".gz": "application/x-compressed",
+ ".gzip": "application/x-gzip",
+ ".h": "text/x-h",
+ ".hdf": "application/x-hdf",
+ ".help": "application/x-helpfile",
+ ".hgl": "application/vndhp-hpgl",
+ ".hh": "text/x-h",
+ ".hlb": "text/x-script",
+ ".hlp": "application/hlp",
+ ".hpg": "application/vndhp-hpgl",
+ ".hpgl": "application/vndhp-hpgl",
+ ".hqx": "application/binhex",
+ ".hta": "application/hta",
+ ".htc": "text/x-component",
+ ".htm": "text/html",
+ ".html": "text/html",
+ ".htmls": "text/html",
+ ".htt": "text/webviewhtml",
+ ".htx": "text/html",
+ ".ice": "x-conference/x-cooltalk",
+ ".ico": "image/x-icon",
+ ".ics": "text/calendar",
+ ".icz": "text/calendar",
+ ".idc": "text/plain",
+ ".ief": "image/ief",
+ ".iefs": "image/ief",
+ ".iges": "application/iges",
+ ".igs": "application/iges",
+ ".ima": "application/x-ima",
+ ".imap": "application/x-httpd-imap",
+ ".inf": "application/inf",
+ ".ins": "application/x-internett-signup",
+ ".ip": "application/x-ip2",
+ ".isu": "video/x-isvideo",
+ ".it": "audio/it",
+ ".iv": "application/x-inventor",
+ ".ivr": "i-world/i-vrml",
+ ".ivy": "application/x-livescreen",
+ ".jam": "audio/x-jam",
+ ".jav": "text/x-java-source",
+ ".java": "text/x-java-source",
+ ".jcm": "application/x-java-commerce",
+ ".jfif-tbnl": "image/jpeg",
+ ".jfif": "image/jpeg",
+ ".jnlp": "application/x-java-jnlp-file",
+ ".jpe": "image/jpeg",
+ ".jpeg": "image/jpeg",
+ ".jpg": "image/jpeg",
+ ".jps": "image/x-jps",
+ ".js": "application/javascript",
+ ".json": "application/json",
+ ".jut": "image/jutvision",
+ ".kar": "audio/midi",
+ ".karbon": "application/vnd.kde.karbon",
+ ".kfo": "application/vnd.kde.kformula",
+ ".flw": "application/vnd.kde.kivio",
+ ".kml": "application/vnd.google-earth.kml+xml",
+ ".kmz": "application/vnd.google-earth.kmz",
+ ".kon": "application/vnd.kde.kontour",
+ ".kpr": "application/vnd.kde.kpresenter",
+ ".kpt": "application/vnd.kde.kpresenter",
+ ".ksp": "application/vnd.kde.kspread",
+ ".kwd": "application/vnd.kde.kword",
+ ".kwt": "application/vnd.kde.kword",
+ ".ksh": "text/x-scriptksh",
+ ".la": "audio/nspaudio",
+ ".lam": "audio/x-liveaudio",
+ ".latex": "application/x-latex",
+ ".lha": "application/lha",
+ ".lhx": "application/octet-stream",
+ ".list": "text/plain",
+ ".lma": "audio/nspaudio",
+ ".log": "text/plain",
+ ".lsp": "text/x-scriptlisp",
+ ".lst": "text/plain",
+ ".lsx": "text/x-la-asf",
+ ".ltx": "application/x-latex",
+ ".lzh": "application/octet-stream",
+ ".lzx": "application/lzx",
+ ".m1v": "video/mpeg",
+ ".m2a": "audio/mpeg",
+ ".m2v": "video/mpeg",
+ ".m3u": "audio/x-mpegurl",
+ ".m": "text/x-m",
+ ".man": "application/x-troff-man",
+ ".manifest": "text/cache-manifest",
+ ".map": "application/x-navimap",
+ ".mar": "text/plain",
+ ".mbd": "application/mbedlet",
+ ".mc$": "application/x-magic-cap-package-10",
+ ".mcd": "application/mcad",
+ ".mcf": "text/mcf",
+ ".mcp": "application/netmc",
+ ".me": "application/x-troff-me",
+ ".mht": "message/rfc822",
+ ".mhtml": "message/rfc822",
+ ".mid": "application/x-midi",
+ ".midi": "application/x-midi",
+ ".mif": "application/x-frame",
+ ".mime": "message/rfc822",
+ ".mjf": "audio/x-vndaudioexplosionmjuicemediafile",
+ ".mjpg": "video/x-motion-jpeg",
+ ".mm": "application/base64",
+ ".mme": "application/base64",
+ ".mod": "audio/mod",
+ ".moov": "video/quicktime",
+ ".mov": "video/quicktime",
+ ".movie": "video/x-sgi-movie",
+ ".mp2": "audio/mpeg",
+ ".mp3": "audio/mpeg3",
+ ".mp4": "video/mp4",
+ ".mpa": "audio/mpeg",
+ ".mpc": "application/x-project",
+ ".mpe": "video/mpeg",
+ ".mpeg": "video/mpeg",
+ ".mpg": "video/mpeg",
+ ".mpga": "audio/mpeg",
+ ".mpp": "application/vndms-project",
+ ".mpt": "application/x-project",
+ ".mpv": "application/x-project",
+ ".mpx": "application/x-project",
+ ".mrc": "application/marc",
+ ".ms": "application/x-troff-ms",
+ ".mv": "video/x-sgi-movie",
+ ".my": "audio/make",
+ ".mzz": "application/x-vndaudioexplosionmzz",
+ ".nap": "image/naplps",
+ ".naplps": "image/naplps",
+ ".nc": "application/x-netcdf",
+ ".ncm": "application/vndnokiaconfiguration-message",
+ ".nif": "image/x-niff",
+ ".niff": "image/x-niff",
+ ".nix": "application/x-mix-transfer",
+ ".nsc": "application/x-conference",
+ ".nvd": "application/x-navidoc",
+ ".o": "application/octet-stream",
+ ".oda": "application/oda",
+ ".odb": "application/vnd.oasis.opendocument.database",
+ ".odc": "application/vnd.oasis.opendocument.chart",
+ ".odf": "application/vnd.oasis.opendocument.formula",
+ ".odg": "application/vnd.oasis.opendocument.graphics",
+ ".odi": "application/vnd.oasis.opendocument.image",
+ ".odm": "application/vnd.oasis.opendocument.text-master",
+ ".odp": "application/vnd.oasis.opendocument.presentation",
+ ".ods": "application/vnd.oasis.opendocument.spreadsheet",
+ ".odt": "application/vnd.oasis.opendocument.text",
+ ".oga": "audio/ogg",
+ ".ogg": "audio/ogg",
+ ".ogv": "video/ogg",
+ ".omc": "application/x-omc",
+ ".omcd": "application/x-omcdatamaker",
+ ".omcr": "application/x-omcregerator",
+ ".otc": "application/vnd.oasis.opendocument.chart-template",
+ ".otf": "application/vnd.oasis.opendocument.formula-template",
+ ".otg": "application/vnd.oasis.opendocument.graphics-template",
+ ".oth": "application/vnd.oasis.opendocument.text-web",
+ ".oti": "application/vnd.oasis.opendocument.image-template",
+ ".otm": "application/vnd.oasis.opendocument.text-master",
+ ".otp": "application/vnd.oasis.opendocument.presentation-template",
+ ".ots": "application/vnd.oasis.opendocument.spreadsheet-template",
+ ".ott": "application/vnd.oasis.opendocument.text-template",
+ ".p10": "application/pkcs10",
+ ".p12": "application/pkcs-12",
+ ".p7a": "application/x-pkcs7-signature",
+ ".p7c": "application/pkcs7-mime",
+ ".p7m": "application/pkcs7-mime",
+ ".p7r": "application/x-pkcs7-certreqresp",
+ ".p7s": "application/pkcs7-signature",
+ ".p": "text/x-pascal",
+ ".part": "application/pro_eng",
+ ".pas": "text/pascal",
+ ".pbm": "image/x-portable-bitmap",
+ ".pcl": "application/vndhp-pcl",
+ ".pct": "image/x-pict",
+ ".pcx": "image/x-pcx",
+ ".pdb": "chemical/x-pdb",
+ ".pdf": "application/pdf",
+ ".pfunk": "audio/make",
+ ".pgm": "image/x-portable-graymap",
+ ".pic": "image/pict",
+ ".pict": "image/pict",
+ ".pkg": "application/x-newton-compatible-pkg",
+ ".pko": "application/vndms-pkipko",
+ ".pl": "text/x-scriptperl",
+ ".plx": "application/x-pixclscript",
+ ".pm4": "application/x-pagemaker",
+ ".pm5": "application/x-pagemaker",
+ ".pm": "text/x-scriptperl-module",
+ ".png": "image/png",
+ ".pnm": "application/x-portable-anymap",
+ ".pot": "application/mspowerpoint",
+ ".pov": "model/x-pov",
+ ".ppa": "application/vndms-powerpoint",
+ ".ppm": "image/x-portable-pixmap",
+ ".pps": "application/mspowerpoint",
+ ".ppt": "application/mspowerpoint",
+ ".ppz": "application/mspowerpoint",
+ ".pre": "application/x-freelance",
+ ".prt": "application/pro_eng",
+ ".ps": "application/postscript",
+ ".psd": "application/octet-stream",
+ ".pvu": "paleovu/x-pv",
+ ".pwz": "application/vndms-powerpoint",
+ ".py": "text/x-scriptphyton",
+ ".pyc": "application/x-bytecodepython",
+ ".qcp": "audio/vndqcelp",
+ ".qd3": "x-world/x-3dmf",
+ ".qd3d": "x-world/x-3dmf",
+ ".qif": "image/x-quicktime",
+ ".qt": "video/quicktime",
+ ".qtc": "video/x-qtc",
+ ".qti": "image/x-quicktime",
+ ".qtif": "image/x-quicktime",
+ ".ra": "audio/x-pn-realaudio",
+ ".ram": "audio/x-pn-realaudio",
+ ".rar": "application/x-rar-compressed",
+ ".ras": "application/x-cmu-raster",
+ ".rast": "image/cmu-raster",
+ ".rexx": "text/x-scriptrexx",
+ ".rf": "image/vndrn-realflash",
+ ".rgb": "image/x-rgb",
+ ".rm": "application/vndrn-realmedia",
+ ".rmi": "audio/mid",
+ ".rmm": "audio/x-pn-realaudio",
+ ".rmp": "audio/x-pn-realaudio",
+ ".rng": "application/ringing-tones",
+ ".rnx": "application/vndrn-realplayer",
+ ".roff": "application/x-troff",
+ ".rp": "image/vndrn-realpix",
+ ".rpm": "audio/x-pn-realaudio-plugin",
+ ".rt": "text/vndrn-realtext",
+ ".rtf": "text/richtext",
+ ".rtx": "text/richtext",
+ ".rv": "video/vndrn-realvideo",
+ ".s": "text/x-asm",
+ ".s3m": "audio/s3m",
+ ".s7z": "application/x-7z-compressed",
+ ".saveme": "application/octet-stream",
+ ".sbk": "application/x-tbook",
+ ".scm": "text/x-scriptscheme",
+ ".sdml": "text/plain",
+ ".sdp": "application/sdp",
+ ".sdr": "application/sounder",
+ ".sea": "application/sea",
+ ".set": "application/set",
+ ".sgm": "text/x-sgml",
+ ".sgml": "text/x-sgml",
+ ".sh": "text/x-scriptsh",
+ ".shar": "application/x-bsh",
+ ".shtml": "text/x-server-parsed-html",
+ ".sid": "audio/x-psid",
+ ".skd": "application/x-koan",
+ ".skm": "application/x-koan",
+ ".skp": "application/x-koan",
+ ".skt": "application/x-koan",
+ ".sit": "application/x-stuffit",
+ ".sitx": "application/x-stuffitx",
+ ".sl": "application/x-seelogo",
+ ".smi": "application/smil",
+ ".smil": "application/smil",
+ ".snd": "audio/basic",
+ ".sol": "application/solids",
+ ".spc": "text/x-speech",
+ ".spl": "application/futuresplash",
+ ".spr": "application/x-sprite",
+ ".sprite": "application/x-sprite",
+ ".spx": "audio/ogg",
+ ".src": "application/x-wais-source",
+ ".ssi": "text/x-server-parsed-html",
+ ".ssm": "application/streamingmedia",
+ ".sst": "application/vndms-pkicertstore",
+ ".step": "application/step",
+ ".stl": "application/sla",
+ ".stp": "application/step",
+ ".sv4cpio": "application/x-sv4cpio",
+ ".sv4crc": "application/x-sv4crc",
+ ".svf": "image/vnddwg",
+ ".svg": "image/svg+xml",
+ ".svr": "application/x-world",
+ ".swf": "application/x-shockwave-flash",
+ ".t": "application/x-troff",
+ ".talk": "text/x-speech",
+ ".tar": "application/x-tar",
+ ".tbk": "application/toolbook",
+ ".tcl": "text/x-scripttcl",
+ ".tcsh": "text/x-scripttcsh",
+ ".tex": "application/x-tex",
+ ".texi": "application/x-texinfo",
+ ".texinfo": "application/x-texinfo",
+ ".text": "text/plain",
+ ".tgz": "application/gnutar",
+ ".tif": "image/tiff",
+ ".tiff": "image/tiff",
+ ".tr": "application/x-troff",
+ ".tsi": "audio/tsp-audio",
+ ".tsp": "application/dsptype",
+ ".tsv": "text/tab-separated-values",
+ ".turbot": "image/florian",
+ ".txt": "text/plain",
+ ".uil": "text/x-uil",
+ ".uni": "text/uri-list",
+ ".unis": "text/uri-list",
+ ".unv": "application/i-deas",
+ ".uri": "text/uri-list",
+ ".uris": "text/uri-list",
+ ".ustar": "application/x-ustar",
+ ".uu": "text/x-uuencode",
+ ".uue": "text/x-uuencode",
+ ".vcd": "application/x-cdlink",
+ ".vcf": "text/x-vcard",
+ ".vcard": "text/x-vcard",
+ ".vcs": "text/x-vcalendar",
+ ".vda": "application/vda",
+ ".vdo": "video/vdo",
+ ".vew": "application/groupwise",
+ ".viv": "video/vivo",
+ ".vivo": "video/vivo",
+ ".vmd": "application/vocaltec-media-desc",
+ ".vmf": "application/vocaltec-media-file",
+ ".voc": "audio/voc",
+ ".vos": "video/vosaic",
+ ".vox": "audio/voxware",
+ ".vqe": "audio/x-twinvq-plugin",
+ ".vqf": "audio/x-twinvq",
+ ".vql": "audio/x-twinvq-plugin",
+ ".vrml": "application/x-vrml",
+ ".vrt": "x-world/x-vrt",
+ ".vsd": "application/x-visio",
+ ".vst": "application/x-visio",
+ ".vsw": "application/x-visio",
+ ".w60": "application/wordperfect60",
+ ".w61": "application/wordperfect61",
+ ".w6w": "application/msword",
+ ".wav": "audio/wav",
+ ".wb1": "application/x-qpro",
+ ".wbmp": "image/vnd.wap.wbmp",
+ ".web": "application/vndxara",
+ ".wiz": "application/msword",
+ ".wk1": "application/x-123",
+ ".wmf": "windows/metafile",
+ ".wml": "text/vnd.wap.wml",
+ ".wmlc": "application/vnd.wap.wmlc",
+ ".wmls": "text/vnd.wap.wmlscript",
+ ".wmlsc": "application/vnd.wap.wmlscriptc",
+ ".word": "application/msword",
+ ".wp5": "application/wordperfect",
+ ".wp6": "application/wordperfect",
+ ".wp": "application/wordperfect",
+ ".wpd": "application/wordperfect",
+ ".wq1": "application/x-lotus",
+ ".wri": "application/mswrite",
+ ".wrl": "application/x-world",
+ ".wrz": "model/vrml",
+ ".wsc": "text/scriplet",
+ ".wsrc": "application/x-wais-source",
+ ".wtk": "application/x-wintalk",
+ ".x-png": "image/png",
+ ".xbm": "image/x-xbitmap",
+ ".xdr": "video/x-amt-demorun",
+ ".xgz": "xgl/drawing",
+ ".xif": "image/vndxiff",
+ ".xl": "application/excel",
+ ".xla": "application/excel",
+ ".xlb": "application/excel",
+ ".xlc": "application/excel",
+ ".xld": "application/excel",
+ ".xlk": "application/excel",
+ ".xll": "application/excel",
+ ".xlm": "application/excel",
+ ".xls": "application/excel",
+ ".xlt": "application/excel",
+ ".xlv": "application/excel",
+ ".xlw": "application/excel",
+ ".xm": "audio/xm",
+ ".xml": "text/xml",
+ ".xmz": "xgl/movie",
+ ".xpix": "application/x-vndls-xpix",
+ ".xpm": "image/x-xpixmap",
+ ".xsr": "video/x-amt-showrun",
+ ".xwd": "image/x-xwd",
+ ".xyz": "chemical/x-pdb",
+ ".z": "application/x-compress",
+ ".zip": "application/zip",
+ ".zoo": "application/octet-stream",
+ ".zsh": "text/x-scriptzsh",
+ ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+ ".docm": "application/vnd.ms-word.document.macroEnabled.12",
+ ".dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
+ ".dotm": "application/vnd.ms-word.template.macroEnabled.12",
+ ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ ".xlsm": "application/vnd.ms-excel.sheet.macroEnabled.12",
+ ".xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
+ ".xltm": "application/vnd.ms-excel.template.macroEnabled.12",
+ ".xlsb": "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
+ ".xlam": "application/vnd.ms-excel.addin.macroEnabled.12",
+ ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
+ ".pptm": "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
+ ".ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
+ ".ppsm": "application/vnd.ms-powerpoint.slideshow.macroEnabled.12",
+ ".potx": "application/vnd.openxmlformats-officedocument.presentationml.template",
+ ".potm": "application/vnd.ms-powerpoint.template.macroEnabled.12",
+ ".ppam": "application/vnd.ms-powerpoint.addin.macroEnabled.12",
+ ".sldx": "application/vnd.openxmlformats-officedocument.presentationml.slide",
+ ".sldm": "application/vnd.ms-powerpoint.slide.macroEnabled.12",
+ ".thmx": "application/vnd.ms-officetheme",
+ ".onetoc": "application/onenote",
+ ".onetoc2": "application/onenote",
+ ".onetmp": "application/onenote",
+ ".onepkg": "application/onenote",
+ ".key": "application/x-iwork-keynote-sffkey",
+ ".kth": "application/x-iwork-keynote-sffkth",
+ ".nmbtemplate": "application/x-iwork-numbers-sfftemplate",
+ ".numbers": "application/x-iwork-numbers-sffnumbers",
+ ".pages": "application/x-iwork-pages-sffpages",
+ ".template": "application/x-iwork-pages-sfftemplate",
+ ".xpi": "application/x-xpinstall",
+ ".oex": "application/x-opera-extension",
+ ".mustache": "text/html",
+}
diff --git a/vendor/github.com/astaxie/beego/namespace.go b/vendor/github.com/astaxie/beego/namespace.go
new file mode 100644
index 0000000..72f22a7
--- /dev/null
+++ b/vendor/github.com/astaxie/beego/namespace.go
@@ -0,0 +1,396 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// 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 beego
+
+import (
+ "net/http"
+ "strings"
+
+ beecontext "github.com/astaxie/beego/context"
+)
+
+type namespaceCond func(*beecontext.Context) bool
+
+// LinkNamespace used as link action
+type LinkNamespace func(*Namespace)
+
+// Namespace is store all the info
+type Namespace struct {
+ prefix string
+ handlers *ControllerRegister
+}
+
+// NewNamespace get new Namespace
+func NewNamespace(prefix string, params ...LinkNamespace) *Namespace {
+ ns := &Namespace{
+ prefix: prefix,
+ handlers: NewControllerRegister(),
+ }
+ for _, p := range params {
+ p(ns)
+ }
+ return ns
+}
+
+// Cond set condition function
+// if cond return true can run this namespace, else can't
+// usage:
+// ns.Cond(func (ctx *context.Context) bool{
+// if ctx.Input.Domain() == "api.beego.me" {
+// return true
+// }
+// return false
+// })
+// Cond as the first filter
+func (n *Namespace) Cond(cond namespaceCond) *Namespace {
+ fn := func(ctx *beecontext.Context) {
+ if !cond(ctx) {
+ exception("405", ctx)
+ }
+ }
+ if v := n.handlers.filters[BeforeRouter]; len(v) > 0 {
+ mr := new(FilterRouter)
+ mr.tree = NewTree()
+ mr.pattern = "*"
+ mr.filterFunc = fn
+ mr.tree.AddRouter("*", true)
+ n.handlers.filters[BeforeRouter] = append([]*FilterRouter{mr}, v...)
+ } else {
+ n.handlers.InsertFilter("*", BeforeRouter, fn)
+ }
+ return n
+}
+
+// Filter add filter in the Namespace
+// action has before & after
+// FilterFunc
+// usage:
+// Filter("before", func (ctx *context.Context){
+// _, ok := ctx.Input.Session("uid").(int)
+// if !ok && ctx.Request.RequestURI != "/login" {
+// ctx.Redirect(302, "/login")
+// }
+// })
+func (n *Namespace) Filter(action string, filter ...FilterFunc) *Namespace {
+ var a int
+ if action == "before" {
+ a = BeforeRouter
+ } else if action == "after" {
+ a = FinishRouter
+ }
+ for _, f := range filter {
+ n.handlers.InsertFilter("*", a, f)
+ }
+ return n
+}
+
+// Router same as beego.Rourer
+// refer: https://godoc.org/github.com/astaxie/beego#Router
+func (n *Namespace) Router(rootpath string, c ControllerInterface, mappingMethods ...string) *Namespace {
+ n.handlers.Add(rootpath, c, mappingMethods...)
+ return n
+}
+
+// AutoRouter same as beego.AutoRouter
+// refer: https://godoc.org/github.com/astaxie/beego#AutoRouter
+func (n *Namespace) AutoRouter(c ControllerInterface) *Namespace {
+ n.handlers.AddAuto(c)
+ return n
+}
+
+// AutoPrefix same as beego.AutoPrefix
+// refer: https://godoc.org/github.com/astaxie/beego#AutoPrefix
+func (n *Namespace) AutoPrefix(prefix string, c ControllerInterface) *Namespace {
+ n.handlers.AddAutoPrefix(prefix, c)
+ return n
+}
+
+// Get same as beego.Get
+// refer: https://godoc.org/github.com/astaxie/beego#Get
+func (n *Namespace) Get(rootpath string, f FilterFunc) *Namespace {
+ n.handlers.Get(rootpath, f)
+ return n
+}
+
+// Post same as beego.Post
+// refer: https://godoc.org/github.com/astaxie/beego#Post
+func (n *Namespace) Post(rootpath string, f FilterFunc) *Namespace {
+ n.handlers.Post(rootpath, f)
+ return n
+}
+
+// Delete same as beego.Delete
+// refer: https://godoc.org/github.com/astaxie/beego#Delete
+func (n *Namespace) Delete(rootpath string, f FilterFunc) *Namespace {
+ n.handlers.Delete(rootpath, f)
+ return n
+}
+
+// Put same as beego.Put
+// refer: https://godoc.org/github.com/astaxie/beego#Put
+func (n *Namespace) Put(rootpath string, f FilterFunc) *Namespace {
+ n.handlers.Put(rootpath, f)
+ return n
+}
+
+// Head same as beego.Head
+// refer: https://godoc.org/github.com/astaxie/beego#Head
+func (n *Namespace) Head(rootpath string, f FilterFunc) *Namespace {
+ n.handlers.Head(rootpath, f)
+ return n
+}
+
+// Options same as beego.Options
+// refer: https://godoc.org/github.com/astaxie/beego#Options
+func (n *Namespace) Options(rootpath string, f FilterFunc) *Namespace {
+ n.handlers.Options(rootpath, f)
+ return n
+}
+
+// Patch same as beego.Patch
+// refer: https://godoc.org/github.com/astaxie/beego#Patch
+func (n *Namespace) Patch(rootpath string, f FilterFunc) *Namespace {
+ n.handlers.Patch(rootpath, f)
+ return n
+}
+
+// Any same as beego.Any
+// refer: https://godoc.org/github.com/astaxie/beego#Any
+func (n *Namespace) Any(rootpath string, f FilterFunc) *Namespace {
+ n.handlers.Any(rootpath, f)
+ return n
+}
+
+// Handler same as beego.Handler
+// refer: https://godoc.org/github.com/astaxie/beego#Handler
+func (n *Namespace) Handler(rootpath string, h http.Handler) *Namespace {
+ n.handlers.Handler(rootpath, h)
+ return n
+}
+
+// Include add include class
+// refer: https://godoc.org/github.com/astaxie/beego#Include
+func (n *Namespace) Include(cList ...ControllerInterface) *Namespace {
+ n.handlers.Include(cList...)
+ return n
+}
+
+// Namespace add nest Namespace
+// usage:
+//ns := beego.NewNamespace(“/v1”).
+//Namespace(
+// beego.NewNamespace("/shop").
+// Get("/:id", func(ctx *context.Context) {
+// ctx.Output.Body([]byte("shopinfo"))
+// }),
+// beego.NewNamespace("/order").
+// Get("/:id", func(ctx *context.Context) {
+// ctx.Output.Body([]byte("orderinfo"))
+// }),
+// beego.NewNamespace("/crm").
+// Get("/:id", func(ctx *context.Context) {
+// ctx.Output.Body([]byte("crminfo"))
+// }),
+//)
+func (n *Namespace) Namespace(ns ...*Namespace) *Namespace {
+ for _, ni := range ns {
+ for k, v := range ni.handlers.routers {
+ if t, ok := n.handlers.routers[k]; ok {
+ addPrefix(v, ni.prefix)
+ n.handlers.routers[k].AddTree(ni.prefix, v)
+ } else {
+ t = NewTree()
+ t.AddTree(ni.prefix, v)
+ addPrefix(t, ni.prefix)
+ n.handlers.routers[k] = t
+ }
+ }
+ if ni.handlers.enableFilter {
+ for pos, filterList := range ni.handlers.filters {
+ for _, mr := range filterList {
+ t := NewTree()
+ t.AddTree(ni.prefix, mr.tree)
+ mr.tree = t
+ n.handlers.insertFilterRouter(pos, mr)
+ }
+ }
+ }
+ }
+ return n
+}
+
+// AddNamespace register Namespace into beego.Handler
+// support multi Namespace
+func AddNamespace(nl ...*Namespace) {
+ for _, n := range nl {
+ for k, v := range n.handlers.routers {
+ if t, ok := BeeApp.Handlers.routers[k]; ok {
+ addPrefix(v, n.prefix)
+ BeeApp.Handlers.routers[k].AddTree(n.prefix, v)
+ } else {
+ t = NewTree()
+ t.AddTree(n.prefix, v)
+ addPrefix(t, n.prefix)
+ BeeApp.Handlers.routers[k] = t
+ }
+ }
+ if n.handlers.enableFilter {
+ for pos, filterList := range n.handlers.filters {
+ for _, mr := range filterList {
+ t := NewTree()
+ t.AddTree(n.prefix, mr.tree)
+ mr.tree = t
+ BeeApp.Handlers.insertFilterRouter(pos, mr)
+ }
+ }
+ }
+ }
+}
+
+func addPrefix(t *Tree, prefix string) {
+ for _, v := range t.fixrouters {
+ addPrefix(v, prefix)
+ }
+ if t.wildcard != nil {
+ addPrefix(t.wildcard, prefix)
+ }
+ for _, l := range t.leaves {
+ if c, ok := l.runObject.(*ControllerInfo); ok {
+ if !strings.HasPrefix(c.pattern, prefix) {
+ c.pattern = prefix + c.pattern
+ }
+ }
+ }
+}
+
+// NSCond is Namespace Condition
+func NSCond(cond namespaceCond) LinkNamespace {
+ return func(ns *Namespace) {
+ ns.Cond(cond)
+ }
+}
+
+// NSBefore Namespace BeforeRouter filter
+func NSBefore(filterList ...FilterFunc) LinkNamespace {
+ return func(ns *Namespace) {
+ ns.Filter("before", filterList...)
+ }
+}
+
+// NSAfter add Namespace FinishRouter filter
+func NSAfter(filterList ...FilterFunc) LinkNamespace {
+ return func(ns *Namespace) {
+ ns.Filter("after", filterList...)
+ }
+}
+
+// NSInclude Namespace Include ControllerInterface
+func NSInclude(cList ...ControllerInterface) LinkNamespace {
+ return func(ns *Namespace) {
+ ns.Include(cList...)
+ }
+}
+
+// NSRouter call Namespace Router
+func NSRouter(rootpath string, c ControllerInterface, mappingMethods ...string) LinkNamespace {
+ return func(ns *Namespace) {
+ ns.Router(rootpath, c, mappingMethods...)
+ }
+}
+
+// NSGet call Namespace Get
+func NSGet(rootpath string, f FilterFunc) LinkNamespace {
+ return func(ns *Namespace) {
+ ns.Get(rootpath, f)
+ }
+}
+
+// NSPost call Namespace Post
+func NSPost(rootpath string, f FilterFunc) LinkNamespace {
+ return func(ns *Namespace) {
+ ns.Post(rootpath, f)
+ }
+}
+
+// NSHead call Namespace Head
+func NSHead(rootpath string, f FilterFunc) LinkNamespace {
+ return func(ns *Namespace) {
+ ns.Head(rootpath, f)
+ }
+}
+
+// NSPut call Namespace Put
+func NSPut(rootpath string, f FilterFunc) LinkNamespace {
+ return func(ns *Namespace) {
+ ns.Put(rootpath, f)
+ }
+}
+
+// NSDelete call Namespace Delete
+func NSDelete(rootpath string, f FilterFunc) LinkNamespace {
+ return func(ns *Namespace) {
+ ns.Delete(rootpath, f)
+ }
+}
+
+// NSAny call Namespace Any
+func NSAny(rootpath string, f FilterFunc) LinkNamespace {
+ return func(ns *Namespace) {
+ ns.Any(rootpath, f)
+ }
+}
+
+// NSOptions call Namespace Options
+func NSOptions(rootpath string, f FilterFunc) LinkNamespace {
+ return func(ns *Namespace) {
+ ns.Options(rootpath, f)
+ }
+}
+
+// NSPatch call Namespace Patch
+func NSPatch(rootpath string, f FilterFunc) LinkNamespace {
+ return func(ns *Namespace) {
+ ns.Patch(rootpath, f)
+ }
+}
+
+// NSAutoRouter call Namespace AutoRouter
+func NSAutoRouter(c ControllerInterface) LinkNamespace {
+ return func(ns *Namespace) {
+ ns.AutoRouter(c)
+ }
+}
+
+// NSAutoPrefix call Namespace AutoPrefix
+func NSAutoPrefix(prefix string, c ControllerInterface) LinkNamespace {
+ return func(ns *Namespace) {
+ ns.AutoPrefix(prefix, c)
+ }
+}
+
+// NSNamespace add sub Namespace
+func NSNamespace(prefix string, params ...LinkNamespace) LinkNamespace {
+ return func(ns *Namespace) {
+ n := NewNamespace(prefix, params...)
+ ns.Namespace(n)
+ }
+}
+
+// NSHandler add handler
+func NSHandler(rootpath string, h http.Handler) LinkNamespace {
+ return func(ns *Namespace) {
+ ns.Handler(rootpath, h)
+ }
+}
diff --git a/vendor/github.com/astaxie/beego/parser.go b/vendor/github.com/astaxie/beego/parser.go
new file mode 100644
index 0000000..2ac48b8
--- /dev/null
+++ b/vendor/github.com/astaxie/beego/parser.go
@@ -0,0 +1,403 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// 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 beego
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "regexp"
+ "sort"
+ "strconv"
+ "strings"
+ "unicode"
+
+ "github.com/astaxie/beego/context/param"
+ "github.com/astaxie/beego/logs"
+ "github.com/astaxie/beego/utils"
+)
+
+var globalRouterTemplate = `package routers
+
+import (
+ "github.com/astaxie/beego"
+ "github.com/astaxie/beego/context/param"
+)
+
+func init() {
+{{.globalinfo}}
+}
+`
+
+var (
+ lastupdateFilename = "lastupdate.tmp"
+ commentFilename string
+ pkgLastupdate map[string]int64
+ genInfoList map[string][]ControllerComments
+)
+
+const commentPrefix = "commentsRouter_"
+
+func init() {
+ pkgLastupdate = make(map[string]int64)
+}
+
+func parserPkg(pkgRealpath, pkgpath string) error {
+ rep := strings.NewReplacer("\\", "_", "/", "_", ".", "_")
+ commentFilename, _ = filepath.Rel(AppPath, pkgRealpath)
+ commentFilename = commentPrefix + rep.Replace(commentFilename) + ".go"
+ if !compareFile(pkgRealpath) {
+ logs.Info(pkgRealpath + " no changed")
+ return nil
+ }
+ genInfoList = make(map[string][]ControllerComments)
+ fileSet := token.NewFileSet()
+ astPkgs, err := parser.ParseDir(fileSet, pkgRealpath, func(info os.FileInfo) bool {
+ name := info.Name()
+ return !info.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
+ }, parser.ParseComments)
+
+ if err != nil {
+ return err
+ }
+ for _, pkg := range astPkgs {
+ for _, fl := range pkg.Files {
+ for _, d := range fl.Decls {
+ switch specDecl := d.(type) {
+ case *ast.FuncDecl:
+ if specDecl.Recv != nil {
+ exp, ok := specDecl.Recv.List[0].Type.(*ast.StarExpr) // Check that the type is correct first beforing throwing to parser
+ if ok {
+ parserComments(specDecl, fmt.Sprint(exp.X), pkgpath)
+ }
+ }
+ }
+ }
+ }
+ }
+ genRouterCode(pkgRealpath)
+ savetoFile(pkgRealpath)
+ return nil
+}
+
+type parsedComment struct {
+ routerPath string
+ methods []string
+ params map[string]parsedParam
+}
+
+type parsedParam struct {
+ name string
+ datatype string
+ location string
+ defValue string
+ required bool
+}
+
+func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error {
+ if f.Doc != nil {
+ parsedComments, err := parseComment(f.Doc.List)
+ if err != nil {
+ return err
+ }
+ for _, parsedComment := range parsedComments {
+ if parsedComment.routerPath != "" {
+ key := pkgpath + ":" + controllerName
+ cc := ControllerComments{}
+ cc.Method = f.Name.String()
+ cc.Router = parsedComment.routerPath
+ cc.AllowHTTPMethods = parsedComment.methods
+ cc.MethodParams = buildMethodParams(f.Type.Params.List, parsedComment)
+ genInfoList[key] = append(genInfoList[key], cc)
+ }
+ }
+ }
+ return nil
+}
+
+func buildMethodParams(funcParams []*ast.Field, pc *parsedComment) []*param.MethodParam {
+ result := make([]*param.MethodParam, 0, len(funcParams))
+ for _, fparam := range funcParams {
+ for _, pName := range fparam.Names {
+ methodParam := buildMethodParam(fparam, pName.Name, pc)
+ result = append(result, methodParam)
+ }
+ }
+ return result
+}
+
+func buildMethodParam(fparam *ast.Field, name string, pc *parsedComment) *param.MethodParam {
+ options := []param.MethodParamOption{}
+ if cparam, ok := pc.params[name]; ok {
+ //Build param from comment info
+ name = cparam.name
+ if cparam.required {
+ options = append(options, param.IsRequired)
+ }
+ switch cparam.location {
+ case "body":
+ options = append(options, param.InBody)
+ case "header":
+ options = append(options, param.InHeader)
+ case "path":
+ options = append(options, param.InPath)
+ }
+ if cparam.defValue != "" {
+ options = append(options, param.Default(cparam.defValue))
+ }
+ } else {
+ if paramInPath(name, pc.routerPath) {
+ options = append(options, param.InPath)
+ }
+ }
+ return param.New(name, options...)
+}
+
+func paramInPath(name, route string) bool {
+ return strings.HasSuffix(route, ":"+name) ||
+ strings.Contains(route, ":"+name+"/")
+}
+
+var routeRegex = regexp.MustCompile(`@router\s+(\S+)(?:\s+\[(\S+)\])?`)
+
+func parseComment(lines []*ast.Comment) (pcs []*parsedComment, err error) {
+ pcs = []*parsedComment{}
+ params := map[string]parsedParam{}
+
+ for _, c := range lines {
+ t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
+ if strings.HasPrefix(t, "@Param") {
+ pv := getparams(strings.TrimSpace(strings.TrimLeft(t, "@Param")))
+ if len(pv) < 4 {
+ logs.Error("Invalid @Param format. Needs at least 4 parameters")
+ }
+ p := parsedParam{}
+ names := strings.SplitN(pv[0], "=>", 2)
+ p.name = names[0]
+ funcParamName := p.name
+ if len(names) > 1 {
+ funcParamName = names[1]
+ }
+ p.location = pv[1]
+ p.datatype = pv[2]
+ switch len(pv) {
+ case 5:
+ p.required, _ = strconv.ParseBool(pv[3])
+ case 6:
+ p.defValue = pv[3]
+ p.required, _ = strconv.ParseBool(pv[4])
+ }
+ params[funcParamName] = p
+ }
+ }
+
+ for _, c := range lines {
+ var pc = &parsedComment{}
+ pc.params = params
+
+ t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
+ if strings.HasPrefix(t, "@router") {
+ t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
+ matches := routeRegex.FindStringSubmatch(t)
+ if len(matches) == 3 {
+ pc.routerPath = matches[1]
+ methods := matches[2]
+ if methods == "" {
+ pc.methods = []string{"get"}
+ //pc.hasGet = true
+ } else {
+ pc.methods = strings.Split(methods, ",")
+ //pc.hasGet = strings.Contains(methods, "get")
+ }
+ pcs = append(pcs, pc)
+ } else {
+ return nil, errors.New("Router information is missing")
+ }
+ }
+ }
+ return
+}
+
+// direct copy from bee\g_docs.go
+// analysis params return []string
+// @Param query form string true "The email for login"
+// [query form string true "The email for login"]
+func getparams(str string) []string {
+ var s []rune
+ var j int
+ var start bool
+ var r []string
+ var quoted int8
+ for _, c := range str {
+ if unicode.IsSpace(c) && quoted == 0 {
+ if !start {
+ continue
+ } else {
+ start = false
+ j++
+ r = append(r, string(s))
+ s = make([]rune, 0)
+ continue
+ }
+ }
+
+ start = true
+ if c == '"' {
+ quoted ^= 1
+ continue
+ }
+ s = append(s, c)
+ }
+ if len(s) > 0 {
+ r = append(r, string(s))
+ }
+ return r
+}
+
+func genRouterCode(pkgRealpath string) {
+ os.Mkdir(getRouterDir(pkgRealpath), 0755)
+ logs.Info("generate router from comments")
+ var (
+ globalinfo string
+ sortKey []string
+ )
+ for k := range genInfoList {
+ sortKey = append(sortKey, k)
+ }
+ sort.Strings(sortKey)
+ for _, k := range sortKey {
+ cList := genInfoList[k]
+ sort.Sort(ControllerCommentsSlice(cList))
+ for _, c := range cList {
+ allmethod := "nil"
+ if len(c.AllowHTTPMethods) > 0 {
+ allmethod = "[]string{"
+ for _, m := range c.AllowHTTPMethods {
+ allmethod += `"` + m + `",`
+ }
+ allmethod = strings.TrimRight(allmethod, ",") + "}"
+ }
+ params := "nil"
+ if len(c.Params) > 0 {
+ params = "[]map[string]string{"
+ for _, p := range c.Params {
+ for k, v := range p {
+ params = params + `map[string]string{` + k + `:"` + v + `"},`
+ }
+ }
+ params = strings.TrimRight(params, ",") + "}"
+ }
+ methodParams := "param.Make("
+ if len(c.MethodParams) > 0 {
+ lines := make([]string, 0, len(c.MethodParams))
+ for _, m := range c.MethodParams {
+ lines = append(lines, fmt.Sprint(m))
+ }
+ methodParams += "\n " +
+ strings.Join(lines, ",\n ") +
+ ",\n "
+ }
+ methodParams += ")"
+ globalinfo = globalinfo + `
+ beego.GlobalControllerRouter["` + k + `"] = append(beego.GlobalControllerRouter["` + k + `"],
+ beego.ControllerComments{
+ Method: "` + strings.TrimSpace(c.Method) + `",
+ ` + "Router: `" + c.Router + "`" + `,
+ AllowHTTPMethods: ` + allmethod + `,
+ MethodParams: ` + methodParams + `,
+ Params: ` + params + `})
+`
+ }
+ }
+ if globalinfo != "" {
+ f, err := os.Create(filepath.Join(getRouterDir(pkgRealpath), commentFilename))
+ if err != nil {
+ panic(err)
+ }
+ defer f.Close()
+ f.WriteString(strings.Replace(globalRouterTemplate, "{{.globalinfo}}", globalinfo, -1))
+ }
+}
+
+func compareFile(pkgRealpath string) bool {
+ if !utils.FileExists(filepath.Join(getRouterDir(pkgRealpath), commentFilename)) {
+ return true
+ }
+ if utils.FileExists(lastupdateFilename) {
+ content, err := ioutil.ReadFile(lastupdateFilename)
+ if err != nil {
+ return true
+ }
+ json.Unmarshal(content, &pkgLastupdate)
+ lastupdate, err := getpathTime(pkgRealpath)
+ if err != nil {
+ return true
+ }
+ if v, ok := pkgLastupdate[pkgRealpath]; ok {
+ if lastupdate <= v {
+ return false
+ }
+ }
+ }
+ return true
+}
+
+func savetoFile(pkgRealpath string) {
+ lastupdate, err := getpathTime(pkgRealpath)
+ if err != nil {
+ return
+ }
+ pkgLastupdate[pkgRealpath] = lastupdate
+ d, err := json.Marshal(pkgLastupdate)
+ if err != nil {
+ return
+ }
+ ioutil.WriteFile(lastupdateFilename, d, os.ModePerm)
+}
+
+func getpathTime(pkgRealpath string) (lastupdate int64, err error) {
+ fl, err := ioutil.ReadDir(pkgRealpath)
+ if err != nil {
+ return lastupdate, err
+ }
+ for _, f := range fl {
+ if lastupdate < f.ModTime().UnixNano() {
+ lastupdate = f.ModTime().UnixNano()
+ }
+ }
+ return lastupdate, nil
+}
+
+func getRouterDir(pkgRealpath string) string {
+ dir := filepath.Dir(pkgRealpath)
+ for {
+ d := filepath.Join(dir, "routers")
+ if utils.FileExists(d) {
+ return d
+ }
+
+ if r, _ := filepath.Rel(dir, AppPath); r == "." {
+ return d
+ }
+ // Parent dir.
+ dir = filepath.Dir(dir)
+ }
+}
diff --git a/vendor/github.com/astaxie/beego/policy.go b/vendor/github.com/astaxie/beego/policy.go
new file mode 100644
index 0000000..ab23f92
--- /dev/null
+++ b/vendor/github.com/astaxie/beego/policy.go
@@ -0,0 +1,97 @@
+// Copyright 2016 beego authors. All Rights Reserved.
+//
+// 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 beego
+
+import (
+ "strings"
+
+ "github.com/astaxie/beego/context"
+)
+
+// PolicyFunc defines a policy function which is invoked before the controller handler is executed.
+type PolicyFunc func(*context.Context)
+
+// FindPolicy Find Router info for URL
+func (p *ControllerRegister) FindPolicy(cont *context.Context) []PolicyFunc {
+ var urlPath = cont.Input.URL()
+ if !BConfig.RouterCaseSensitive {
+ urlPath = strings.ToLower(urlPath)
+ }
+ httpMethod := cont.Input.Method()
+ isWildcard := false
+ // Find policy for current method
+ t, ok := p.policies[httpMethod]
+ // If not found - find policy for whole controller
+ if !ok {
+ t, ok = p.policies["*"]
+ isWildcard = true
+ }
+ if ok {
+ runObjects := t.Match(urlPath, cont)
+ if r, ok := runObjects.([]PolicyFunc); ok {
+ return r
+ } else if !isWildcard {
+ // If no policies found and we checked not for "*" method - try to find it
+ t, ok = p.policies["*"]
+ if ok {
+ runObjects = t.Match(urlPath, cont)
+ if r, ok = runObjects.([]PolicyFunc); ok {
+ return r
+ }
+ }
+ }
+ }
+ return nil
+}
+
+func (p *ControllerRegister) addToPolicy(method, pattern string, r ...PolicyFunc) {
+ method = strings.ToUpper(method)
+ p.enablePolicy = true
+ if !BConfig.RouterCaseSensitive {
+ pattern = strings.ToLower(pattern)
+ }
+ if t, ok := p.policies[method]; ok {
+ t.AddRouter(pattern, r)
+ } else {
+ t := NewTree()
+ t.AddRouter(pattern, r)
+ p.policies[method] = t
+ }
+}
+
+// Policy Register new policy in beego
+func Policy(pattern, method string, policy ...PolicyFunc) {
+ BeeApp.Handlers.addToPolicy(method, pattern, policy...)
+}
+
+// Find policies and execute if were found
+func (p *ControllerRegister) execPolicy(cont *context.Context, urlPath string) (started bool) {
+ if !p.enablePolicy {
+ return false
+ }
+ // Find Policy for method
+ policyList := p.FindPolicy(cont)
+ if len(policyList) > 0 {
+ // Run policies
+ for _, runPolicy := range policyList {
+ runPolicy(cont)
+ if cont.ResponseWriter.Started {
+ return true
+ }
+ }
+ return false
+ }
+ return false
+}
diff --git a/vendor/github.com/astaxie/beego/router.go b/vendor/github.com/astaxie/beego/router.go
new file mode 100644
index 0000000..c6b08e2
--- /dev/null
+++ b/vendor/github.com/astaxie/beego/router.go
@@ -0,0 +1,993 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// 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 beego
+
+import (
+ "fmt"
+ "net/http"
+ "path"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ beecontext "github.com/astaxie/beego/context"
+ "github.com/astaxie/beego/context/param"
+ "github.com/astaxie/beego/logs"
+ "github.com/astaxie/beego/toolbox"
+ "github.com/astaxie/beego/utils"
+)
+
+// default filter execution points
+const (
+ BeforeStatic = iota
+ BeforeRouter
+ BeforeExec
+ AfterExec
+ FinishRouter
+)
+
+const (
+ routerTypeBeego = iota
+ routerTypeRESTFul
+ routerTypeHandler
+)
+
+var (
+ // HTTPMETHOD list the supported http methods.
+ HTTPMETHOD = map[string]bool{
+ "GET": true,
+ "POST": true,
+ "PUT": true,
+ "DELETE": true,
+ "PATCH": true,
+ "OPTIONS": true,
+ "HEAD": true,
+ "TRACE": true,
+ "CONNECT": true,
+ "MKCOL": true,
+ "COPY": true,
+ "MOVE": true,
+ "PROPFIND": true,
+ "PROPPATCH": true,
+ "LOCK": true,
+ "UNLOCK": true,
+ }
+ // these beego.Controller's methods shouldn't reflect to AutoRouter
+ exceptMethod = []string{"Init", "Prepare", "Finish", "Render", "RenderString",
+ "RenderBytes", "Redirect", "Abort", "StopRun", "UrlFor", "ServeJSON", "ServeJSONP",
+ "ServeXML", "Input", "ParseForm", "GetString", "GetStrings", "GetInt", "GetBool",
+ "GetFloat", "GetFile", "SaveToFile", "StartSession", "SetSession", "GetSession",
+ "DelSession", "SessionRegenerateID", "DestroySession", "IsAjax", "GetSecureCookie",
+ "SetSecureCookie", "XsrfToken", "CheckXsrfCookie", "XsrfFormHtml",
+ "GetControllerAndAction", "ServeFormatted"}
+
+ urlPlaceholder = "{{placeholder}}"
+ // DefaultAccessLogFilter will skip the accesslog if return true
+ DefaultAccessLogFilter FilterHandler = &logFilter{}
+)
+
+// FilterHandler is an interface for
+type FilterHandler interface {
+ Filter(*beecontext.Context) bool
+}
+
+// default log filter static file will not show
+type logFilter struct {
+}
+
+func (l *logFilter) Filter(ctx *beecontext.Context) bool {
+ requestPath := path.Clean(ctx.Request.URL.Path)
+ if requestPath == "/favicon.ico" || requestPath == "/robots.txt" {
+ return true
+ }
+ for prefix := range BConfig.WebConfig.StaticDir {
+ if strings.HasPrefix(requestPath, prefix) {
+ return true
+ }
+ }
+ return false
+}
+
+// ExceptMethodAppend to append a slice's value into "exceptMethod", for controller's methods shouldn't reflect to AutoRouter
+func ExceptMethodAppend(action string) {
+ exceptMethod = append(exceptMethod, action)
+}
+
+// ControllerInfo holds information about the controller.
+type ControllerInfo struct {
+ pattern string
+ controllerType reflect.Type
+ methods map[string]string
+ handler http.Handler
+ runFunction FilterFunc
+ routerType int
+ initialize func() ControllerInterface
+ methodParams []*param.MethodParam
+}
+
+// ControllerRegister containers registered router rules, controller handlers and filters.
+type ControllerRegister struct {
+ routers map[string]*Tree
+ enablePolicy bool
+ policies map[string]*Tree
+ enableFilter bool
+ filters [FinishRouter + 1][]*FilterRouter
+ pool sync.Pool
+}
+
+// NewControllerRegister returns a new ControllerRegister.
+func NewControllerRegister() *ControllerRegister {
+ cr := &ControllerRegister{
+ routers: make(map[string]*Tree),
+ policies: make(map[string]*Tree),
+ }
+ cr.pool.New = func() interface{} {
+ return beecontext.NewContext()
+ }
+ return cr
+}
+
+// Add controller handler and pattern rules to ControllerRegister.
+// usage:
+// default methods is the same name as method
+// Add("/user",&UserController{})
+// Add("/api/list",&RestController{},"*:ListFood")
+// Add("/api/create",&RestController{},"post:CreateFood")
+// Add("/api/update",&RestController{},"put:UpdateFood")
+// Add("/api/delete",&RestController{},"delete:DeleteFood")
+// Add("/api",&RestController{},"get,post:ApiFunc"
+// Add("/simple",&SimpleController{},"get:GetFunc;post:PostFunc")
+func (p *ControllerRegister) Add(pattern string, c ControllerInterface, mappingMethods ...string) {
+ p.addWithMethodParams(pattern, c, nil, mappingMethods...)
+}
+
+func (p *ControllerRegister) addWithMethodParams(pattern string, c ControllerInterface, methodParams []*param.MethodParam, mappingMethods ...string) {
+ reflectVal := reflect.ValueOf(c)
+ t := reflect.Indirect(reflectVal).Type()
+ methods := make(map[string]string)
+ if len(mappingMethods) > 0 {
+ semi := strings.Split(mappingMethods[0], ";")
+ for _, v := range semi {
+ colon := strings.Split(v, ":")
+ if len(colon) != 2 {
+ panic("method mapping format is invalid")
+ }
+ comma := strings.Split(colon[0], ",")
+ for _, m := range comma {
+ if m == "*" || HTTPMETHOD[strings.ToUpper(m)] {
+ if val := reflectVal.MethodByName(colon[1]); val.IsValid() {
+ methods[strings.ToUpper(m)] = colon[1]
+ } else {
+ panic("'" + colon[1] + "' method doesn't exist in the controller " + t.Name())
+ }
+ } else {
+ panic(v + " is an invalid method mapping. Method doesn't exist " + m)
+ }
+ }
+ }
+ }
+
+ route := &ControllerInfo{}
+ route.pattern = pattern
+ route.methods = methods
+ route.routerType = routerTypeBeego
+ route.controllerType = t
+ route.initialize = func() ControllerInterface {
+ vc := reflect.New(route.controllerType)
+ execController, ok := vc.Interface().(ControllerInterface)
+ if !ok {
+ panic("controller is not ControllerInterface")
+ }
+
+ elemVal := reflect.ValueOf(c).Elem()
+ elemType := reflect.TypeOf(c).Elem()
+ execElem := reflect.ValueOf(execController).Elem()
+
+ numOfFields := elemVal.NumField()
+ for i := 0; i < numOfFields; i++ {
+ fieldType := elemType.Field(i)
+ elemField := execElem.FieldByName(fieldType.Name)
+ if elemField.CanSet() {
+ fieldVal := elemVal.Field(i)
+ elemField.Set(fieldVal)
+ }
+ }
+
+ return execController
+ }
+
+ route.methodParams = methodParams
+ if len(methods) == 0 {
+ for m := range HTTPMETHOD {
+ p.addToRouter(m, pattern, route)
+ }
+ } else {
+ for k := range methods {
+ if k == "*" {
+ for m := range HTTPMETHOD {
+ p.addToRouter(m, pattern, route)
+ }
+ } else {
+ p.addToRouter(k, pattern, route)
+ }
+ }
+ }
+}
+
+func (p *ControllerRegister) addToRouter(method, pattern string, r *ControllerInfo) {
+ if !BConfig.RouterCaseSensitive {
+ pattern = strings.ToLower(pattern)
+ }
+ if t, ok := p.routers[method]; ok {
+ t.AddRouter(pattern, r)
+ } else {
+ t := NewTree()
+ t.AddRouter(pattern, r)
+ p.routers[method] = t
+ }
+}
+
+// Include only when the Runmode is dev will generate router file in the router/auto.go from the controller
+// Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{})
+func (p *ControllerRegister) Include(cList ...ControllerInterface) {
+ if BConfig.RunMode == DEV {
+ skip := make(map[string]bool, 10)
+ for _, c := range cList {
+ reflectVal := reflect.ValueOf(c)
+ t := reflect.Indirect(reflectVal).Type()
+ wgopath := utils.GetGOPATHs()
+ if len(wgopath) == 0 {
+ panic("you are in dev mode. So please set gopath")
+ }
+ pkgpath := ""
+ for _, wg := range wgopath {
+ wg, _ = filepath.EvalSymlinks(filepath.Join(wg, "src", t.PkgPath()))
+ if utils.FileExists(wg) {
+ pkgpath = wg
+ break
+ }
+ }
+ if pkgpath != "" {
+ if _, ok := skip[pkgpath]; !ok {
+ skip[pkgpath] = true
+ parserPkg(pkgpath, t.PkgPath())
+ }
+ }
+ }
+ }
+ for _, c := range cList {
+ reflectVal := reflect.ValueOf(c)
+ t := reflect.Indirect(reflectVal).Type()
+ key := t.PkgPath() + ":" + t.Name()
+ if comm, ok := GlobalControllerRouter[key]; ok {
+ for _, a := range comm {
+ p.addWithMethodParams(a.Router, c, a.MethodParams, strings.Join(a.AllowHTTPMethods, ",")+":"+a.Method)
+ }
+ }
+ }
+}
+
+// Get add get method
+// usage:
+// Get("/", func(ctx *context.Context){
+// ctx.Output.Body("hello world")
+// })
+func (p *ControllerRegister) Get(pattern string, f FilterFunc) {
+ p.AddMethod("get", pattern, f)
+}
+
+// Post add post method
+// usage:
+// Post("/api", func(ctx *context.Context){
+// ctx.Output.Body("hello world")
+// })
+func (p *ControllerRegister) Post(pattern string, f FilterFunc) {
+ p.AddMethod("post", pattern, f)
+}
+
+// Put add put method
+// usage:
+// Put("/api/:id", func(ctx *context.Context){
+// ctx.Output.Body("hello world")
+// })
+func (p *ControllerRegister) Put(pattern string, f FilterFunc) {
+ p.AddMethod("put", pattern, f)
+}
+
+// Delete add delete method
+// usage:
+// Delete("/api/:id", func(ctx *context.Context){
+// ctx.Output.Body("hello world")
+// })
+func (p *ControllerRegister) Delete(pattern string, f FilterFunc) {
+ p.AddMethod("delete", pattern, f)
+}
+
+// Head add head method
+// usage:
+// Head("/api/:id", func(ctx *context.Context){
+// ctx.Output.Body("hello world")
+// })
+func (p *ControllerRegister) Head(pattern string, f FilterFunc) {
+ p.AddMethod("head", pattern, f)
+}
+
+// Patch add patch method
+// usage:
+// Patch("/api/:id", func(ctx *context.Context){
+// ctx.Output.Body("hello world")
+// })
+func (p *ControllerRegister) Patch(pattern string, f FilterFunc) {
+ p.AddMethod("patch", pattern, f)
+}
+
+// Options add options method
+// usage:
+// Options("/api/:id", func(ctx *context.Context){
+// ctx.Output.Body("hello world")
+// })
+func (p *ControllerRegister) Options(pattern string, f FilterFunc) {
+ p.AddMethod("options", pattern, f)
+}
+
+// Any add all method
+// usage:
+// Any("/api/:id", func(ctx *context.Context){
+// ctx.Output.Body("hello world")
+// })
+func (p *ControllerRegister) Any(pattern string, f FilterFunc) {
+ p.AddMethod("*", pattern, f)
+}
+
+// AddMethod add http method router
+// usage:
+// AddMethod("get","/api/:id", func(ctx *context.Context){
+// ctx.Output.Body("hello world")
+// })
+func (p *ControllerRegister) AddMethod(method, pattern string, f FilterFunc) {
+ method = strings.ToUpper(method)
+ if method != "*" && !HTTPMETHOD[method] {
+ panic("not support http method: " + method)
+ }
+ route := &ControllerInfo{}
+ route.pattern = pattern
+ route.routerType = routerTypeRESTFul
+ route.runFunction = f
+ methods := make(map[string]string)
+ if method == "*" {
+ for val := range HTTPMETHOD {
+ methods[val] = val
+ }
+ } else {
+ methods[method] = method
+ }
+ route.methods = methods
+ for k := range methods {
+ if k == "*" {
+ for m := range HTTPMETHOD {
+ p.addToRouter(m, pattern, route)
+ }
+ } else {
+ p.addToRouter(k, pattern, route)
+ }
+ }
+}
+
+// Handler add user defined Handler
+func (p *ControllerRegister) Handler(pattern string, h http.Handler, options ...interface{}) {
+ route := &ControllerInfo{}
+ route.pattern = pattern
+ route.routerType = routerTypeHandler
+ route.handler = h
+ if len(options) > 0 {
+ if _, ok := options[0].(bool); ok {
+ pattern = path.Join(pattern, "?:all(.*)")
+ }
+ }
+ for m := range HTTPMETHOD {
+ p.addToRouter(m, pattern, route)
+ }
+}
+
+// AddAuto router to ControllerRegister.
+// example beego.AddAuto(&MainContorlller{}),
+// MainController has method List and Page.
+// visit the url /main/list to execute List function
+// /main/page to execute Page function.
+func (p *ControllerRegister) AddAuto(c ControllerInterface) {
+ p.AddAutoPrefix("/", c)
+}
+
+// AddAutoPrefix Add auto router to ControllerRegister with prefix.
+// example beego.AddAutoPrefix("/admin",&MainContorlller{}),
+// MainController has method List and Page.
+// visit the url /admin/main/list to execute List function
+// /admin/main/page to execute Page function.
+func (p *ControllerRegister) AddAutoPrefix(prefix string, c ControllerInterface) {
+ reflectVal := reflect.ValueOf(c)
+ rt := reflectVal.Type()
+ ct := reflect.Indirect(reflectVal).Type()
+ controllerName := strings.TrimSuffix(ct.Name(), "Controller")
+ for i := 0; i < rt.NumMethod(); i++ {
+ if !utils.InSlice(rt.Method(i).Name, exceptMethod) {
+ route := &ControllerInfo{}
+ route.routerType = routerTypeBeego
+ route.methods = map[string]string{"*": rt.Method(i).Name}
+ route.controllerType = ct
+ pattern := path.Join(prefix, strings.ToLower(controllerName), strings.ToLower(rt.Method(i).Name), "*")
+ patternInit := path.Join(prefix, controllerName, rt.Method(i).Name, "*")
+ patternFix := path.Join(prefix, strings.ToLower(controllerName), strings.ToLower(rt.Method(i).Name))
+ patternFixInit := path.Join(prefix, controllerName, rt.Method(i).Name)
+ route.pattern = pattern
+ for m := range HTTPMETHOD {
+ p.addToRouter(m, pattern, route)
+ p.addToRouter(m, patternInit, route)
+ p.addToRouter(m, patternFix, route)
+ p.addToRouter(m, patternFixInit, route)
+ }
+ }
+ }
+}
+
+// InsertFilter Add a FilterFunc with pattern rule and action constant.
+// params is for:
+// 1. setting the returnOnOutput value (false allows multiple filters to execute)
+// 2. determining whether or not params need to be reset.
+func (p *ControllerRegister) InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) error {
+ mr := &FilterRouter{
+ tree: NewTree(),
+ pattern: pattern,
+ filterFunc: filter,
+ returnOnOutput: true,
+ }
+ if !BConfig.RouterCaseSensitive {
+ mr.pattern = strings.ToLower(pattern)
+ }
+
+ paramsLen := len(params)
+ if paramsLen > 0 {
+ mr.returnOnOutput = params[0]
+ }
+ if paramsLen > 1 {
+ mr.resetParams = params[1]
+ }
+ mr.tree.AddRouter(pattern, true)
+ return p.insertFilterRouter(pos, mr)
+}
+
+// add Filter into
+func (p *ControllerRegister) insertFilterRouter(pos int, mr *FilterRouter) (err error) {
+ if pos < BeforeStatic || pos > FinishRouter {
+ err = fmt.Errorf("can not find your filter position")
+ return
+ }
+ p.enableFilter = true
+ p.filters[pos] = append(p.filters[pos], mr)
+ return nil
+}
+
+// URLFor does another controller handler in this request function.
+// it can access any controller method.
+func (p *ControllerRegister) URLFor(endpoint string, values ...interface{}) string {
+ paths := strings.Split(endpoint, ".")
+ if len(paths) <= 1 {
+ logs.Warn("urlfor endpoint must like path.controller.method")
+ return ""
+ }
+ if len(values)%2 != 0 {
+ logs.Warn("urlfor params must key-value pair")
+ return ""
+ }
+ params := make(map[string]string)
+ if len(values) > 0 {
+ key := ""
+ for k, v := range values {
+ if k%2 == 0 {
+ key = fmt.Sprint(v)
+ } else {
+ params[key] = fmt.Sprint(v)
+ }
+ }
+ }
+ controllName := strings.Join(paths[:len(paths)-1], "/")
+ methodName := paths[len(paths)-1]
+ for m, t := range p.routers {
+ ok, url := p.geturl(t, "/", controllName, methodName, params, m)
+ if ok {
+ return url
+ }
+ }
+ return ""
+}
+
+func (p *ControllerRegister) geturl(t *Tree, url, controllName, methodName string, params map[string]string, httpMethod string) (bool, string) {
+ for _, subtree := range t.fixrouters {
+ u := path.Join(url, subtree.prefix)
+ ok, u := p.geturl(subtree, u, controllName, methodName, params, httpMethod)
+ if ok {
+ return ok, u
+ }
+ }
+ if t.wildcard != nil {
+ u := path.Join(url, urlPlaceholder)
+ ok, u := p.geturl(t.wildcard, u, controllName, methodName, params, httpMethod)
+ if ok {
+ return ok, u
+ }
+ }
+ for _, l := range t.leaves {
+ if c, ok := l.runObject.(*ControllerInfo); ok {
+ if c.routerType == routerTypeBeego &&
+ strings.HasSuffix(path.Join(c.controllerType.PkgPath(), c.controllerType.Name()), controllName) {
+ find := false
+ if HTTPMETHOD[strings.ToUpper(methodName)] {
+ if len(c.methods) == 0 {
+ find = true
+ } else if m, ok := c.methods[strings.ToUpper(methodName)]; ok && m == strings.ToUpper(methodName) {
+ find = true
+ } else if m, ok = c.methods["*"]; ok && m == methodName {
+ find = true
+ }
+ }
+ if !find {
+ for m, md := range c.methods {
+ if (m == "*" || m == httpMethod) && md == methodName {
+ find = true
+ }
+ }
+ }
+ if find {
+ if l.regexps == nil {
+ if len(l.wildcards) == 0 {
+ return true, strings.Replace(url, "/"+urlPlaceholder, "", 1) + toURL(params)
+ }
+ if len(l.wildcards) == 1 {
+ if v, ok := params[l.wildcards[0]]; ok {
+ delete(params, l.wildcards[0])
+ return true, strings.Replace(url, urlPlaceholder, v, 1) + toURL(params)
+ }
+ return false, ""
+ }
+ if len(l.wildcards) == 3 && l.wildcards[0] == "." {
+ if p, ok := params[":path"]; ok {
+ if e, isok := params[":ext"]; isok {
+ delete(params, ":path")
+ delete(params, ":ext")
+ return true, strings.Replace(url, urlPlaceholder, p+"."+e, -1) + toURL(params)
+ }
+ }
+ }
+ canskip := false
+ for _, v := range l.wildcards {
+ if v == ":" {
+ canskip = true
+ continue
+ }
+ if u, ok := params[v]; ok {
+ delete(params, v)
+ url = strings.Replace(url, urlPlaceholder, u, 1)
+ } else {
+ if canskip {
+ canskip = false
+ continue
+ }
+ return false, ""
+ }
+ }
+ return true, url + toURL(params)
+ }
+ var i int
+ var startreg bool
+ regurl := ""
+ for _, v := range strings.Trim(l.regexps.String(), "^$") {
+ if v == '(' {
+ startreg = true
+ continue
+ } else if v == ')' {
+ startreg = false
+ if v, ok := params[l.wildcards[i]]; ok {
+ delete(params, l.wildcards[i])
+ regurl = regurl + v
+ i++
+ } else {
+ break
+ }
+ } else if !startreg {
+ regurl = string(append([]rune(regurl), v))
+ }
+ }
+ if l.regexps.MatchString(regurl) {
+ ps := strings.Split(regurl, "/")
+ for _, p := range ps {
+ url = strings.Replace(url, urlPlaceholder, p, 1)
+ }
+ return true, url + toURL(params)
+ }
+ }
+ }
+ }
+ }
+
+ return false, ""
+}
+
+func (p *ControllerRegister) execFilter(context *beecontext.Context, urlPath string, pos int) (started bool) {
+ var preFilterParams map[string]string
+ for _, filterR := range p.filters[pos] {
+ if filterR.returnOnOutput && context.ResponseWriter.Started {
+ return true
+ }
+ if filterR.resetParams {
+ preFilterParams = context.Input.Params()
+ }
+ if ok := filterR.ValidRouter(urlPath, context); ok {
+ filterR.filterFunc(context)
+ if filterR.resetParams {
+ context.Input.ResetParams()
+ for k, v := range preFilterParams {
+ context.Input.SetParam(k, v)
+ }
+ }
+ }
+ if filterR.returnOnOutput && context.ResponseWriter.Started {
+ return true
+ }
+ }
+ return false
+}
+
+// Implement http.Handler interface.
+func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
+ startTime := time.Now()
+ var (
+ runRouter reflect.Type
+ findRouter bool
+ runMethod string
+ methodParams []*param.MethodParam
+ routerInfo *ControllerInfo
+ isRunnable bool
+ )
+ context := p.pool.Get().(*beecontext.Context)
+ context.Reset(rw, r)
+
+ defer p.pool.Put(context)
+ if BConfig.RecoverFunc != nil {
+ defer BConfig.RecoverFunc(context)
+ }
+
+ context.Output.EnableGzip = BConfig.EnableGzip
+
+ if BConfig.RunMode == DEV {
+ context.Output.Header("Server", BConfig.ServerName)
+ }
+
+ var urlPath = r.URL.Path
+
+ if !BConfig.RouterCaseSensitive {
+ urlPath = strings.ToLower(urlPath)
+ }
+
+ // filter wrong http method
+ if !HTTPMETHOD[r.Method] {
+ http.Error(rw, "Method Not Allowed", 405)
+ goto Admin
+ }
+
+ // filter for static file
+ if len(p.filters[BeforeStatic]) > 0 && p.execFilter(context, urlPath, BeforeStatic) {
+ goto Admin
+ }
+
+ serverStaticRouter(context)
+
+ if context.ResponseWriter.Started {
+ findRouter = true
+ goto Admin
+ }
+
+ if r.Method != http.MethodGet && r.Method != http.MethodHead {
+ if BConfig.CopyRequestBody && !context.Input.IsUpload() {
+ context.Input.CopyBody(BConfig.MaxMemory)
+ }
+ context.Input.ParseFormOrMulitForm(BConfig.MaxMemory)
+ }
+
+ // session init
+ if BConfig.WebConfig.Session.SessionOn {
+ var err error
+ context.Input.CruSession, err = GlobalSessions.SessionStart(rw, r)
+ if err != nil {
+ logs.Error(err)
+ exception("503", context)
+ goto Admin
+ }
+ defer func() {
+ if context.Input.CruSession != nil {
+ context.Input.CruSession.SessionRelease(rw)
+ }
+ }()
+ }
+ if len(p.filters[BeforeRouter]) > 0 && p.execFilter(context, urlPath, BeforeRouter) {
+ goto Admin
+ }
+ // User can define RunController and RunMethod in filter
+ if context.Input.RunController != nil && context.Input.RunMethod != "" {
+ findRouter = true
+ runMethod = context.Input.RunMethod
+ runRouter = context.Input.RunController
+ } else {
+ routerInfo, findRouter = p.FindRouter(context)
+ }
+
+ //if no matches to url, throw a not found exception
+ if !findRouter {
+ exception("404", context)
+ goto Admin
+ }
+ if splat := context.Input.Param(":splat"); splat != "" {
+ for k, v := range strings.Split(splat, "/") {
+ context.Input.SetParam(strconv.Itoa(k), v)
+ }
+ }
+
+ //execute middleware filters
+ if len(p.filters[BeforeExec]) > 0 && p.execFilter(context, urlPath, BeforeExec) {
+ goto Admin
+ }
+
+ //check policies
+ if p.execPolicy(context, urlPath) {
+ goto Admin
+ }
+
+ if routerInfo != nil {
+ //store router pattern into context
+ context.Input.SetData("RouterPattern", routerInfo.pattern)
+ if routerInfo.routerType == routerTypeRESTFul {
+ if _, ok := routerInfo.methods[r.Method]; ok {
+ isRunnable = true
+ routerInfo.runFunction(context)
+ } else {
+ exception("405", context)
+ goto Admin
+ }
+ } else if routerInfo.routerType == routerTypeHandler {
+ isRunnable = true
+ routerInfo.handler.ServeHTTP(rw, r)
+ } else {
+ runRouter = routerInfo.controllerType
+ methodParams = routerInfo.methodParams
+ method := r.Method
+ if r.Method == http.MethodPost && context.Input.Query("_method") == http.MethodPost {
+ method = http.MethodPut
+ }
+ if r.Method == http.MethodPost && context.Input.Query("_method") == http.MethodDelete {
+ method = http.MethodDelete
+ }
+ if m, ok := routerInfo.methods[method]; ok {
+ runMethod = m
+ } else if m, ok = routerInfo.methods["*"]; ok {
+ runMethod = m
+ } else {
+ runMethod = method
+ }
+ }
+ }
+
+ // also defined runRouter & runMethod from filter
+ if !isRunnable {
+ //Invoke the request handler
+ var execController ControllerInterface
+ if routerInfo.initialize != nil {
+ execController = routerInfo.initialize()
+ } else {
+ vc := reflect.New(runRouter)
+ var ok bool
+ execController, ok = vc.Interface().(ControllerInterface)
+ if !ok {
+ panic("controller is not ControllerInterface")
+ }
+ }
+
+ //call the controller init function
+ execController.Init(context, runRouter.Name(), runMethod, execController)
+
+ //call prepare function
+ execController.Prepare()
+
+ //if XSRF is Enable then check cookie where there has any cookie in the request's cookie _csrf
+ if BConfig.WebConfig.EnableXSRF {
+ execController.XSRFToken()
+ if r.Method == http.MethodPost || r.Method == http.MethodDelete || r.Method == http.MethodPut ||
+ (r.Method == http.MethodPost && (context.Input.Query("_method") == http.MethodDelete || context.Input.Query("_method") == http.MethodPut)) {
+ execController.CheckXSRFCookie()
+ }
+ }
+
+ execController.URLMapping()
+
+ if !context.ResponseWriter.Started {
+ //exec main logic
+ switch runMethod {
+ case http.MethodGet:
+ execController.Get()
+ case http.MethodPost:
+ execController.Post()
+ case http.MethodDelete:
+ execController.Delete()
+ case http.MethodPut:
+ execController.Put()
+ case http.MethodHead:
+ execController.Head()
+ case http.MethodPatch:
+ execController.Patch()
+ case http.MethodOptions:
+ execController.Options()
+ default:
+ if !execController.HandlerFunc(runMethod) {
+ vc := reflect.ValueOf(execController)
+ method := vc.MethodByName(runMethod)
+ in := param.ConvertParams(methodParams, method.Type(), context)
+ out := method.Call(in)
+
+ //For backward compatibility we only handle response if we had incoming methodParams
+ if methodParams != nil {
+ p.handleParamResponse(context, execController, out)
+ }
+ }
+ }
+
+ //render template
+ if !context.ResponseWriter.Started && context.Output.Status == 0 {
+ if BConfig.WebConfig.AutoRender {
+ if err := execController.Render(); err != nil {
+ logs.Error(err)
+ }
+ }
+ }
+ }
+
+ // finish all runRouter. release resource
+ execController.Finish()
+ }
+
+ //execute middleware filters
+ if len(p.filters[AfterExec]) > 0 && p.execFilter(context, urlPath, AfterExec) {
+ goto Admin
+ }
+
+ if len(p.filters[FinishRouter]) > 0 && p.execFilter(context, urlPath, FinishRouter) {
+ goto Admin
+ }
+
+Admin:
+ //admin module record QPS
+
+ statusCode := context.ResponseWriter.Status
+ if statusCode == 0 {
+ statusCode = 200
+ }
+
+ if BConfig.Listen.EnableAdmin {
+ timeDur := time.Since(startTime)
+ pattern := ""
+ if routerInfo != nil {
+ pattern = routerInfo.pattern
+ }
+
+ if FilterMonitorFunc(r.Method, r.URL.Path, timeDur, pattern, statusCode) {
+ if runRouter != nil {
+ go toolbox.StatisticsMap.AddStatistics(r.Method, r.URL.Path, runRouter.Name(), timeDur)
+ } else {
+ go toolbox.StatisticsMap.AddStatistics(r.Method, r.URL.Path, "", timeDur)
+ }
+ }
+ }
+
+ if BConfig.RunMode == DEV || BConfig.Log.AccessLogs {
+ timeDur := time.Since(startTime)
+ var devInfo string
+
+ iswin := (runtime.GOOS == "windows")
+ statusColor := logs.ColorByStatus(iswin, statusCode)
+ methodColor := logs.ColorByMethod(iswin, r.Method)
+ resetColor := logs.ColorByMethod(iswin, "")
+ if BConfig.Log.AccessLogsFormat != "" {
+ record := &logs.AccessLogRecord{
+ RemoteAddr: context.Input.IP(),
+ RequestTime: startTime,
+ RequestMethod: r.Method,
+ Request: fmt.Sprintf("%s %s %s", r.Method, r.RequestURI, r.Proto),
+ ServerProtocol: r.Proto,
+ Host: r.Host,
+ Status: statusCode,
+ ElapsedTime: timeDur,
+ HTTPReferrer: r.Header.Get("Referer"),
+ HTTPUserAgent: r.Header.Get("User-Agent"),
+ RemoteUser: r.Header.Get("Remote-User"),
+ BodyBytesSent: 0, //@todo this one is missing!
+ }
+ logs.AccessLog(record, BConfig.Log.AccessLogsFormat)
+ } else {
+ if findRouter {
+ if routerInfo != nil {
+ devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s r:%s", context.Input.IP(), statusColor, statusCode,
+ resetColor, timeDur.String(), "match", methodColor, r.Method, resetColor, r.URL.Path,
+ routerInfo.pattern)
+ } else {
+ devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s", context.Input.IP(), statusColor, statusCode, resetColor,
+ timeDur.String(), "match", methodColor, r.Method, resetColor, r.URL.Path)
+ }
+ } else {
+ devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s", context.Input.IP(), statusColor, statusCode, resetColor,
+ timeDur.String(), "nomatch", methodColor, r.Method, resetColor, r.URL.Path)
+ }
+ if iswin {
+ logs.W32Debug(devInfo)
+ } else {
+ logs.Debug(devInfo)
+ }
+ }
+ }
+ // Call WriteHeader if status code has been set changed
+ if context.Output.Status != 0 {
+ context.ResponseWriter.WriteHeader(context.Output.Status)
+ }
+}
+
+func (p *ControllerRegister) handleParamResponse(context *beecontext.Context, execController ControllerInterface, results []reflect.Value) {
+ //looping in reverse order for the case when both error and value are returned and error sets the response status code
+ for i := len(results) - 1; i >= 0; i-- {
+ result := results[i]
+ if result.Kind() != reflect.Interface || !result.IsNil() {
+ resultValue := result.Interface()
+ context.RenderMethodResult(resultValue)
+ }
+ }
+ if !context.ResponseWriter.Started && context.Output.Status == 0 {
+ context.Output.SetStatus(200)
+ }
+}
+
+// FindRouter Find Router info for URL
+func (p *ControllerRegister) FindRouter(context *beecontext.Context) (routerInfo *ControllerInfo, isFind bool) {
+ var urlPath = context.Input.URL()
+ if !BConfig.RouterCaseSensitive {
+ urlPath = strings.ToLower(urlPath)
+ }
+ httpMethod := context.Input.Method()
+ if t, ok := p.routers[httpMethod]; ok {
+ runObject := t.Match(urlPath, context)
+ if r, ok := runObject.(*ControllerInfo); ok {
+ return r, true
+ }
+ }
+ return
+}
+
+func toURL(params map[string]string) string {
+ if len(params) == 0 {
+ return ""
+ }
+ u := "?"
+ for k, v := range params {
+ u += k + "=" + v + "&"
+ }
+ return strings.TrimRight(u, "&")
+}
diff --git a/vendor/github.com/astaxie/beego/staticfile.go b/vendor/github.com/astaxie/beego/staticfile.go
new file mode 100644
index 0000000..bbb2a1f
--- /dev/null
+++ b/vendor/github.com/astaxie/beego/staticfile.go
@@ -0,0 +1,204 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// 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 beego
+
+import (
+ "bytes"
+ "errors"
+ "net/http"
+ "os"
+ "path"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/astaxie/beego/context"
+ "github.com/astaxie/beego/logs"
+)
+
+var errNotStaticRequest = errors.New("request not a static file request")
+
+func serverStaticRouter(ctx *context.Context) {
+ if ctx.Input.Method() != "GET" && ctx.Input.Method() != "HEAD" {
+ return
+ }
+
+ forbidden, filePath, fileInfo, err := lookupFile(ctx)
+ if err == errNotStaticRequest {
+ return
+ }
+
+ if forbidden {
+ exception("403", ctx)
+ return
+ }
+
+ if filePath == "" || fileInfo == nil {
+ if BConfig.RunMode == DEV {
+ logs.Warn("Can't find/open the file:", filePath, err)
+ }
+ http.NotFound(ctx.ResponseWriter, ctx.Request)
+ return
+ }
+ if fileInfo.IsDir() {
+ requestURL := ctx.Input.URL()
+ if requestURL[len(requestURL)-1] != '/' {
+ redirectURL := requestURL + "/"
+ if ctx.Request.URL.RawQuery != "" {
+ redirectURL = redirectURL + "?" + ctx.Request.URL.RawQuery
+ }
+ ctx.Redirect(302, redirectURL)
+ } else {
+ //serveFile will list dir
+ http.ServeFile(ctx.ResponseWriter, ctx.Request, filePath)
+ }
+ return
+ }
+
+ var enableCompress = BConfig.EnableGzip && isStaticCompress(filePath)
+ var acceptEncoding string
+ if enableCompress {
+ acceptEncoding = context.ParseEncoding(ctx.Request)
+ }
+ b, n, sch, err := openFile(filePath, fileInfo, acceptEncoding)
+ if err != nil {
+ if BConfig.RunMode == DEV {
+ logs.Warn("Can't compress the file:", filePath, err)
+ }
+ http.NotFound(ctx.ResponseWriter, ctx.Request)
+ return
+ }
+
+ if b {
+ ctx.Output.Header("Content-Encoding", n)
+ } else {
+ ctx.Output.Header("Content-Length", strconv.FormatInt(sch.size, 10))
+ }
+
+ http.ServeContent(ctx.ResponseWriter, ctx.Request, filePath, sch.modTime, sch)
+}
+
+type serveContentHolder struct {
+ *bytes.Reader
+ modTime time.Time
+ size int64
+ encoding string
+}
+
+var (
+ staticFileMap = make(map[string]*serveContentHolder)
+ mapLock sync.RWMutex
+)
+
+func openFile(filePath string, fi os.FileInfo, acceptEncoding string) (bool, string, *serveContentHolder, error) {
+ mapKey := acceptEncoding + ":" + filePath
+ mapLock.RLock()
+ mapFile := staticFileMap[mapKey]
+ mapLock.RUnlock()
+ if isOk(mapFile, fi) {
+ return mapFile.encoding != "", mapFile.encoding, mapFile, nil
+ }
+ mapLock.Lock()
+ defer mapLock.Unlock()
+ if mapFile = staticFileMap[mapKey]; !isOk(mapFile, fi) {
+ file, err := os.Open(filePath)
+ if err != nil {
+ return false, "", nil, err
+ }
+ defer file.Close()
+ var bufferWriter bytes.Buffer
+ _, n, err := context.WriteFile(acceptEncoding, &bufferWriter, file)
+ if err != nil {
+ return false, "", nil, err
+ }
+ mapFile = &serveContentHolder{Reader: bytes.NewReader(bufferWriter.Bytes()), modTime: fi.ModTime(), size: int64(bufferWriter.Len()), encoding: n}
+ staticFileMap[mapKey] = mapFile
+ }
+
+ return mapFile.encoding != "", mapFile.encoding, mapFile, nil
+}
+
+func isOk(s *serveContentHolder, fi os.FileInfo) bool {
+ if s == nil {
+ return false
+ }
+ return s.modTime == fi.ModTime() && s.size == fi.Size()
+}
+
+// isStaticCompress detect static files
+func isStaticCompress(filePath string) bool {
+ for _, statExtension := range BConfig.WebConfig.StaticExtensionsToGzip {
+ if strings.HasSuffix(strings.ToLower(filePath), strings.ToLower(statExtension)) {
+ return true
+ }
+ }
+ return false
+}
+
+// searchFile search the file by url path
+// if none the static file prefix matches ,return notStaticRequestErr
+func searchFile(ctx *context.Context) (string, os.FileInfo, error) {
+ requestPath := filepath.ToSlash(filepath.Clean(ctx.Request.URL.Path))
+ // special processing : favicon.ico/robots.txt can be in any static dir
+ if requestPath == "/favicon.ico" || requestPath == "/robots.txt" {
+ file := path.Join(".", requestPath)
+ if fi, _ := os.Stat(file); fi != nil {
+ return file, fi, nil
+ }
+ for _, staticDir := range BConfig.WebConfig.StaticDir {
+ filePath := path.Join(staticDir, requestPath)
+ if fi, _ := os.Stat(filePath); fi != nil {
+ return filePath, fi, nil
+ }
+ }
+ return "", nil, errNotStaticRequest
+ }
+
+ for prefix, staticDir := range BConfig.WebConfig.StaticDir {
+ if !strings.Contains(requestPath, prefix) {
+ continue
+ }
+ if len(requestPath) > len(prefix) && requestPath[len(prefix)] != '/' {
+ continue
+ }
+ filePath := path.Join(staticDir, requestPath[len(prefix):])
+ if fi, err := os.Stat(filePath); fi != nil {
+ return filePath, fi, err
+ }
+ }
+ return "", nil, errNotStaticRequest
+}
+
+// lookupFile find the file to serve
+// if the file is dir ,search the index.html as default file( MUST NOT A DIR also)
+// if the index.html not exist or is a dir, give a forbidden response depending on DirectoryIndex
+func lookupFile(ctx *context.Context) (bool, string, os.FileInfo, error) {
+ fp, fi, err := searchFile(ctx)
+ if fp == "" || fi == nil {
+ return false, "", nil, err
+ }
+ if !fi.IsDir() {
+ return false, fp, fi, err
+ }
+ if requestURL := ctx.Input.URL(); requestURL[len(requestURL)-1] == '/' {
+ ifp := filepath.Join(fp, "index.html")
+ if ifi, _ := os.Stat(ifp); ifi != nil && ifi.Mode().IsRegular() {
+ return false, ifp, ifi, err
+ }
+ }
+ return !BConfig.WebConfig.DirectoryIndex, fp, fi, err
+}
diff --git a/vendor/github.com/astaxie/beego/template.go b/vendor/github.com/astaxie/beego/template.go
new file mode 100644
index 0000000..88c84e2
--- /dev/null
+++ b/vendor/github.com/astaxie/beego/template.go
@@ -0,0 +1,374 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// 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 beego
+
+import (
+ "errors"
+ "fmt"
+ "html/template"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "regexp"
+ "strings"
+ "sync"
+
+ "github.com/astaxie/beego/logs"
+ "github.com/astaxie/beego/utils"
+)
+
+var (
+ beegoTplFuncMap = make(template.FuncMap)
+ beeViewPathTemplateLocked = false
+ // beeViewPathTemplates caching map and supported template file extensions per view
+ beeViewPathTemplates = make(map[string]map[string]*template.Template)
+ templatesLock sync.RWMutex
+ // beeTemplateExt stores the template extension which will build
+ beeTemplateExt = []string{"tpl", "html"}
+ // beeTemplatePreprocessors stores associations of extension -> preprocessor handler
+ beeTemplateEngines = map[string]templatePreProcessor{}
+)
+
+// ExecuteTemplate applies the template with name to the specified data object,
+// writing the output to wr.
+// A template will be executed safely in parallel.
+func ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
+ return ExecuteViewPathTemplate(wr, name, BConfig.WebConfig.ViewsPath, data)
+}
+
+// ExecuteViewPathTemplate applies the template with name and from specific viewPath to the specified data object,
+// writing the output to wr.
+// A template will be executed safely in parallel.
+func ExecuteViewPathTemplate(wr io.Writer, name string, viewPath string, data interface{}) error {
+ if BConfig.RunMode == DEV {
+ templatesLock.RLock()
+ defer templatesLock.RUnlock()
+ }
+ if beeTemplates, ok := beeViewPathTemplates[viewPath]; ok {
+ if t, ok := beeTemplates[name]; ok {
+ var err error
+ if t.Lookup(name) != nil {
+ err = t.ExecuteTemplate(wr, name, data)
+ } else {
+ err = t.Execute(wr, data)
+ }
+ if err != nil {
+ logs.Trace("template Execute err:", err)
+ }
+ return err
+ }
+ panic("can't find templatefile in the path:" + viewPath + "/" + name)
+ }
+ panic("Unknown view path:" + viewPath)
+}
+
+func init() {
+ beegoTplFuncMap["dateformat"] = DateFormat
+ beegoTplFuncMap["date"] = Date
+ beegoTplFuncMap["compare"] = Compare
+ beegoTplFuncMap["compare_not"] = CompareNot
+ beegoTplFuncMap["not_nil"] = NotNil
+ beegoTplFuncMap["not_null"] = NotNil
+ beegoTplFuncMap["substr"] = Substr
+ beegoTplFuncMap["html2str"] = HTML2str
+ beegoTplFuncMap["str2html"] = Str2html
+ beegoTplFuncMap["htmlquote"] = Htmlquote
+ beegoTplFuncMap["htmlunquote"] = Htmlunquote
+ beegoTplFuncMap["renderform"] = RenderForm
+ beegoTplFuncMap["assets_js"] = AssetsJs
+ beegoTplFuncMap["assets_css"] = AssetsCSS
+ beegoTplFuncMap["config"] = GetConfig
+ beegoTplFuncMap["map_get"] = MapGet
+
+ // Comparisons
+ beegoTplFuncMap["eq"] = eq // ==
+ beegoTplFuncMap["ge"] = ge // >=
+ beegoTplFuncMap["gt"] = gt // >
+ beegoTplFuncMap["le"] = le // <=
+ beegoTplFuncMap["lt"] = lt // <
+ beegoTplFuncMap["ne"] = ne // !=
+
+ beegoTplFuncMap["urlfor"] = URLFor // build a URL to match a Controller and it's method
+}
+
+// AddFuncMap let user to register a func in the template.
+func AddFuncMap(key string, fn interface{}) error {
+ beegoTplFuncMap[key] = fn
+ return nil
+}
+
+type templatePreProcessor func(root, path string, funcs template.FuncMap) (*template.Template, error)
+
+type templateFile struct {
+ root string
+ files map[string][]string
+}
+
+// visit will make the paths into two part,the first is subDir (without tf.root),the second is full path(without tf.root).
+// if tf.root="views" and
+// paths is "views/errors/404.html",the subDir will be "errors",the file will be "errors/404.html"
+// paths is "views/admin/errors/404.html",the subDir will be "admin/errors",the file will be "admin/errors/404.html"
+func (tf *templateFile) visit(paths string, f os.FileInfo, err error) error {
+ if f == nil {
+ return err
+ }
+ if f.IsDir() || (f.Mode()&os.ModeSymlink) > 0 {
+ return nil
+ }
+ if !HasTemplateExt(paths) {
+ return nil
+ }
+
+ replace := strings.NewReplacer("\\", "/")
+ file := strings.TrimLeft(replace.Replace(paths[len(tf.root):]), "/")
+ subDir := filepath.Dir(file)
+
+ tf.files[subDir] = append(tf.files[subDir], file)
+ return nil
+}
+
+// HasTemplateExt return this path contains supported template extension of beego or not.
+func HasTemplateExt(paths string) bool {
+ for _, v := range beeTemplateExt {
+ if strings.HasSuffix(paths, "."+v) {
+ return true
+ }
+ }
+ return false
+}
+
+// AddTemplateExt add new extension for template.
+func AddTemplateExt(ext string) {
+ for _, v := range beeTemplateExt {
+ if v == ext {
+ return
+ }
+ }
+ beeTemplateExt = append(beeTemplateExt, ext)
+}
+
+// AddViewPath adds a new path to the supported view paths.
+//Can later be used by setting a controller ViewPath to this folder
+//will panic if called after beego.Run()
+func AddViewPath(viewPath string) error {
+ if beeViewPathTemplateLocked {
+ if _, exist := beeViewPathTemplates[viewPath]; exist {
+ return nil //Ignore if viewpath already exists
+ }
+ panic("Can not add new view paths after beego.Run()")
+ }
+ beeViewPathTemplates[viewPath] = make(map[string]*template.Template)
+ return BuildTemplate(viewPath)
+}
+
+func lockViewPaths() {
+ beeViewPathTemplateLocked = true
+}
+
+// BuildTemplate will build all template files in a directory.
+// it makes beego can render any template file in view directory.
+func BuildTemplate(dir string, files ...string) error {
+ if _, err := os.Stat(dir); err != nil {
+ if os.IsNotExist(err) {
+ return nil
+ }
+ return errors.New("dir open err")
+ }
+ beeTemplates, ok := beeViewPathTemplates[dir]
+ if !ok {
+ panic("Unknown view path: " + dir)
+ }
+ self := &templateFile{
+ root: dir,
+ files: make(map[string][]string),
+ }
+ err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
+ return self.visit(path, f, err)
+ })
+ if err != nil {
+ fmt.Printf("filepath.Walk() returned %v\n", err)
+ return err
+ }
+ buildAllFiles := len(files) == 0
+ for _, v := range self.files {
+ for _, file := range v {
+ if buildAllFiles || utils.InSlice(file, files) {
+ templatesLock.Lock()
+ ext := filepath.Ext(file)
+ var t *template.Template
+ if len(ext) == 0 {
+ t, err = getTemplate(self.root, file, v...)
+ } else if fn, ok := beeTemplateEngines[ext[1:]]; ok {
+ t, err = fn(self.root, file, beegoTplFuncMap)
+ } else {
+ t, err = getTemplate(self.root, file, v...)
+ }
+ if err != nil {
+ logs.Error("parse template err:", file, err)
+ templatesLock.Unlock()
+ return err
+ }
+ beeTemplates[file] = t
+ templatesLock.Unlock()
+ }
+ }
+ }
+ return nil
+}
+
+func getTplDeep(root, file, parent string, t *template.Template) (*template.Template, [][]string, error) {
+ var fileAbsPath string
+ var rParent string
+ if filepath.HasPrefix(file, "../") {
+ rParent = filepath.Join(filepath.Dir(parent), file)
+ fileAbsPath = filepath.Join(root, filepath.Dir(parent), file)
+ } else {
+ rParent = file
+ fileAbsPath = filepath.Join(root, file)
+ }
+ if e := utils.FileExists(fileAbsPath); !e {
+ panic("can't find template file:" + file)
+ }
+ data, err := ioutil.ReadFile(fileAbsPath)
+ if err != nil {
+ return nil, [][]string{}, err
+ }
+ t, err = t.New(file).Parse(string(data))
+ if err != nil {
+ return nil, [][]string{}, err
+ }
+ reg := regexp.MustCompile(BConfig.WebConfig.TemplateLeft + "[ ]*template[ ]+\"([^\"]+)\"")
+ allSub := reg.FindAllStringSubmatch(string(data), -1)
+ for _, m := range allSub {
+ if len(m) == 2 {
+ tl := t.Lookup(m[1])
+ if tl != nil {
+ continue
+ }
+ if !HasTemplateExt(m[1]) {
+ continue
+ }
+ _, _, err = getTplDeep(root, m[1], rParent, t)
+ if err != nil {
+ return nil, [][]string{}, err
+ }
+ }
+ }
+ return t, allSub, nil
+}
+
+func getTemplate(root, file string, others ...string) (t *template.Template, err error) {
+ t = template.New(file).Delims(BConfig.WebConfig.TemplateLeft, BConfig.WebConfig.TemplateRight).Funcs(beegoTplFuncMap)
+ var subMods [][]string
+ t, subMods, err = getTplDeep(root, file, "", t)
+ if err != nil {
+ return nil, err
+ }
+ t, err = _getTemplate(t, root, subMods, others...)
+
+ if err != nil {
+ return nil, err
+ }
+ return
+}
+
+func _getTemplate(t0 *template.Template, root string, subMods [][]string, others ...string) (t *template.Template, err error) {
+ t = t0
+ for _, m := range subMods {
+ if len(m) == 2 {
+ tpl := t.Lookup(m[1])
+ if tpl != nil {
+ continue
+ }
+ //first check filename
+ for _, otherFile := range others {
+ if otherFile == m[1] {
+ var subMods1 [][]string
+ t, subMods1, err = getTplDeep(root, otherFile, "", t)
+ if err != nil {
+ logs.Trace("template parse file err:", err)
+ } else if len(subMods1) > 0 {
+ t, err = _getTemplate(t, root, subMods1, others...)
+ }
+ break
+ }
+ }
+ //second check define
+ for _, otherFile := range others {
+ var data []byte
+ fileAbsPath := filepath.Join(root, otherFile)
+ data, err = ioutil.ReadFile(fileAbsPath)
+ if err != nil {
+ continue
+ }
+ reg := regexp.MustCompile(BConfig.WebConfig.TemplateLeft + "[ ]*define[ ]+\"([^\"]+)\"")
+ allSub := reg.FindAllStringSubmatch(string(data), -1)
+ for _, sub := range allSub {
+ if len(sub) == 2 && sub[1] == m[1] {
+ var subMods1 [][]string
+ t, subMods1, err = getTplDeep(root, otherFile, "", t)
+ if err != nil {
+ logs.Trace("template parse file err:", err)
+ } else if len(subMods1) > 0 {
+ t, err = _getTemplate(t, root, subMods1, others...)
+ }
+ break
+ }
+ }
+ }
+ }
+
+ }
+ return
+}
+
+// SetViewsPath sets view directory path in beego application.
+func SetViewsPath(path string) *App {
+ BConfig.WebConfig.ViewsPath = path
+ return BeeApp
+}
+
+// SetStaticPath sets static directory path and proper url pattern in beego application.
+// if beego.SetStaticPath("static","public"), visit /static/* to load static file in folder "public".
+func SetStaticPath(url string, path string) *App {
+ if !strings.HasPrefix(url, "/") {
+ url = "/" + url
+ }
+ if url != "/" {
+ url = strings.TrimRight(url, "/")
+ }
+ BConfig.WebConfig.StaticDir[url] = path
+ return BeeApp
+}
+
+// DelStaticPath removes the static folder setting in this url pattern in beego application.
+func DelStaticPath(url string) *App {
+ if !strings.HasPrefix(url, "/") {
+ url = "/" + url
+ }
+ if url != "/" {
+ url = strings.TrimRight(url, "/")
+ }
+ delete(BConfig.WebConfig.StaticDir, url)
+ return BeeApp
+}
+
+// AddTemplateEngine add a new templatePreProcessor which support extension
+func AddTemplateEngine(extension string, fn templatePreProcessor) *App {
+ AddTemplateExt(extension)
+ beeTemplateEngines[extension] = fn
+ return BeeApp
+}
diff --git a/vendor/github.com/astaxie/beego/templatefunc.go b/vendor/github.com/astaxie/beego/templatefunc.go
new file mode 100644
index 0000000..e4d4667
--- /dev/null
+++ b/vendor/github.com/astaxie/beego/templatefunc.go
@@ -0,0 +1,766 @@
+// Copyright 2014 beego Author. All Rights Reserved.
+//
+// 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 beego
+
+import (
+ "errors"
+ "fmt"
+ "html"
+ "html/template"
+ "net/url"
+ "reflect"
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+)
+
+const (
+ formatTime = "15:04:05"
+ formatDate = "2006-01-02"
+ formatDateTime = "2006-01-02 15:04:05"
+ formatDateTimeT = "2006-01-02T15:04:05"
+)
+
+// Substr returns the substr from start to length.
+func Substr(s string, start, length int) string {
+ bt := []rune(s)
+ if start < 0 {
+ start = 0
+ }
+ if start > len(bt) {
+ start = start % len(bt)
+ }
+ var end int
+ if (start + length) > (len(bt) - 1) {
+ end = len(bt)
+ } else {
+ end = start + length
+ }
+ return string(bt[start:end])
+}
+
+// HTML2str returns escaping text convert from html.
+func HTML2str(html string) string {
+
+ re, _ := regexp.Compile(`\<[\S\s]+?\>`)
+ html = re.ReplaceAllStringFunc(html, strings.ToLower)
+
+ //remove STYLE
+ re, _ = regexp.Compile(`\