diff --git a/pkg/adapter/admin.go b/pkg/adapter/admin.go
index 87e7259b..3127416f 100644
--- a/pkg/adapter/admin.go
+++ b/pkg/adapter/admin.go
@@ -17,6 +17,7 @@ package adapter
import (
"time"
+ _ "github.com/astaxie/beego/pkg/infrastructure/governor"
"github.com/astaxie/beego/pkg/server/web"
)
@@ -38,11 +39,7 @@ import (
// beego.FilterMonitorFunc = MyFilterMonitor.
var FilterMonitorFunc func(string, string, time.Duration, string, int) bool
-func init() {
- FilterMonitorFunc = web.FilterMonitorFunc
-}
-
// PrintTree prints all registered routers.
func PrintTree() M {
- return (M)(web.PrintTree())
+ return (M)(web.BeeApp.PrintTree())
}
diff --git a/pkg/adapter/app.go b/pkg/adapter/app.go
index c1046c79..10ffa96a 100644
--- a/pkg/adapter/app.go
+++ b/pkg/adapter/app.go
@@ -33,11 +33,11 @@ func init() {
}
// App defines beego application with a new PatternServeMux.
-type App web.App
+type App web.HttpServer
// NewApp returns a new beego application.
func NewApp() *App {
- return (*App)(web.NewApp())
+ return (*App)(web.NewHttpSever())
}
// MiddleWare function for http.Handler
@@ -46,7 +46,7 @@ type MiddleWare web.MiddleWare
// Run beego application.
func (app *App) Run(mws ...MiddleWare) {
newMws := oldMiddlewareToNew(mws)
- (*web.App)(app).Run(newMws...)
+ (*web.HttpServer)(app).Run("", newMws...)
}
func oldMiddlewareToNew(mws []MiddleWare) []web.MiddleWare {
@@ -58,7 +58,7 @@ func oldMiddlewareToNew(mws []MiddleWare) []web.MiddleWare {
}
// Router adds a patterned controller handler to BeeApp.
-// it's an alias method of App.Router.
+// it's an alias method of HttpServer.Router.
// usage:
// simple router
// beego.Router("/admin", &admin.UserController{})
@@ -138,7 +138,7 @@ func RESTRouter(rootpath string, c ControllerInterface) *App {
}
// AutoRouter adds defined controller handler to BeeApp.
-// it's same to App.AutoRouter.
+// it's same to HttpServer.AutoRouter.
// if beego.AddAuto(&MainContorlller{}) and MainController has methods List and Page,
// visit the url /main/list to exec List function or /main/page to exec Page function.
func AutoRouter(c ControllerInterface) *App {
@@ -146,7 +146,7 @@ func AutoRouter(c ControllerInterface) *App {
}
// AutoPrefix adds controller handler to BeeApp with prefix.
-// it's same to App.AutoRouterWithPrefix.
+// it's same to HttpServer.AutoRouterWithPrefix.
// if beego.AutoPrefix("/admin",&MainContorlller{}) and MainController has methods List and Page,
// visit the url /admin/main/list to exec List function or /admin/main/page to exec Page function.
func AutoPrefix(prefix string, c ControllerInterface) *App {
diff --git a/pkg/adapter/beego.go b/pkg/adapter/beego.go
index efd2d4ea..eb7be3f6 100644
--- a/pkg/adapter/beego.go
+++ b/pkg/adapter/beego.go
@@ -15,12 +15,14 @@
package adapter
import (
+ "github.com/astaxie/beego/pkg"
"github.com/astaxie/beego/pkg/server/web"
)
const (
+
// VERSION represent beego web framework version.
- VERSION = web.VERSION
+ VERSION = pkg.VERSION
// DEV is for develop
DEV = web.DEV
diff --git a/pkg/adapter/metric/prometheus.go b/pkg/adapter/metric/prometheus.go
index 1d3488c6..6af2c26c 100644
--- a/pkg/adapter/metric/prometheus.go
+++ b/pkg/adapter/metric/prometheus.go
@@ -23,6 +23,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
+ "github.com/astaxie/beego/pkg"
"github.com/astaxie/beego/pkg/infrastructure/logs"
"github.com/astaxie/beego/pkg/server/web"
)
@@ -58,13 +59,13 @@ func registerBuildInfo() {
Help: "The building information",
ConstLabels: map[string]string{
"appname": web.BConfig.AppName,
- "build_version": web.BuildVersion,
- "build_revision": web.BuildGitRevision,
- "build_status": web.BuildStatus,
- "build_tag": web.BuildTag,
- "build_time": strings.Replace(web.BuildTime, "--", " ", 1),
- "go_version": web.GoVersion,
- "git_branch": web.GitBranch,
+ "build_version": pkg.BuildVersion,
+ "build_revision": pkg.BuildGitRevision,
+ "build_status": pkg.BuildStatus,
+ "build_tag": pkg.BuildTag,
+ "build_time": strings.Replace(pkg.BuildTime, "--", " ", 1),
+ "go_version": pkg.GoVersion,
+ "git_branch": pkg.GitBranch,
"start_time": time.Now().Format("2006-01-02 15:04:05"),
},
}, []string{})
diff --git a/pkg/server/web/build_info.go b/pkg/build_info.go
similarity index 88%
rename from pkg/server/web/build_info.go
rename to pkg/build_info.go
index 53351c11..778856c6 100644
--- a/pkg/server/web/build_info.go
+++ b/pkg/build_info.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package web
+package pkg
var (
BuildVersion string
@@ -25,3 +25,8 @@ var (
GitBranch string
)
+
+const (
+ // VERSION represent beego web framework version.
+ VERSION = "1.12.2"
+)
diff --git a/pkg/server/web/admin.go b/pkg/server/web/admin.go
index f54ac9e5..46c0f738 100644
--- a/pkg/server/web/admin.go
+++ b/pkg/server/web/admin.go
@@ -15,24 +15,12 @@
package web
import (
- "bytes"
- context2 "context"
- "encoding/json"
"fmt"
"net/http"
- "os"
"reflect"
- "strconv"
- "text/template"
"time"
- "github.com/prometheus/client_golang/prometheus/promhttp"
-
"github.com/astaxie/beego/pkg/infrastructure/logs"
-
- "github.com/astaxie/beego/pkg/infrastructure/governor"
- "github.com/astaxie/beego/pkg/infrastructure/utils"
- "github.com/astaxie/beego/pkg/server/web/grace"
"github.com/astaxie/beego/pkg/task"
)
@@ -58,127 +46,9 @@ var beeAdminApp *adminApp
var FilterMonitorFunc func(string, string, time.Duration, string, int) bool
func init() {
- beeAdminApp = &adminApp{
- routers: make(map[string]http.HandlerFunc),
- }
- // keep in mind that all data should be html escaped to avoid XSS attack
- beeAdminApp.Route("/", adminIndex)
- beeAdminApp.Route("/qps", qpsIndex)
- beeAdminApp.Route("/prof", profIndex)
- beeAdminApp.Route("/healthcheck", healthcheck)
- beeAdminApp.Route("/task", taskStatus)
- beeAdminApp.Route("/listconf", listConf)
- beeAdminApp.Route("/metrics", promhttp.Handler().ServeHTTP)
+
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, _ *http.Request) {
- writeTemplate(rw, map[interface{}]interface{}{}, indexTpl, defaultScriptsTpl)
-}
-
-// QpsIndex is the http.Handler for writing qps statistics map result info in http.ResponseWriter.
-// it's registered with url pattern "/qps" in admin module.
-func qpsIndex(rw http.ResponseWriter, _ *http.Request) {
- data := make(map[interface{}]interface{})
- data["Content"] = StatisticsMap.GetMap()
-
- // do html escape before display path, avoid xss
- if content, ok := (data["Content"]).(M); ok {
- if resultLists, ok := (content["Data"]).([][]string); ok {
- for i := range resultLists {
- if len(resultLists[i]) > 0 {
- resultLists[i][0] = template.HTMLEscapeString(resultLists[i][0])
- }
- }
- }
- }
-
- writeTemplate(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(M)
- list("BConfig", BConfig, m)
- m["AppConfigPath"] = template.HTMLEscapeString(appConfigPath)
- m["AppConfigProvider"] = template.HTMLEscapeString(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"
- writeTemplate(rw, data, routerAndFilterTpl, defaultScriptsTpl)
- case "filter":
- var (
- content = M{
- "Fields": []string{
- "Router Pattern",
- "Filter Function",
- },
- }
- filterTypes = []string{}
- filterTypeData = make(M)
- )
-
- 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{
- // void xss
- template.HTMLEscapeString(f.pattern),
- template.HTMLEscapeString(utils.GetFuncName(f.filterFunc)),
- }
- *resultList = append(*resultList, result)
- }
- filterTypeData[filterType] = resultList
- }
- }
- }
-
- content["Data"] = filterTypeData
- content["Methods"] = filterTypes
-
- data["Content"] = content
- data["Title"] = "Filters"
- writeTemplate(rw, data, routerAndFilterTpl, defaultScriptsTpl)
- default:
- rw.Write([]byte("command not support"))
- }
}
func list(root string, p interface{}, m M) {
@@ -203,239 +73,19 @@ func list(root string, p interface{}, m M) {
}
}
-// PrintTree prints all registered routers.
-func PrintTree() M {
- var (
- content = M{}
- methods = []string{}
- methodsData = make(M)
- )
- for method, t := range BeeApp.Handlers.routers {
-
- resultList := new([][]string)
-
- printTree(resultList, t)
-
- methods = append(methods, template.HTMLEscapeString(method))
- methodsData[template.HTMLEscapeString(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{
- template.HTMLEscapeString(v.pattern),
- template.HTMLEscapeString(fmt.Sprintf("%s", v.methods)),
- template.HTMLEscapeString(v.controllerType.String()),
- }
- *resultList = append(*resultList, result)
- } else if v.routerType == routerTypeRESTFul {
- var result = []string{
- template.HTMLEscapeString(v.pattern),
- template.HTMLEscapeString(fmt.Sprintf("%s", v.methods)),
- "",
- }
- *resultList = append(*resultList, result)
- } else if v.routerType == routerTypeHandler {
- var result = []string{
- template.HTMLEscapeString(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
- )
- governor.ProcessInput(command, &result)
- data["Content"] = template.HTMLEscapeString(result.String())
-
- if format == "json" && command == "gc summary" {
- dataJSON, err := json.Marshal(data)
- if err != nil {
- http.Error(rw, err.Error(), http.StatusInternalServerError)
- return
- }
- writeJSON(rw, dataJSON)
- return
- }
-
- data["Title"] = template.HTMLEscapeString(command)
- defaultTpl := defaultScriptsTpl
- if command == "gc summary" {
- defaultTpl = gcAjaxTpl
- }
- writeTemplate(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, r *http.Request) {
- var (
- result []string
- data = make(map[interface{}]interface{})
- resultList = new([][]string)
- content = M{
- "Fields": []string{"Name", "Message", "Status"},
- }
- )
-
- for name, h := range governor.AdminCheckList {
- if err := h.Check(); err != nil {
- result = []string{
- "error",
- template.HTMLEscapeString(name),
- template.HTMLEscapeString(err.Error()),
- }
- } else {
- result = []string{
- "success",
- template.HTMLEscapeString(name),
- "OK",
- }
- }
- *resultList = append(*resultList, result)
- }
-
- queryParams := r.URL.Query()
- jsonFlag := queryParams.Get("json")
- shouldReturnJSON, _ := strconv.ParseBool(jsonFlag)
-
- if shouldReturnJSON {
- response := buildHealthCheckResponseList(resultList)
- jsonResponse, err := json.Marshal(response)
-
- if err != nil {
- http.Error(rw, err.Error(), http.StatusInternalServerError)
- } else {
- writeJSON(rw, jsonResponse)
- }
- return
- }
-
- content["Data"] = resultList
- data["Content"] = content
- data["Title"] = "Health Check"
-
- writeTemplate(rw, data, healthCheckTpl, defaultScriptsTpl)
-}
-
-func buildHealthCheckResponseList(healthCheckResults *[][]string) []map[string]interface{} {
- response := make([]map[string]interface{}, len(*healthCheckResults))
-
- for i, healthCheckResult := range *healthCheckResults {
- currentResultMap := make(map[string]interface{})
-
- currentResultMap["name"] = healthCheckResult[0]
- currentResultMap["message"] = healthCheckResult[1]
- currentResultMap["status"] = healthCheckResult[2]
-
- response[i] = currentResultMap
- }
-
- return response
-
-}
-
func writeJSON(rw http.ResponseWriter, jsonData []byte) {
rw.Header().Set("Content-Type", "application/json")
rw.Write(jsonData)
}
-// 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 := task.AdminTaskList[taskname]; ok {
- if err := t.Run(nil); err != nil {
- data["Message"] = []string{"error", template.HTMLEscapeString(fmt.Sprintf("%s", err))}
- }
- data["Message"] = []string{"success", template.HTMLEscapeString(fmt.Sprintf("%s run success,Now the Status is
%s", taskname, t.GetStatus(nil)))}
- } else {
- data["Message"] = []string{"warning", template.HTMLEscapeString(fmt.Sprintf("there's no task which named: %s", taskname))}
- }
- }
-
- // List Tasks
- content := make(M)
- resultList := new([][]string)
- var fields = []string{
- "Task Name",
- "Task Spec",
- "Task Status",
- "Last Time",
- "",
- }
- for tname, tk := range task.AdminTaskList {
- result := []string{
- template.HTMLEscapeString(tname),
- template.HTMLEscapeString(tk.GetSpec(nil)),
- template.HTMLEscapeString(tk.GetStatus(nil)),
- template.HTMLEscapeString(tk.GetPrev(context2.Background()).String()),
- }
- *resultList = append(*resultList, result)
- }
-
- content["Fields"] = fields
- content["Data"] = resultList
- data["Content"] = content
- data["Title"] = "Tasks"
- writeTemplate(rw, data, tasksTpl, defaultScriptsTpl)
-}
-
-func writeTemplate(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
+ *HttpServer
}
// 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(task.AdminTaskList) > 0 {
task.StartTask()
}
@@ -444,18 +94,31 @@ func (admin *adminApp) Run() {
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()))
- }
+ admin.HttpServer.Run(addr)
+}
+
+func registerAdmin() error {
+ if BConfig.Listen.EnableAdmin {
+
+ c := &adminController{
+ servers: make([]*HttpServer, 0, 2),
+ }
+ beeAdminApp = &adminApp{
+ HttpServer: NewHttpServerWithCfg(*BConfig),
+ }
+ // keep in mind that all data should be html escaped to avoid XSS attack
+ beeAdminApp.Router("/", c, "get:AdminIndex")
+ beeAdminApp.Router("/qps", c, "get:QpsIndex")
+ beeAdminApp.Router("/prof", c, "get:ProfIndex")
+ beeAdminApp.Router("/healthcheck", c, "get:Healthcheck")
+ beeAdminApp.Router("/task", c, "get:TaskStatus")
+ beeAdminApp.Router("/listconf", c, "get:ListConf")
+ beeAdminApp.Router("/metrics", c, "get:PrometheusMetrics")
+
+ go beeAdminApp.Run()
+ }
+ return nil
}
diff --git a/pkg/server/web/admin_controller.go b/pkg/server/web/admin_controller.go
new file mode 100644
index 00000000..c53c54cf
--- /dev/null
+++ b/pkg/server/web/admin_controller.go
@@ -0,0 +1,305 @@
+// Copyright 2020
+//
+// 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 web
+
+import (
+ "bytes"
+ context2 "context"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "strconv"
+ "text/template"
+
+ "github.com/prometheus/client_golang/prometheus/promhttp"
+
+ "github.com/astaxie/beego/pkg/infrastructure/governor"
+ "github.com/astaxie/beego/pkg/task"
+)
+
+type adminController struct {
+ Controller
+ servers []*HttpServer
+}
+
+func (a *adminController) registerHttpServer(svr *HttpServer) {
+ a.servers = append(a.servers, svr)
+}
+
+// ProfIndex is a http.Handler for showing profile command.
+// it's in url pattern "/prof" in admin module.
+func (a *adminController) ProfIndex() {
+ rw, r := a.Ctx.ResponseWriter, a.Ctx.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
+ )
+ governor.ProcessInput(command, &result)
+ data["Content"] = template.HTMLEscapeString(result.String())
+
+ if format == "json" && command == "gc summary" {
+ dataJSON, err := json.Marshal(data)
+ if err != nil {
+ http.Error(rw, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ writeJSON(rw, dataJSON)
+ return
+ }
+
+ data["Title"] = template.HTMLEscapeString(command)
+ defaultTpl := defaultScriptsTpl
+ if command == "gc summary" {
+ defaultTpl = gcAjaxTpl
+ }
+ writeTemplate(rw, data, profillingTpl, defaultTpl)
+}
+
+func (a *adminController) PrometheusMetrics() {
+ promhttp.Handler().ServeHTTP(a.Ctx.ResponseWriter, a.Ctx.Request)
+}
+
+// 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 (a *adminController) TaskStatus() {
+
+ rw, req := a.Ctx.ResponseWriter, a.Ctx.Request
+
+ data := make(map[interface{}]interface{})
+
+ // Run Task
+ req.ParseForm()
+ taskname := req.Form.Get("taskname")
+ if taskname != "" {
+ if t, ok := task.AdminTaskList[taskname]; ok {
+ if err := t.Run(nil); err != nil {
+ data["Message"] = []string{"error", template.HTMLEscapeString(fmt.Sprintf("%s", err))}
+ }
+ data["Message"] = []string{"success", template.HTMLEscapeString(fmt.Sprintf("%s run success,Now the Status is
%s", taskname, t.GetStatus(nil)))}
+ } else {
+ data["Message"] = []string{"warning", template.HTMLEscapeString(fmt.Sprintf("there's no task which named: %s", taskname))}
+ }
+ }
+
+ // List Tasks
+ content := make(M)
+ resultList := new([][]string)
+ var fields = []string{
+ "Task Name",
+ "Task Spec",
+ "Task Status",
+ "Last Time",
+ "",
+ }
+ for tname, tk := range task.AdminTaskList {
+ result := []string{
+ template.HTMLEscapeString(tname),
+ template.HTMLEscapeString(tk.GetSpec(nil)),
+ template.HTMLEscapeString(tk.GetStatus(nil)),
+ template.HTMLEscapeString(tk.GetPrev(context2.Background()).String()),
+ }
+ *resultList = append(*resultList, result)
+ }
+
+ content["Fields"] = fields
+ content["Data"] = resultList
+ data["Content"] = content
+ data["Title"] = "Tasks"
+ writeTemplate(rw, data, tasksTpl, defaultScriptsTpl)
+}
+
+func (a *adminController) AdminIndex() {
+ // AdminIndex is the default http.Handler for admin module.
+ // it matches url pattern "/".
+ writeTemplate(a.Ctx.ResponseWriter, map[interface{}]interface{}{}, indexTpl, defaultScriptsTpl)
+}
+
+// Healthcheck is a http.Handler calling health checking and showing the result.
+// it's in "/healthcheck" pattern in admin module.
+func (a *adminController) Healthcheck() {
+ heathCheck(a.Ctx.ResponseWriter, a.Ctx.Request)
+}
+
+func heathCheck(rw http.ResponseWriter, r *http.Request) {
+ var (
+ result []string
+ data = make(map[interface{}]interface{})
+ resultList = new([][]string)
+ content = M{
+ "Fields": []string{"Name", "Message", "Status"},
+ }
+ )
+
+ for name, h := range governor.AdminCheckList {
+ if err := h.Check(); err != nil {
+ result = []string{
+ "error",
+ template.HTMLEscapeString(name),
+ template.HTMLEscapeString(err.Error()),
+ }
+ } else {
+ result = []string{
+ "success",
+ template.HTMLEscapeString(name),
+ "OK",
+ }
+ }
+ *resultList = append(*resultList, result)
+ }
+
+ queryParams := r.URL.Query()
+ jsonFlag := queryParams.Get("json")
+ shouldReturnJSON, _ := strconv.ParseBool(jsonFlag)
+
+ if shouldReturnJSON {
+ response := buildHealthCheckResponseList(resultList)
+ jsonResponse, err := json.Marshal(response)
+
+ if err != nil {
+ http.Error(rw, err.Error(), http.StatusInternalServerError)
+ } else {
+ writeJSON(rw, jsonResponse)
+ }
+ return
+ }
+
+ content["Data"] = resultList
+ data["Content"] = content
+ data["Title"] = "Health Check"
+
+ writeTemplate(rw, data, healthCheckTpl, defaultScriptsTpl)
+}
+
+// QpsIndex is the http.Handler for writing qps statistics map result info in http.ResponseWriter.
+// it's registered with url pattern "/qps" in admin module.
+func (a *adminController) QpsIndex() {
+ data := make(map[interface{}]interface{})
+ data["Content"] = StatisticsMap.GetMap()
+
+ // do html escape before display path, avoid xss
+ if content, ok := (data["Content"]).(M); ok {
+ if resultLists, ok := (content["Data"]).([][]string); ok {
+ for i := range resultLists {
+ if len(resultLists[i]) > 0 {
+ resultLists[i][0] = template.HTMLEscapeString(resultLists[i][0])
+ }
+ }
+ }
+ }
+ writeTemplate(a.Ctx.ResponseWriter, 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 (a *adminController) ListConf() {
+ rw := a.Ctx.ResponseWriter
+ r := a.Ctx.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(M)
+ list("BConfig", BConfig, m)
+ m["appConfigPath"] = template.HTMLEscapeString(appConfigPath)
+ m["appConfigProvider"] = template.HTMLEscapeString(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 := BeeApp.PrintTree()
+ content["Fields"] = []string{
+ "Router Pattern",
+ "Methods",
+ "Controller",
+ }
+ data["Content"] = content
+ data["Title"] = "Routers"
+ writeTemplate(rw, data, routerAndFilterTpl, defaultScriptsTpl)
+ case "filter":
+ var (
+ content = M{
+ "Fields": []string{
+ "Router Pattern",
+ "Filter Function",
+ },
+ }
+ )
+
+ filterTypeData := BeeApp.reportFilter()
+
+ filterTypes := make([]string, 0, len(filterTypeData))
+ for k, _ := range filterTypeData {
+ filterTypes = append(filterTypes, k)
+ }
+
+ content["Data"] = filterTypeData
+ content["Methods"] = filterTypes
+
+ data["Content"] = content
+ data["Title"] = "Filters"
+ writeTemplate(rw, data, routerAndFilterTpl, defaultScriptsTpl)
+ default:
+ rw.Write([]byte("command not support"))
+ }
+}
+
+func writeTemplate(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)
+}
+
+func buildHealthCheckResponseList(healthCheckResults *[][]string) []map[string]interface{} {
+ response := make([]map[string]interface{}, len(*healthCheckResults))
+
+ for i, healthCheckResult := range *healthCheckResults {
+ currentResultMap := make(map[string]interface{})
+
+ currentResultMap["name"] = healthCheckResult[0]
+ currentResultMap["message"] = healthCheckResult[1]
+ currentResultMap["status"] = healthCheckResult[2]
+
+ response[i] = currentResultMap
+ }
+
+ return response
+
+}
+
+// PrintTree print all routers
+// Deprecated using BeeApp directly
+func PrintTree() M {
+ return BeeApp.PrintTree()
+}
diff --git a/pkg/server/web/admin_test.go b/pkg/server/web/admin_test.go
index acc67aeb..d04ac319 100644
--- a/pkg/server/web/admin_test.go
+++ b/pkg/server/web/admin_test.go
@@ -136,7 +136,7 @@ func TestHealthCheckHandlerDefault(t *testing.T) {
w := httptest.NewRecorder()
- handler := http.HandlerFunc(healthcheck)
+ handler := http.HandlerFunc(heathCheck)
handler.ServeHTTP(w, req)
@@ -197,7 +197,7 @@ func TestHealthCheckHandlerReturnsJSON(t *testing.T) {
w := httptest.NewRecorder()
- handler := http.HandlerFunc(healthcheck)
+ handler := http.HandlerFunc(heathCheck)
handler.ServeHTTP(w, req)
if status := w.Code; status != http.StatusOK {
diff --git a/pkg/server/web/app.go b/pkg/server/web/app.go
deleted file mode 100644
index 7511c7fe..00000000
--- a/pkg/server/web/app.go
+++ /dev/null
@@ -1,506 +0,0 @@
-// 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 web
-
-import (
- "crypto/tls"
- "crypto/x509"
- "fmt"
- "io/ioutil"
- "net"
- "net/http"
- "net/http/fcgi"
- "os"
- "path"
- "strings"
- "time"
-
- "golang.org/x/crypto/acme/autocert"
-
- "github.com/astaxie/beego/pkg/infrastructure/logs"
-
- "github.com/astaxie/beego/pkg/infrastructure/utils"
- "github.com/astaxie/beego/pkg/server/web/grace"
-)
-
-var (
- // BeeApp is an application instance
- BeeApp *App
-)
-
-func init() {
- // create beego application
- BeeApp = NewApp()
-}
-
-// App defines beego application with a new PatternServeMux.
-type App struct {
- Handlers *ControllerRegister
- Server *http.Server
-}
-
-// NewApp returns a new beego application.
-func NewApp() *App {
- cr := NewControllerRegister()
- app := &App{Handlers: cr, Server: &http.Server{}}
- return app
-}
-
-// MiddleWare function for http.Handler
-type MiddleWare func(http.Handler) http.Handler
-
-// Run beego application.
-func (app *App) Run(mws ...MiddleWare) {
- addr := BConfig.Listen.HTTPAddr
-
- if BConfig.Listen.HTTPPort != 0 {
- addr = fmt.Sprintf("%s:%d", BConfig.Listen.HTTPAddr, BConfig.Listen.HTTPPort)
- }
-
- var (
- err error
- l net.Listener
- endRunning = make(chan bool, 1)
- )
-
- // run cgi server
- if BConfig.Listen.EnableFcgi {
- if BConfig.Listen.EnableStdIo {
- if err = fcgi.Serve(nil, app.Handlers); err == nil { // standard I/O
- logs.Info("Use FCGI via standard I/O")
- } else {
- logs.Critical("Cannot use FCGI via standard I/O", err)
- }
- return
- }
- if BConfig.Listen.HTTPPort == 0 {
- // remove the Socket file before start
- if utils.FileExists(addr) {
- os.Remove(addr)
- }
- l, err = net.Listen("unix", addr)
- } else {
- l, err = net.Listen("tcp", addr)
- }
- if err != nil {
- logs.Critical("Listen: ", err)
- }
- if err = fcgi.Serve(l, app.Handlers); err != nil {
- logs.Critical("fcgi.Serve: ", err)
- }
- return
- }
-
- app.Server.Handler = app.Handlers
- for i := len(mws) - 1; i >= 0; i-- {
- if mws[i] == nil {
- continue
- }
- app.Server.Handler = mws[i](app.Server.Handler)
- }
- app.Server.ReadTimeout = time.Duration(BConfig.Listen.ServerTimeOut) * time.Second
- app.Server.WriteTimeout = time.Duration(BConfig.Listen.ServerTimeOut) * time.Second
- app.Server.ErrorLog = logs.GetLogger("HTTP")
-
- // run graceful mode
- if BConfig.Listen.Graceful {
- httpsAddr := BConfig.Listen.HTTPSAddr
- app.Server.Addr = httpsAddr
- if BConfig.Listen.EnableHTTPS || BConfig.Listen.EnableMutualHTTPS {
- go func() {
- time.Sleep(1000 * time.Microsecond)
- if BConfig.Listen.HTTPSPort != 0 {
- httpsAddr = fmt.Sprintf("%s:%d", BConfig.Listen.HTTPSAddr, BConfig.Listen.HTTPSPort)
- app.Server.Addr = httpsAddr
- }
- server := grace.NewServer(httpsAddr, app.Server.Handler)
- server.Server.ReadTimeout = app.Server.ReadTimeout
- server.Server.WriteTimeout = app.Server.WriteTimeout
- if BConfig.Listen.EnableMutualHTTPS {
- if err := server.ListenAndServeMutualTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile, BConfig.Listen.TrustCaFile); err != nil {
- logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid()))
- time.Sleep(100 * time.Microsecond)
- }
- } else {
- if BConfig.Listen.AutoTLS {
- m := autocert.Manager{
- Prompt: autocert.AcceptTOS,
- HostPolicy: autocert.HostWhitelist(BConfig.Listen.Domains...),
- Cache: autocert.DirCache(BConfig.Listen.TLSCacheDir),
- }
- app.Server.TLSConfig = &tls.Config{GetCertificate: m.GetCertificate}
- BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile = "", ""
- }
- if err := server.ListenAndServeTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile); err != nil {
- logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid()))
- time.Sleep(100 * time.Microsecond)
- }
- }
- endRunning <- true
- }()
- }
- if BConfig.Listen.EnableHTTP {
- go func() {
- server := grace.NewServer(addr, app.Server.Handler)
- server.Server.ReadTimeout = app.Server.ReadTimeout
- server.Server.WriteTimeout = app.Server.WriteTimeout
- if BConfig.Listen.ListenTCP4 {
- server.Network = "tcp4"
- }
- if err := server.ListenAndServe(); err != nil {
- logs.Critical("ListenAndServe: ", err, fmt.Sprintf("%d", os.Getpid()))
- time.Sleep(100 * time.Microsecond)
- }
- endRunning <- true
- }()
- }
- <-endRunning
- return
- }
-
- // run normal mode
- if BConfig.Listen.EnableHTTPS || BConfig.Listen.EnableMutualHTTPS {
- go func() {
- time.Sleep(1000 * time.Microsecond)
- if BConfig.Listen.HTTPSPort != 0 {
- app.Server.Addr = fmt.Sprintf("%s:%d", BConfig.Listen.HTTPSAddr, BConfig.Listen.HTTPSPort)
- } else if BConfig.Listen.EnableHTTP {
- logs.Info("Start https server error, conflict with http. Please reset https port")
- return
- }
- logs.Info("https server Running on https://%s", app.Server.Addr)
- if BConfig.Listen.AutoTLS {
- m := autocert.Manager{
- Prompt: autocert.AcceptTOS,
- HostPolicy: autocert.HostWhitelist(BConfig.Listen.Domains...),
- Cache: autocert.DirCache(BConfig.Listen.TLSCacheDir),
- }
- app.Server.TLSConfig = &tls.Config{GetCertificate: m.GetCertificate}
- BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile = "", ""
- } else if BConfig.Listen.EnableMutualHTTPS {
- pool := x509.NewCertPool()
- data, err := ioutil.ReadFile(BConfig.Listen.TrustCaFile)
- if err != nil {
- logs.Info("MutualHTTPS should provide TrustCaFile")
- return
- }
- pool.AppendCertsFromPEM(data)
- app.Server.TLSConfig = &tls.Config{
- ClientCAs: pool,
- ClientAuth: tls.ClientAuthType(BConfig.Listen.ClientAuth),
- }
- }
- if err := app.Server.ListenAndServeTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile); err != nil {
- logs.Critical("ListenAndServeTLS: ", err)
- time.Sleep(100 * time.Microsecond)
- endRunning <- true
- }
- }()
-
- }
- if BConfig.Listen.EnableHTTP {
- go func() {
- app.Server.Addr = addr
- logs.Info("http server Running on http://%s", app.Server.Addr)
- if BConfig.Listen.ListenTCP4 {
- ln, err := net.Listen("tcp4", app.Server.Addr)
- if err != nil {
- logs.Critical("ListenAndServe: ", err)
- time.Sleep(100 * time.Microsecond)
- endRunning <- true
- return
- }
- if err = app.Server.Serve(ln); err != nil {
- logs.Critical("ListenAndServe: ", err)
- time.Sleep(100 * time.Microsecond)
- endRunning <- true
- return
- }
- } else {
- if err := app.Server.ListenAndServe(); err != nil {
- logs.Critical("ListenAndServe: ", err)
- time.Sleep(100 * time.Microsecond)
- endRunning <- true
- }
- }
- }()
- }
- <-endRunning
-}
-
-// Router adds a patterned controller handler to BeeApp.
-// it's an alias method of App.Router.
-// usage:
-// simple router
-// beego.Router("/admin", &admin.UserController{})
-// beego.Router("/admin/index", &admin.ArticleController{})
-//
-// regex router
-//
-// beego.Router("/api/:id([0-9]+)", &controllers.RController{})
-//
-// custom rules
-// beego.Router("/api/list",&RestController{},"*:ListFood")
-// beego.Router("/api/create",&RestController{},"post:CreateFood")
-// beego.Router("/api/update",&RestController{},"put:UpdateFood")
-// beego.Router("/api/delete",&RestController{},"delete:DeleteFood")
-func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *App {
- BeeApp.Handlers.Add(rootpath, c, mappingMethods...)
- return BeeApp
-}
-
-// UnregisterFixedRoute unregisters the route with the specified fixedRoute. It is particularly useful
-// in web applications that inherit most routes from a base webapp via the underscore
-// import, and aim to overwrite only certain paths.
-// The method parameter can be empty or "*" for all HTTP methods, or a particular
-// method type (e.g. "GET" or "POST") for selective removal.
-//
-// Usage (replace "GET" with "*" for all methods):
-// beego.UnregisterFixedRoute("/yourpreviouspath", "GET")
-// beego.Router("/yourpreviouspath", yourControllerAddress, "get:GetNewPage")
-func UnregisterFixedRoute(fixedRoute string, method string) *App {
- subPaths := splitPath(fixedRoute)
- if method == "" || method == "*" {
- for m := range HTTPMETHOD {
- if _, ok := BeeApp.Handlers.routers[m]; !ok {
- continue
- }
- if BeeApp.Handlers.routers[m].prefix == strings.Trim(fixedRoute, "/ ") {
- findAndRemoveSingleTree(BeeApp.Handlers.routers[m])
- continue
- }
- findAndRemoveTree(subPaths, BeeApp.Handlers.routers[m], m)
- }
- return BeeApp
- }
- // Single HTTP method
- um := strings.ToUpper(method)
- if _, ok := BeeApp.Handlers.routers[um]; ok {
- if BeeApp.Handlers.routers[um].prefix == strings.Trim(fixedRoute, "/ ") {
- findAndRemoveSingleTree(BeeApp.Handlers.routers[um])
- return BeeApp
- }
- findAndRemoveTree(subPaths, BeeApp.Handlers.routers[um], um)
- }
- return BeeApp
-}
-
-func findAndRemoveTree(paths []string, entryPointTree *Tree, method string) {
- for i := range entryPointTree.fixrouters {
- if entryPointTree.fixrouters[i].prefix == paths[0] {
- if len(paths) == 1 {
- if len(entryPointTree.fixrouters[i].fixrouters) > 0 {
- // If the route had children subtrees, remove just the functional leaf,
- // to allow children to function as before
- if len(entryPointTree.fixrouters[i].leaves) > 0 {
- entryPointTree.fixrouters[i].leaves[0] = nil
- entryPointTree.fixrouters[i].leaves = entryPointTree.fixrouters[i].leaves[1:]
- }
- } else {
- // Remove the *Tree from the fixrouters slice
- entryPointTree.fixrouters[i] = nil
-
- if i == len(entryPointTree.fixrouters)-1 {
- entryPointTree.fixrouters = entryPointTree.fixrouters[:i]
- } else {
- entryPointTree.fixrouters = append(entryPointTree.fixrouters[:i], entryPointTree.fixrouters[i+1:len(entryPointTree.fixrouters)]...)
- }
- }
- return
- }
- findAndRemoveTree(paths[1:], entryPointTree.fixrouters[i], method)
- }
- }
-}
-
-func findAndRemoveSingleTree(entryPointTree *Tree) {
- if entryPointTree == nil {
- return
- }
- if len(entryPointTree.fixrouters) > 0 {
- // If the route had children subtrees, remove just the functional leaf,
- // to allow children to function as before
- if len(entryPointTree.leaves) > 0 {
- entryPointTree.leaves[0] = nil
- entryPointTree.leaves = entryPointTree.leaves[1:]
- }
- }
-}
-
-// Include will generate router file in the router/xxx.go from the controller's comments
-// usage:
-// beego.Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{})
-// type BankAccount struct{
-// beego.Controller
-// }
-//
-// register the function
-// func (b *BankAccount)Mapping(){
-// b.Mapping("ShowAccount" , b.ShowAccount)
-// b.Mapping("ModifyAccount", b.ModifyAccount)
-// }
-//
-// //@router /account/:id [get]
-// func (b *BankAccount) ShowAccount(){
-// //logic
-// }
-//
-//
-// //@router /account/:id [post]
-// func (b *BankAccount) ModifyAccount(){
-// //logic
-// }
-//
-// the comments @router url methodlist
-// url support all the function Router's pattern
-// methodlist [get post head put delete options *]
-func Include(cList ...ControllerInterface) *App {
- BeeApp.Handlers.Include(cList...)
- return BeeApp
-}
-
-// RESTRouter adds a restful controller handler to BeeApp.
-// its' controller implements beego.ControllerInterface and
-// defines a param "pattern/:objectId" to visit each resource.
-func RESTRouter(rootpath string, c ControllerInterface) *App {
- Router(rootpath, c)
- Router(path.Join(rootpath, ":objectId"), c)
- return BeeApp
-}
-
-// AutoRouter adds defined controller handler to BeeApp.
-// it's same to App.AutoRouter.
-// if beego.AddAuto(&MainContorlller{}) and MainController has methods List and Page,
-// visit the url /main/list to exec List function or /main/page to exec Page function.
-func AutoRouter(c ControllerInterface) *App {
- BeeApp.Handlers.AddAuto(c)
- return BeeApp
-}
-
-// AutoPrefix adds controller handler to BeeApp with prefix.
-// it's same to App.AutoRouterWithPrefix.
-// if beego.AutoPrefix("/admin",&MainContorlller{}) and MainController has methods List and Page,
-// visit the url /admin/main/list to exec List function or /admin/main/page to exec Page function.
-func AutoPrefix(prefix string, c ControllerInterface) *App {
- BeeApp.Handlers.AddAutoPrefix(prefix, c)
- return BeeApp
-}
-
-// Get used to register router for Get method
-// usage:
-// beego.Get("/", func(ctx *context.Context){
-// ctx.Output.Body("hello world")
-// })
-func Get(rootpath string, f FilterFunc) *App {
- BeeApp.Handlers.Get(rootpath, f)
- return BeeApp
-}
-
-// Post used to register router for Post method
-// usage:
-// beego.Post("/api", func(ctx *context.Context){
-// ctx.Output.Body("hello world")
-// })
-func Post(rootpath string, f FilterFunc) *App {
- BeeApp.Handlers.Post(rootpath, f)
- return BeeApp
-}
-
-// Delete used to register router for Delete method
-// usage:
-// beego.Delete("/api", func(ctx *context.Context){
-// ctx.Output.Body("hello world")
-// })
-func Delete(rootpath string, f FilterFunc) *App {
- BeeApp.Handlers.Delete(rootpath, f)
- return BeeApp
-}
-
-// Put used to register router for Put method
-// usage:
-// beego.Put("/api", func(ctx *context.Context){
-// ctx.Output.Body("hello world")
-// })
-func Put(rootpath string, f FilterFunc) *App {
- BeeApp.Handlers.Put(rootpath, f)
- return BeeApp
-}
-
-// Head used to register router for Head method
-// usage:
-// beego.Head("/api", func(ctx *context.Context){
-// ctx.Output.Body("hello world")
-// })
-func Head(rootpath string, f FilterFunc) *App {
- BeeApp.Handlers.Head(rootpath, f)
- return BeeApp
-}
-
-// Options used to register router for Options method
-// usage:
-// beego.Options("/api", func(ctx *context.Context){
-// ctx.Output.Body("hello world")
-// })
-func Options(rootpath string, f FilterFunc) *App {
- BeeApp.Handlers.Options(rootpath, f)
- return BeeApp
-}
-
-// Patch used to register router for Patch method
-// usage:
-// beego.Patch("/api", func(ctx *context.Context){
-// ctx.Output.Body("hello world")
-// })
-func Patch(rootpath string, f FilterFunc) *App {
- BeeApp.Handlers.Patch(rootpath, f)
- return BeeApp
-}
-
-// Any used to register router for all methods
-// usage:
-// beego.Any("/api", func(ctx *context.Context){
-// ctx.Output.Body("hello world")
-// })
-func Any(rootpath string, f FilterFunc) *App {
- BeeApp.Handlers.Any(rootpath, f)
- return BeeApp
-}
-
-// Handler used to register a Handler router
-// usage:
-// beego.Handler("/api", http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
-// fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
-// }))
-func Handler(rootpath string, h http.Handler, options ...interface{}) *App {
- BeeApp.Handlers.Handler(rootpath, h, options...)
- return BeeApp
-}
-
-// InsertFilter adds a FilterFunc with pattern condition and action constant.
-// The pos means action constant including
-// beego.BeforeStatic, beego.BeforeRouter, beego.BeforeExec, beego.AfterExec and beego.FinishRouter.
-// The bool params is for setting the returnOnOutput value (false allows multiple filters to execute)
-func InsertFilter(pattern string, pos int, filter FilterFunc, opts ...FilterOpt) *App {
- BeeApp.Handlers.InsertFilter(pattern, pos, filter, opts...)
- return BeeApp
-}
-
-// InsertFilterChain adds a FilterFunc built by filterChain.
-// This filter will be executed before all filters.
-// the filter's behavior is like stack
-func InsertFilterChain(pattern string, filterChain FilterChain, opts ...FilterOpt) *App {
- BeeApp.Handlers.InsertFilterChain(pattern, filterChain, opts...)
- return BeeApp
-}
diff --git a/pkg/server/web/beego.go b/pkg/server/web/beego.go
index 76e7b85e..14e51a94 100644
--- a/pkg/server/web/beego.go
+++ b/pkg/server/web/beego.go
@@ -17,14 +17,10 @@ package web
import (
"os"
"path/filepath"
- "strconv"
- "strings"
+ "sync"
)
const (
- // VERSION represent beego web framework version.
- VERSION = "1.12.2"
-
// DEV is for develop
DEV = "dev"
// PROD is for production
@@ -38,7 +34,7 @@ type M map[string]interface{}
type hookfunc func() error
var (
- hooks = make([]hookfunc, 0) //hook function slice to store the hookfunc
+ hooks = make([]hookfunc, 0) // hook function slice to store the hookfunc
)
// AddAPPStartHook is used to register the hookfunc
@@ -55,56 +51,39 @@ func AddAPPStartHook(hf ...hookfunc) {
// beego.Run("127.0.0.1:8089")
func Run(params ...string) {
- initBeforeHTTPRun()
-
if len(params) > 0 && params[0] != "" {
- strs := strings.Split(params[0], ":")
- if len(strs) > 0 && strs[0] != "" {
- BConfig.Listen.HTTPAddr = strs[0]
- }
- if len(strs) > 1 && strs[1] != "" {
- BConfig.Listen.HTTPPort, _ = strconv.Atoi(strs[1])
- }
-
- BConfig.Listen.Domains = params
+ BeeApp.Run(params[0])
}
-
- BeeApp.Run()
+ BeeApp.Run("")
}
// RunWithMiddleWares Run beego application with middlewares.
func RunWithMiddleWares(addr string, mws ...MiddleWare) {
- initBeforeHTTPRun()
-
- strs := strings.Split(addr, ":")
- if len(strs) > 0 && strs[0] != "" {
- BConfig.Listen.HTTPAddr = strs[0]
- BConfig.Listen.Domains = []string{strs[0]}
- }
- if len(strs) > 1 && strs[1] != "" {
- BConfig.Listen.HTTPPort, _ = strconv.Atoi(strs[1])
- }
-
- BeeApp.Run(mws...)
+ BeeApp.Run(addr, mws...)
}
-func initBeforeHTTPRun() {
- //init hooks
- AddAPPStartHook(
- registerMime,
- registerDefaultErrorHandler,
- registerSession,
- registerTemplate,
- registerAdmin,
- registerGzip,
- registerCommentRouter,
- )
+var initHttpOnce sync.Once
- for _, hk := range hooks {
- if err := hk(); err != nil {
- panic(err)
+// TODO move to module init function
+func initBeforeHTTPRun() {
+ initHttpOnce.Do(func() {
+ // init hooks
+ AddAPPStartHook(
+ registerMime,
+ registerDefaultErrorHandler,
+ registerSession,
+ registerTemplate,
+ registerAdmin,
+ registerGzip,
+ registerCommentRouter,
+ )
+
+ for _, hk := range hooks {
+ if err := hk(); err != nil {
+ panic(err)
+ }
}
- }
+ })
}
// TestBeegoInit is for test package init
diff --git a/pkg/server/web/config.go b/pkg/server/web/config.go
index 6e69a2fb..bc46b20e 100644
--- a/pkg/server/web/config.go
+++ b/pkg/server/web/config.go
@@ -24,6 +24,7 @@ import (
"runtime"
"strings"
+ "github.com/astaxie/beego/pkg"
"github.com/astaxie/beego/pkg/infrastructure/config"
"github.com/astaxie/beego/pkg/infrastructure/logs"
"github.com/astaxie/beego/pkg/infrastructure/session"
@@ -33,13 +34,14 @@ import (
)
// Config is the main struct for BConfig
+// TODO after supporting multiple servers, remove common config to somewhere else
type Config struct {
AppName string // Application name
RunMode string // Running Mode: dev | prod
RouterCaseSensitive bool
ServerName string
RecoverPanic bool
- RecoverFunc func(*context.Context)
+ RecoverFunc func(*context.Context, *Config)
CopyRequestBody bool
EnableGzip bool
MaxMemory int64
@@ -167,15 +169,15 @@ func init() {
}
}
-func recoverPanic(ctx *context.Context) {
+func defaultRecoverPanic(ctx *context.Context, cfg *Config) {
if err := recover(); err != nil {
if err == ErrAbort {
return
}
- if !BConfig.RecoverPanic {
+ if !cfg.RecoverPanic {
panic(err)
}
- if BConfig.EnableErrorsShow {
+ if cfg.EnableErrorsShow {
if _, ok := ErrorMaps[fmt.Sprint(err)]; ok {
exception(fmt.Sprint(err), ctx)
return
@@ -192,7 +194,7 @@ func recoverPanic(ctx *context.Context) {
logs.Critical(fmt.Sprintf("%s:%d", file, line))
stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line))
}
- if BConfig.RunMode == DEV && BConfig.EnableErrorsRender {
+ if cfg.RunMode == DEV && cfg.EnableErrorsRender {
showErr(err, ctx, stack)
}
if ctx.Output.Status != 0 {
@@ -204,18 +206,18 @@ func recoverPanic(ctx *context.Context) {
}
func newBConfig() *Config {
- return &Config{
+ res := &Config{
AppName: "beego",
RunMode: PROD,
RouterCaseSensitive: true,
- ServerName: "beegoServer:" + VERSION,
+ ServerName: "beegoServer:" + pkg.VERSION,
RecoverPanic: true,
- RecoverFunc: recoverPanic,
- CopyRequestBody: false,
- EnableGzip: false,
- MaxMemory: 1 << 26, // 64MB
- EnableErrorsShow: true,
- EnableErrorsRender: true,
+
+ CopyRequestBody: false,
+ EnableGzip: false,
+ MaxMemory: 1 << 26, // 64MB
+ EnableErrorsShow: true,
+ EnableErrorsRender: true,
Listen: Listen{
Graceful: false,
ServerTimeOut: 0,
@@ -278,6 +280,9 @@ func newBConfig() *Config {
Outputs: map[string]string{"console": ""},
},
}
+
+ res.RecoverFunc = defaultRecoverPanic
+ return res
}
// now only support ini, next will support json.
diff --git a/pkg/server/web/error.go b/pkg/server/web/error.go
index b62fb70d..a005c110 100644
--- a/pkg/server/web/error.go
+++ b/pkg/server/web/error.go
@@ -23,6 +23,7 @@ import (
"strconv"
"strings"
+ "github.com/astaxie/beego/pkg"
"github.com/astaxie/beego/pkg/infrastructure/utils"
"github.com/astaxie/beego/pkg/server/web/context"
@@ -91,7 +92,7 @@ func showErr(err interface{}, ctx *context.Context, stack string) {
"RequestURL": ctx.Input.URI(),
"RemoteAddr": ctx.Input.IP(),
"Stack": stack,
- "BeegoVersion": VERSION,
+ "BeegoVersion": pkg.VERSION,
"GoVersion": runtime.Version(),
}
t.Execute(ctx.ResponseWriter, data)
@@ -378,7 +379,7 @@ func responseError(rw http.ResponseWriter, r *http.Request, errCode int, errCont
t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := M{
"Title": http.StatusText(errCode),
- "BeegoVersion": VERSION,
+ "BeegoVersion": pkg.VERSION,
"Content": template.HTML(errContent),
}
t.Execute(rw, data)
@@ -388,7 +389,7 @@ func responseError(rw http.ResponseWriter, r *http.Request, errCode int, errCont
// usage:
// beego.ErrorHandler("404",NotFound)
// beego.ErrorHandler("500",InternalServerError)
-func ErrorHandler(code string, h http.HandlerFunc) *App {
+func ErrorHandler(code string, h http.HandlerFunc) *HttpServer {
ErrorMaps[code] = &errorInfo{
errorType: errorTypeHandler,
handler: h,
@@ -400,7 +401,7 @@ func ErrorHandler(code string, h http.HandlerFunc) *App {
// ErrorController registers ControllerInterface to each http err code string.
// usage:
// beego.ErrorController(&controllers.ErrorController{})
-func ErrorController(c ControllerInterface) *App {
+func ErrorController(c ControllerInterface) *HttpServer {
reflectVal := reflect.ValueOf(c)
rt := reflectVal.Type()
ct := reflect.Indirect(reflectVal).Type()
diff --git a/pkg/server/web/filter/prometheus/filter.go b/pkg/server/web/filter/prometheus/filter.go
index f4231c73..eb5b0b78 100644
--- a/pkg/server/web/filter/prometheus/filter.go
+++ b/pkg/server/web/filter/prometheus/filter.go
@@ -21,6 +21,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
+ "github.com/astaxie/beego/pkg"
"github.com/astaxie/beego/pkg/server/web"
"github.com/astaxie/beego/pkg/server/web/context"
)
@@ -63,13 +64,13 @@ func registerBuildInfo() {
Help: "The building information",
ConstLabels: map[string]string{
"appname": web.BConfig.AppName,
- "build_version": web.BuildVersion,
- "build_revision": web.BuildGitRevision,
- "build_status": web.BuildStatus,
- "build_tag": web.BuildTag,
- "build_time": strings.Replace(web.BuildTime, "--", " ", 1),
- "go_version": web.GoVersion,
- "git_branch": web.GitBranch,
+ "build_version": pkg.BuildVersion,
+ "build_revision": pkg.BuildGitRevision,
+ "build_status": pkg.BuildStatus,
+ "build_tag": pkg.BuildTag,
+ "build_time": strings.Replace(pkg.BuildTime, "--", " ", 1),
+ "go_version": pkg.GoVersion,
+ "git_branch": pkg.GitBranch,
"start_time": time.Now().Format("2006-01-02 15:04:05"),
},
}, []string{})
diff --git a/pkg/server/web/hooks.go b/pkg/server/web/hooks.go
index 080b2006..2f0cb159 100644
--- a/pkg/server/web/hooks.go
+++ b/pkg/server/web/hooks.go
@@ -9,7 +9,6 @@ import (
"github.com/astaxie/beego/pkg/infrastructure/logs"
"github.com/astaxie/beego/pkg/infrastructure/session"
-
"github.com/astaxie/beego/pkg/server/web/context"
)
@@ -87,13 +86,6 @@ func registerTemplate() error {
return nil
}
-func registerAdmin() error {
- if BConfig.Listen.EnableAdmin {
- go beeAdminApp.Run()
- }
- return nil
-}
-
func registerGzip() error {
if BConfig.EnableGzip {
context.InitGzip(
diff --git a/pkg/server/web/router.go b/pkg/server/web/router.go
index 3dd19a6f..a9d1b0cf 100644
--- a/pkg/server/web/router.go
+++ b/pkg/server/web/router.go
@@ -135,10 +135,18 @@ type ControllerRegister struct {
// the filter created by FilterChain
chainRoot *FilterRouter
+
+ cfg *Config
}
// NewControllerRegister returns a new ControllerRegister.
+// Usually you should not use this method
+// please use NewControllerRegisterWithCfg
func NewControllerRegister() *ControllerRegister {
+ return NewControllerRegisterWithCfg(BeeApp.Cfg)
+}
+
+func NewControllerRegisterWithCfg(cfg *Config) *ControllerRegister {
res := &ControllerRegister{
routers: make(map[string]*Tree),
policies: make(map[string]*Tree),
@@ -147,6 +155,7 @@ func NewControllerRegister() *ControllerRegister {
return beecontext.NewContext()
},
},
+ cfg: cfg,
}
res.chainRoot = newFilterRouter("/*", res.serveHttp, WithCaseSensitive(false))
return res
@@ -240,7 +249,7 @@ func (p *ControllerRegister) addWithMethodParams(pattern string, c ControllerInt
}
func (p *ControllerRegister) addToRouter(method, pattern string, r *ControllerInfo) {
- if !BConfig.RouterCaseSensitive {
+ if !p.cfg.RouterCaseSensitive {
pattern = strings.ToLower(pattern)
}
if t, ok := p.routers[method]; ok {
@@ -453,7 +462,7 @@ func (p *ControllerRegister) AddAutoPrefix(prefix string, c ControllerInterface)
// 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, opts ...FilterOpt) error {
- opts = append(opts, WithCaseSensitive(BConfig.RouterCaseSensitive))
+ opts = append(opts, WithCaseSensitive(p.cfg.RouterCaseSensitive))
mr := newFilterRouter(pattern, filter, opts...)
return p.insertFilterRouter(pos, mr)
}
@@ -472,7 +481,7 @@ func (p *ControllerRegister) InsertFilter(pattern string, pos int, filter Filter
func (p *ControllerRegister) InsertFilterChain(pattern string, chain FilterChain, opts ...FilterOpt) {
root := p.chainRoot
filterFunc := chain(root.filterFunc)
- opts = append(opts, WithCaseSensitive(BConfig.RouterCaseSensitive))
+ opts = append(opts, WithCaseSensitive(p.cfg.RouterCaseSensitive))
p.chainRoot = newFilterRouter(pattern, filterFunc, opts...)
p.chainRoot.next = root
@@ -669,14 +678,14 @@ func (p *ControllerRegister) serveHttp(ctx *beecontext.Context) {
isRunnable bool
)
- if BConfig.RecoverFunc != nil {
- defer BConfig.RecoverFunc(ctx)
+ if p.cfg.RecoverFunc != nil {
+ defer p.cfg.RecoverFunc(ctx, p.cfg)
}
- ctx.Output.EnableGzip = BConfig.EnableGzip
+ ctx.Output.EnableGzip = p.cfg.EnableGzip
- if BConfig.RunMode == DEV {
- ctx.Output.Header("Server", BConfig.ServerName)
+ if p.cfg.RunMode == DEV {
+ ctx.Output.Header("Server", p.cfg.ServerName)
}
urlPath := p.getUrlPath(ctx)
@@ -700,20 +709,20 @@ func (p *ControllerRegister) serveHttp(ctx *beecontext.Context) {
}
if r.Method != http.MethodGet && r.Method != http.MethodHead {
- if BConfig.CopyRequestBody && !ctx.Input.IsUpload() {
+ if p.cfg.CopyRequestBody && !ctx.Input.IsUpload() {
// connection will close if the incoming data are larger (RFC 7231, 6.5.11)
- if r.ContentLength > BConfig.MaxMemory {
+ if r.ContentLength > p.cfg.MaxMemory {
logs.Error(errors.New("payload too large"))
exception("413", ctx)
goto Admin
}
- ctx.Input.CopyBody(BConfig.MaxMemory)
+ ctx.Input.CopyBody(p.cfg.MaxMemory)
}
- ctx.Input.ParseFormOrMulitForm(BConfig.MaxMemory)
+ ctx.Input.ParseFormOrMulitForm(p.cfg.MaxMemory)
}
// session init
- if BConfig.WebConfig.Session.SessionOn {
+ if p.cfg.WebConfig.Session.SessionOn {
var err error
ctx.Input.CruSession, err = GlobalSessions.SessionStart(rw, r)
if err != nil {
@@ -819,7 +828,7 @@ func (p *ControllerRegister) serveHttp(ctx *beecontext.Context) {
execController.Prepare()
// if XSRF is Enable then check cookie where there has any cookie in the request's cookie _csrf
- if BConfig.WebConfig.EnableXSRF {
+ if p.cfg.WebConfig.EnableXSRF {
execController.XSRFToken()
if r.Method == http.MethodPost || r.Method == http.MethodDelete || r.Method == http.MethodPut ||
(r.Method == http.MethodPost && (ctx.Input.Query("_method") == http.MethodDelete || ctx.Input.Query("_method") == http.MethodPut)) {
@@ -864,7 +873,7 @@ func (p *ControllerRegister) serveHttp(ctx *beecontext.Context) {
// render template
if !ctx.ResponseWriter.Started && ctx.Output.Status == 0 {
- if BConfig.WebConfig.AutoRender {
+ if p.cfg.WebConfig.AutoRender {
if err := execController.Render(); err != nil {
logs.Error(err)
}
@@ -897,7 +906,7 @@ Admin:
timeDur := time.Since(startTime)
ctx.ResponseWriter.Elapsed = timeDur
- if BConfig.Listen.EnableAdmin {
+ if p.cfg.Listen.EnableAdmin {
pattern := ""
if routerInfo != nil {
pattern = routerInfo.pattern
@@ -912,7 +921,7 @@ Admin:
}
}
- if BConfig.RunMode == DEV && !BConfig.Log.AccessLogs {
+ if p.cfg.RunMode == DEV && !p.cfg.Log.AccessLogs {
match := map[bool]string{true: "match", false: "nomatch"}
devInfo := fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s",
ctx.Input.IP(),
@@ -935,7 +944,7 @@ Admin:
func (p *ControllerRegister) getUrlPath(ctx *beecontext.Context) string {
urlPath := ctx.Request.URL.Path
- if !BConfig.RouterCaseSensitive {
+ if !p.cfg.RouterCaseSensitive {
urlPath = strings.ToLower(urlPath)
}
return urlPath
@@ -958,7 +967,7 @@ func (p *ControllerRegister) handleParamResponse(context *beecontext.Context, ex
// 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 {
+ if !p.cfg.RouterCaseSensitive {
urlPath = strings.ToLower(urlPath)
}
httpMethod := context.Input.Method()
@@ -984,36 +993,5 @@ func toURL(params map[string]string) string {
// LogAccess logging info HTTP Access
func LogAccess(ctx *beecontext.Context, startTime *time.Time, statusCode int) {
- // Skip logging if AccessLogs config is false
- if !BConfig.Log.AccessLogs {
- return
- }
- // Skip logging static requests unless EnableStaticLogs config is true
- if !BConfig.Log.EnableStaticLogs && DefaultAccessLogFilter.Filter(ctx) {
- return
- }
- var (
- requestTime time.Time
- elapsedTime time.Duration
- r = ctx.Request
- )
- if startTime != nil {
- requestTime = *startTime
- elapsedTime = time.Since(*startTime)
- }
- record := &logs.AccessLogRecord{
- RemoteAddr: ctx.Input.IP(),
- RequestTime: requestTime,
- RequestMethod: r.Method,
- Request: fmt.Sprintf("%s %s %s", r.Method, r.RequestURI, r.Proto),
- ServerProtocol: r.Proto,
- Host: r.Host,
- Status: statusCode,
- ElapsedTime: elapsedTime,
- HTTPReferrer: r.Header.Get("Referer"),
- HTTPUserAgent: r.Header.Get("User-Agent"),
- RemoteUser: r.Header.Get("Remote-User"),
- BodyBytesSent: r.ContentLength,
- }
- logs.AccessLog(record, BConfig.Log.AccessLogsFormat)
+ BeeApp.LogAccess(ctx, startTime, statusCode)
}
diff --git a/pkg/server/web/router_test.go b/pkg/server/web/router_test.go
index 2863da3a..a59cde8b 100644
--- a/pkg/server/web/router_test.go
+++ b/pkg/server/web/router_test.go
@@ -381,7 +381,7 @@ func TestRouterHandlerAll(t *testing.T) {
}
//
-// Benchmarks NewApp:
+// Benchmarks NewHttpSever:
//
func beegoFilterFunc(ctx *context.Context) {
@@ -731,6 +731,8 @@ func TestRouterEntityTooLargeCopyBody(t *testing.T) {
BConfig.CopyRequestBody = true
BConfig.MaxMemory = 20
+ BeeApp.Cfg.CopyRequestBody = true
+ BeeApp.Cfg.MaxMemory = 20
b := bytes.NewBuffer([]byte("barbarbarbarbarbarbarbarbarbar"))
r, _ := http.NewRequest("POST", "/user/123", b)
w := httptest.NewRecorder()
diff --git a/pkg/server/web/server.go b/pkg/server/web/server.go
new file mode 100644
index 00000000..7bd9023d
--- /dev/null
+++ b/pkg/server/web/server.go
@@ -0,0 +1,752 @@
+// 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 web
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "fmt"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "net/http/fcgi"
+ "os"
+ "path"
+ "strconv"
+ "strings"
+ "text/template"
+ "time"
+
+ "golang.org/x/crypto/acme/autocert"
+
+ "github.com/astaxie/beego/pkg/infrastructure/logs"
+ beecontext "github.com/astaxie/beego/pkg/server/web/context"
+
+ "github.com/astaxie/beego/pkg/infrastructure/utils"
+ "github.com/astaxie/beego/pkg/server/web/grace"
+)
+
+var (
+ // BeeApp is an application instance
+ // If you are using single server, you could use this
+ // But if you need multiple servers, do not use this
+ BeeApp *HttpServer
+)
+
+func init() {
+ // create beego application
+ BeeApp = NewHttpSever()
+}
+
+// HttpServer defines beego application with a new PatternServeMux.
+type HttpServer struct {
+ Handlers *ControllerRegister
+ Server *http.Server
+ Cfg *Config
+}
+
+// NewHttpSever returns a new beego application.
+// this method will use the BConfig as the configure to create HttpServer
+// Be careful that when you update BConfig, the server's Cfg will not be updated
+func NewHttpSever() *HttpServer {
+ return NewHttpServerWithCfg(*BConfig)
+}
+
+// NewHttpServerWithCfg will create an sever with specific cfg
+func NewHttpServerWithCfg(cfg Config) *HttpServer {
+ cfgPtr := &cfg
+ cr := NewControllerRegisterWithCfg(cfgPtr)
+ app := &HttpServer{
+ Handlers: cr,
+ Server: &http.Server{},
+ Cfg: cfgPtr,
+ }
+
+ return app
+}
+
+// MiddleWare function for http.Handler
+type MiddleWare func(http.Handler) http.Handler
+
+// Run beego application.
+func (app *HttpServer) Run(addr string, mws ...MiddleWare) {
+
+ initBeforeHTTPRun()
+
+ app.initAddr(addr)
+
+ addr = app.Cfg.Listen.HTTPAddr
+
+ if app.Cfg.Listen.HTTPPort != 0 {
+ addr = fmt.Sprintf("%s:%d", app.Cfg.Listen.HTTPAddr, app.Cfg.Listen.HTTPPort)
+ }
+
+ var (
+ err error
+ l net.Listener
+ endRunning = make(chan bool, 1)
+ )
+
+ // run cgi server
+ if app.Cfg.Listen.EnableFcgi {
+ if app.Cfg.Listen.EnableStdIo {
+ if err = fcgi.Serve(nil, app.Handlers); err == nil { // standard I/O
+ logs.Info("Use FCGI via standard I/O")
+ } else {
+ logs.Critical("Cannot use FCGI via standard I/O", err)
+ }
+ return
+ }
+ if app.Cfg.Listen.HTTPPort == 0 {
+ // remove the Socket file before start
+ if utils.FileExists(addr) {
+ os.Remove(addr)
+ }
+ l, err = net.Listen("unix", addr)
+ } else {
+ l, err = net.Listen("tcp", addr)
+ }
+ if err != nil {
+ logs.Critical("Listen: ", err)
+ }
+ if err = fcgi.Serve(l, app.Handlers); err != nil {
+ logs.Critical("fcgi.Serve: ", err)
+ }
+ return
+ }
+
+ app.Server.Handler = app.Handlers
+ for i := len(mws) - 1; i >= 0; i-- {
+ if mws[i] == nil {
+ continue
+ }
+ app.Server.Handler = mws[i](app.Server.Handler)
+ }
+ app.Server.ReadTimeout = time.Duration(app.Cfg.Listen.ServerTimeOut) * time.Second
+ app.Server.WriteTimeout = time.Duration(app.Cfg.Listen.ServerTimeOut) * time.Second
+ app.Server.ErrorLog = logs.GetLogger("HTTP")
+
+ // run graceful mode
+ if app.Cfg.Listen.Graceful {
+ httpsAddr := app.Cfg.Listen.HTTPSAddr
+ app.Server.Addr = httpsAddr
+ if app.Cfg.Listen.EnableHTTPS || app.Cfg.Listen.EnableMutualHTTPS {
+ go func() {
+ time.Sleep(1000 * time.Microsecond)
+ if app.Cfg.Listen.HTTPSPort != 0 {
+ httpsAddr = fmt.Sprintf("%s:%d", app.Cfg.Listen.HTTPSAddr, app.Cfg.Listen.HTTPSPort)
+ app.Server.Addr = httpsAddr
+ }
+ server := grace.NewServer(httpsAddr, app.Server.Handler)
+ server.Server.ReadTimeout = app.Server.ReadTimeout
+ server.Server.WriteTimeout = app.Server.WriteTimeout
+ if app.Cfg.Listen.EnableMutualHTTPS {
+ if err := server.ListenAndServeMutualTLS(app.Cfg.Listen.HTTPSCertFile,
+ app.Cfg.Listen.HTTPSKeyFile,
+ app.Cfg.Listen.TrustCaFile); err != nil {
+ logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid()))
+ time.Sleep(100 * time.Microsecond)
+ }
+ } else {
+ if app.Cfg.Listen.AutoTLS {
+ m := autocert.Manager{
+ Prompt: autocert.AcceptTOS,
+ HostPolicy: autocert.HostWhitelist(app.Cfg.Listen.Domains...),
+ Cache: autocert.DirCache(app.Cfg.Listen.TLSCacheDir),
+ }
+ app.Server.TLSConfig = &tls.Config{GetCertificate: m.GetCertificate}
+ app.Cfg.Listen.HTTPSCertFile, app.Cfg.Listen.HTTPSKeyFile = "", ""
+ }
+ if err := server.ListenAndServeTLS(app.Cfg.Listen.HTTPSCertFile, app.Cfg.Listen.HTTPSKeyFile); err != nil {
+ logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid()))
+ time.Sleep(100 * time.Microsecond)
+ }
+ }
+ endRunning <- true
+ }()
+ }
+ if app.Cfg.Listen.EnableHTTP {
+ go func() {
+ server := grace.NewServer(addr, app.Server.Handler)
+ server.Server.ReadTimeout = app.Server.ReadTimeout
+ server.Server.WriteTimeout = app.Server.WriteTimeout
+ if app.Cfg.Listen.ListenTCP4 {
+ server.Network = "tcp4"
+ }
+ if err := server.ListenAndServe(); err != nil {
+ logs.Critical("ListenAndServe: ", err, fmt.Sprintf("%d", os.Getpid()))
+ time.Sleep(100 * time.Microsecond)
+ }
+ endRunning <- true
+ }()
+ }
+ <-endRunning
+ return
+ }
+
+ // run normal mode
+ if app.Cfg.Listen.EnableHTTPS || app.Cfg.Listen.EnableMutualHTTPS {
+ go func() {
+ time.Sleep(1000 * time.Microsecond)
+ if app.Cfg.Listen.HTTPSPort != 0 {
+ app.Server.Addr = fmt.Sprintf("%s:%d", app.Cfg.Listen.HTTPSAddr, app.Cfg.Listen.HTTPSPort)
+ } else if app.Cfg.Listen.EnableHTTP {
+ logs.Info("Start https server error, conflict with http. Please reset https port")
+ return
+ }
+ logs.Info("https server Running on https://%s", app.Server.Addr)
+ if app.Cfg.Listen.AutoTLS {
+ m := autocert.Manager{
+ Prompt: autocert.AcceptTOS,
+ HostPolicy: autocert.HostWhitelist(app.Cfg.Listen.Domains...),
+ Cache: autocert.DirCache(app.Cfg.Listen.TLSCacheDir),
+ }
+ app.Server.TLSConfig = &tls.Config{GetCertificate: m.GetCertificate}
+ app.Cfg.Listen.HTTPSCertFile, app.Cfg.Listen.HTTPSKeyFile = "", ""
+ } else if app.Cfg.Listen.EnableMutualHTTPS {
+ pool := x509.NewCertPool()
+ data, err := ioutil.ReadFile(app.Cfg.Listen.TrustCaFile)
+ if err != nil {
+ logs.Info("MutualHTTPS should provide TrustCaFile")
+ return
+ }
+ pool.AppendCertsFromPEM(data)
+ app.Server.TLSConfig = &tls.Config{
+ ClientCAs: pool,
+ ClientAuth: tls.ClientAuthType(app.Cfg.Listen.ClientAuth),
+ }
+ }
+ if err := app.Server.ListenAndServeTLS(app.Cfg.Listen.HTTPSCertFile, app.Cfg.Listen.HTTPSKeyFile); err != nil {
+ logs.Critical("ListenAndServeTLS: ", err)
+ time.Sleep(100 * time.Microsecond)
+ endRunning <- true
+ }
+ }()
+
+ }
+ if app.Cfg.Listen.EnableHTTP {
+ go func() {
+ app.Server.Addr = addr
+ logs.Info("http server Running on http://%s", app.Server.Addr)
+ if app.Cfg.Listen.ListenTCP4 {
+ ln, err := net.Listen("tcp4", app.Server.Addr)
+ if err != nil {
+ logs.Critical("ListenAndServe: ", err)
+ time.Sleep(100 * time.Microsecond)
+ endRunning <- true
+ return
+ }
+ if err = app.Server.Serve(ln); err != nil {
+ logs.Critical("ListenAndServe: ", err)
+ time.Sleep(100 * time.Microsecond)
+ endRunning <- true
+ return
+ }
+ } else {
+ if err := app.Server.ListenAndServe(); err != nil {
+ logs.Critical("ListenAndServe: ", err)
+ time.Sleep(100 * time.Microsecond)
+ endRunning <- true
+ }
+ }
+ }()
+ }
+ <-endRunning
+}
+
+// Router see HttpServer.Router
+func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *HttpServer {
+ return BeeApp.Router(rootpath, c, mappingMethods...)
+}
+
+// Router adds a patterned controller handler to BeeApp.
+// it's an alias method of HttpServer.Router.
+// usage:
+// simple router
+// beego.Router("/admin", &admin.UserController{})
+// beego.Router("/admin/index", &admin.ArticleController{})
+//
+// regex router
+//
+// beego.Router("/api/:id([0-9]+)", &controllers.RController{})
+//
+// custom rules
+// beego.Router("/api/list",&RestController{},"*:ListFood")
+// beego.Router("/api/create",&RestController{},"post:CreateFood")
+// beego.Router("/api/update",&RestController{},"put:UpdateFood")
+// beego.Router("/api/delete",&RestController{},"delete:DeleteFood")
+func (app *HttpServer) Router(rootPath string, c ControllerInterface, mappingMethods ...string) *HttpServer {
+ app.Handlers.Add(rootPath, c, mappingMethods...)
+ return app
+}
+
+// UnregisterFixedRoute see HttpServer.UnregisterFixedRoute
+func UnregisterFixedRoute(fixedRoute string, method string) *HttpServer {
+ return BeeApp.UnregisterFixedRoute(fixedRoute, method)
+}
+
+// UnregisterFixedRoute unregisters the route with the specified fixedRoute. It is particularly useful
+// in web applications that inherit most routes from a base webapp via the underscore
+// import, and aim to overwrite only certain paths.
+// The method parameter can be empty or "*" for all HTTP methods, or a particular
+// method type (e.g. "GET" or "POST") for selective removal.
+//
+// Usage (replace "GET" with "*" for all methods):
+// beego.UnregisterFixedRoute("/yourpreviouspath", "GET")
+// beego.Router("/yourpreviouspath", yourControllerAddress, "get:GetNewPage")
+func (app *HttpServer) UnregisterFixedRoute(fixedRoute string, method string) *HttpServer {
+ subPaths := splitPath(fixedRoute)
+ if method == "" || method == "*" {
+ for m := range HTTPMETHOD {
+ if _, ok := app.Handlers.routers[m]; !ok {
+ continue
+ }
+ if app.Handlers.routers[m].prefix == strings.Trim(fixedRoute, "/ ") {
+ findAndRemoveSingleTree(app.Handlers.routers[m])
+ continue
+ }
+ findAndRemoveTree(subPaths, app.Handlers.routers[m], m)
+ }
+ return app
+ }
+ // Single HTTP method
+ um := strings.ToUpper(method)
+ if _, ok := app.Handlers.routers[um]; ok {
+ if app.Handlers.routers[um].prefix == strings.Trim(fixedRoute, "/ ") {
+ findAndRemoveSingleTree(app.Handlers.routers[um])
+ return app
+ }
+ findAndRemoveTree(subPaths, app.Handlers.routers[um], um)
+ }
+ return app
+}
+
+func findAndRemoveTree(paths []string, entryPointTree *Tree, method string) {
+ for i := range entryPointTree.fixrouters {
+ if entryPointTree.fixrouters[i].prefix == paths[0] {
+ if len(paths) == 1 {
+ if len(entryPointTree.fixrouters[i].fixrouters) > 0 {
+ // If the route had children subtrees, remove just the functional leaf,
+ // to allow children to function as before
+ if len(entryPointTree.fixrouters[i].leaves) > 0 {
+ entryPointTree.fixrouters[i].leaves[0] = nil
+ entryPointTree.fixrouters[i].leaves = entryPointTree.fixrouters[i].leaves[1:]
+ }
+ } else {
+ // Remove the *Tree from the fixrouters slice
+ entryPointTree.fixrouters[i] = nil
+
+ if i == len(entryPointTree.fixrouters)-1 {
+ entryPointTree.fixrouters = entryPointTree.fixrouters[:i]
+ } else {
+ entryPointTree.fixrouters = append(entryPointTree.fixrouters[:i], entryPointTree.fixrouters[i+1:len(entryPointTree.fixrouters)]...)
+ }
+ }
+ return
+ }
+ findAndRemoveTree(paths[1:], entryPointTree.fixrouters[i], method)
+ }
+ }
+}
+
+func findAndRemoveSingleTree(entryPointTree *Tree) {
+ if entryPointTree == nil {
+ return
+ }
+ if len(entryPointTree.fixrouters) > 0 {
+ // If the route had children subtrees, remove just the functional leaf,
+ // to allow children to function as before
+ if len(entryPointTree.leaves) > 0 {
+ entryPointTree.leaves[0] = nil
+ entryPointTree.leaves = entryPointTree.leaves[1:]
+ }
+ }
+}
+
+// Include see HttpServer.Include
+func Include(cList ...ControllerInterface) *HttpServer {
+ return BeeApp.Include(cList...)
+}
+
+// Include will generate router file in the router/xxx.go from the controller's comments
+// usage:
+// beego.Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{})
+// type BankAccount struct{
+// beego.Controller
+// }
+//
+// register the function
+// func (b *BankAccount)Mapping(){
+// b.Mapping("ShowAccount" , b.ShowAccount)
+// b.Mapping("ModifyAccount", b.ModifyAccount)
+// }
+//
+// //@router /account/:id [get]
+// func (b *BankAccount) ShowAccount(){
+// //logic
+// }
+//
+//
+// //@router /account/:id [post]
+// func (b *BankAccount) ModifyAccount(){
+// //logic
+// }
+//
+// the comments @router url methodlist
+// url support all the function Router's pattern
+// methodlist [get post head put delete options *]
+func (app *HttpServer) Include(cList ...ControllerInterface) *HttpServer {
+ app.Handlers.Include(cList...)
+ return app
+}
+
+// RESTRouter see HttpServer.RESTRouter
+func RESTRouter(rootpath string, c ControllerInterface) *HttpServer {
+ return BeeApp.RESTRouter(rootpath, c)
+}
+
+// RESTRouter adds a restful controller handler to BeeApp.
+// its' controller implements beego.ControllerInterface and
+// defines a param "pattern/:objectId" to visit each resource.
+func (app *HttpServer) RESTRouter(rootpath string, c ControllerInterface) *HttpServer {
+ app.Router(rootpath, c)
+ app.Router(path.Join(rootpath, ":objectId"), c)
+ return app
+}
+
+// AutoRouter see HttpServer.AutoRouter
+func AutoRouter(c ControllerInterface) *HttpServer {
+ return BeeApp.AutoRouter(c)
+}
+
+// AutoRouter adds defined controller handler to BeeApp.
+// it's same to HttpServer.AutoRouter.
+// if beego.AddAuto(&MainContorlller{}) and MainController has methods List and Page,
+// visit the url /main/list to exec List function or /main/page to exec Page function.
+func (app *HttpServer) AutoRouter(c ControllerInterface) *HttpServer {
+ app.Handlers.AddAuto(c)
+ return app
+}
+
+// AutoPrefix see HttpServer.AutoPrefix
+func AutoPrefix(prefix string, c ControllerInterface) *HttpServer {
+ return BeeApp.AutoPrefix(prefix, c)
+}
+
+// AutoPrefix adds controller handler to BeeApp with prefix.
+// it's same to HttpServer.AutoRouterWithPrefix.
+// if beego.AutoPrefix("/admin",&MainContorlller{}) and MainController has methods List and Page,
+// visit the url /admin/main/list to exec List function or /admin/main/page to exec Page function.
+func (app *HttpServer) AutoPrefix(prefix string, c ControllerInterface) *HttpServer {
+ app.Handlers.AddAutoPrefix(prefix, c)
+ return app
+}
+
+// Get see HttpServer.Get
+func Get(rootpath string, f FilterFunc) *HttpServer {
+ return BeeApp.Get(rootpath, f)
+}
+
+// Get used to register router for Get method
+// usage:
+// beego.Get("/", func(ctx *context.Context){
+// ctx.Output.Body("hello world")
+// })
+func (app *HttpServer) Get(rootpath string, f FilterFunc) *HttpServer {
+ app.Handlers.Get(rootpath, f)
+ return app
+}
+
+// Post see HttpServer.Post
+func Post(rootpath string, f FilterFunc) *HttpServer {
+ return BeeApp.Post(rootpath, f)
+}
+
+// Post used to register router for Post method
+// usage:
+// beego.Post("/api", func(ctx *context.Context){
+// ctx.Output.Body("hello world")
+// })
+func (app *HttpServer) Post(rootpath string, f FilterFunc) *HttpServer {
+ app.Handlers.Post(rootpath, f)
+ return app
+}
+
+// Delete see HttpServer.Delete
+func Delete(rootpath string, f FilterFunc) *HttpServer {
+ return BeeApp.Delete(rootpath, f)
+}
+
+// Delete used to register router for Delete method
+// usage:
+// beego.Delete("/api", func(ctx *context.Context){
+// ctx.Output.Body("hello world")
+// })
+func (app *HttpServer) Delete(rootpath string, f FilterFunc) *HttpServer {
+ app.Handlers.Delete(rootpath, f)
+ return app
+}
+
+// Put see HttpServer.Put
+func Put(rootpath string, f FilterFunc) *HttpServer {
+ return BeeApp.Put(rootpath, f)
+}
+
+// Put used to register router for Put method
+// usage:
+// beego.Put("/api", func(ctx *context.Context){
+// ctx.Output.Body("hello world")
+// })
+func (app *HttpServer) Put(rootpath string, f FilterFunc) *HttpServer {
+ app.Handlers.Put(rootpath, f)
+ return app
+}
+
+// Head see HttpServer.Head
+func Head(rootpath string, f FilterFunc) *HttpServer {
+ return BeeApp.Head(rootpath, f)
+}
+
+// Head used to register router for Head method
+// usage:
+// beego.Head("/api", func(ctx *context.Context){
+// ctx.Output.Body("hello world")
+// })
+func (app *HttpServer) Head(rootpath string, f FilterFunc) *HttpServer {
+ app.Handlers.Head(rootpath, f)
+ return app
+}
+
+// Options see HttpServer.Options
+func Options(rootpath string, f FilterFunc) *HttpServer {
+ BeeApp.Handlers.Options(rootpath, f)
+ return BeeApp
+}
+
+// Options used to register router for Options method
+// usage:
+// beego.Options("/api", func(ctx *context.Context){
+// ctx.Output.Body("hello world")
+// })
+func (app *HttpServer) Options(rootpath string, f FilterFunc) *HttpServer {
+ app.Handlers.Options(rootpath, f)
+ return app
+}
+
+// Patch see HttpServer.Patch
+func Patch(rootpath string, f FilterFunc) *HttpServer {
+ return BeeApp.Patch(rootpath, f)
+}
+
+// Patch used to register router for Patch method
+// usage:
+// beego.Patch("/api", func(ctx *context.Context){
+// ctx.Output.Body("hello world")
+// })
+func (app *HttpServer) Patch(rootpath string, f FilterFunc) *HttpServer {
+ app.Handlers.Patch(rootpath, f)
+ return app
+}
+
+// Any see HttpServer.Any
+func Any(rootpath string, f FilterFunc) *HttpServer {
+ return BeeApp.Any(rootpath, f)
+}
+
+// Any used to register router for all methods
+// usage:
+// beego.Any("/api", func(ctx *context.Context){
+// ctx.Output.Body("hello world")
+// })
+func (app *HttpServer) Any(rootpath string, f FilterFunc) *HttpServer {
+ app.Handlers.Any(rootpath, f)
+ return app
+}
+
+// Handler see HttpServer.Handler
+func Handler(rootpath string, h http.Handler, options ...interface{}) *HttpServer {
+ return BeeApp.Handler(rootpath, h, options...)
+}
+
+// Handler used to register a Handler router
+// usage:
+// beego.Handler("/api", http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
+// fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
+// }))
+func (app *HttpServer) Handler(rootpath string, h http.Handler, options ...interface{}) *HttpServer {
+ app.Handlers.Handler(rootpath, h, options...)
+ return app
+}
+
+// InserFilter see HttpServer.InsertFilter
+func InsertFilter(pattern string, pos int, filter FilterFunc, opts ...FilterOpt) *HttpServer {
+ return BeeApp.InsertFilter(pattern, pos, filter, opts...)
+}
+
+// InsertFilter adds a FilterFunc with pattern condition and action constant.
+// The pos means action constant including
+// beego.BeforeStatic, beego.BeforeRouter, beego.BeforeExec, beego.AfterExec and beego.FinishRouter.
+// The bool params is for setting the returnOnOutput value (false allows multiple filters to execute)
+func (app *HttpServer) InsertFilter(pattern string, pos int, filter FilterFunc, opts ...FilterOpt) *HttpServer {
+ app.Handlers.InsertFilter(pattern, pos, filter, opts...)
+ return app
+}
+
+// InsertFilterChain see HttpServer.InsertFilterChain
+func InsertFilterChain(pattern string, filterChain FilterChain, opts ...FilterOpt) *HttpServer {
+ return BeeApp.InsertFilterChain(pattern, filterChain, opts...)
+}
+
+// InsertFilterChain adds a FilterFunc built by filterChain.
+// This filter will be executed before all filters.
+// the filter's behavior like stack's behavior
+// and the last filter is serving the http request
+func (app *HttpServer) InsertFilterChain(pattern string, filterChain FilterChain, opts ...FilterOpt) *HttpServer {
+ app.Handlers.InsertFilterChain(pattern, filterChain, opts...)
+ return app
+}
+
+func (app *HttpServer) initAddr(addr string) {
+ strs := strings.Split(addr, ":")
+ if len(strs) > 0 && strs[0] != "" {
+ app.Cfg.Listen.HTTPAddr = strs[0]
+ app.Cfg.Listen.Domains = []string{strs[0]}
+ }
+ if len(strs) > 1 && strs[1] != "" {
+ app.Cfg.Listen.HTTPPort, _ = strconv.Atoi(strs[1])
+ }
+}
+
+func (app *HttpServer) LogAccess(ctx *beecontext.Context, startTime *time.Time, statusCode int) {
+ // Skip logging if AccessLogs config is false
+ if !app.Cfg.Log.AccessLogs {
+ return
+ }
+ // Skip logging static requests unless EnableStaticLogs config is true
+ if !app.Cfg.Log.EnableStaticLogs && DefaultAccessLogFilter.Filter(ctx) {
+ return
+ }
+ var (
+ requestTime time.Time
+ elapsedTime time.Duration
+ r = ctx.Request
+ )
+ if startTime != nil {
+ requestTime = *startTime
+ elapsedTime = time.Since(*startTime)
+ }
+ record := &logs.AccessLogRecord{
+ RemoteAddr: ctx.Input.IP(),
+ RequestTime: requestTime,
+ RequestMethod: r.Method,
+ Request: fmt.Sprintf("%s %s %s", r.Method, r.RequestURI, r.Proto),
+ ServerProtocol: r.Proto,
+ Host: r.Host,
+ Status: statusCode,
+ ElapsedTime: elapsedTime,
+ HTTPReferrer: r.Header.Get("Referer"),
+ HTTPUserAgent: r.Header.Get("User-Agent"),
+ RemoteUser: r.Header.Get("Remote-User"),
+ BodyBytesSent: r.ContentLength,
+ }
+ logs.AccessLog(record, app.Cfg.Log.AccessLogsFormat)
+}
+
+// PrintTree prints all registered routers.
+func (app *HttpServer) PrintTree() M {
+ var (
+ content = M{}
+ methods = []string{}
+ methodsData = make(M)
+ )
+ for method, t := range app.Handlers.routers {
+
+ resultList := new([][]string)
+
+ printTree(resultList, t)
+
+ methods = append(methods, template.HTMLEscapeString(method))
+ methodsData[template.HTMLEscapeString(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{
+ template.HTMLEscapeString(v.pattern),
+ template.HTMLEscapeString(fmt.Sprintf("%s", v.methods)),
+ template.HTMLEscapeString(v.controllerType.String()),
+ }
+ *resultList = append(*resultList, result)
+ } else if v.routerType == routerTypeRESTFul {
+ var result = []string{
+ template.HTMLEscapeString(v.pattern),
+ template.HTMLEscapeString(fmt.Sprintf("%s", v.methods)),
+ "",
+ }
+ *resultList = append(*resultList, result)
+ } else if v.routerType == routerTypeHandler {
+ var result = []string{
+ template.HTMLEscapeString(v.pattern),
+ "",
+ "",
+ }
+ *resultList = append(*resultList, result)
+ }
+ }
+ }
+}
+
+func (app *HttpServer) reportFilter() M {
+ filterTypeData := make(M)
+ // filterTypes := []string{}
+ if app.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 := app.Handlers.filters[k]; len(bf) > 0 {
+ resultList := new([][]string)
+ for _, f := range bf {
+ var result = []string{
+ // void xss
+ template.HTMLEscapeString(f.pattern),
+ template.HTMLEscapeString(utils.GetFuncName(f.filterFunc)),
+ }
+ *resultList = append(*resultList, result)
+ }
+ filterTypeData[fr] = resultList
+ }
+ }
+ }
+
+ return filterTypeData
+}
diff --git a/pkg/server/web/server_test.go b/pkg/server/web/server_test.go
new file mode 100644
index 00000000..45ab2d4f
--- /dev/null
+++ b/pkg/server/web/server_test.go
@@ -0,0 +1,31 @@
+// Copyright 2020
+//
+// 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 web
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestNewHttpServerWithCfg(t *testing.T) {
+ // we should make sure that update server's config won't change
+ BConfig.AppName = "Before"
+ svr := NewHttpServerWithCfg(*BConfig)
+ svr.Cfg.AppName = "hello"
+ assert.NotEqual(t, "hello", BConfig.AppName)
+ assert.Equal(t, "Before", BConfig.AppName)
+
+}
diff --git a/pkg/server/web/template.go b/pkg/server/web/template.go
index a4b8db99..1192a3f2 100644
--- a/pkg/server/web/template.go
+++ b/pkg/server/web/template.go
@@ -368,14 +368,14 @@ func SetTemplateFSFunc(fnt templateFSFunc) {
}
// SetViewsPath sets view directory path in beego application.
-func SetViewsPath(path string) *App {
+func SetViewsPath(path string) *HttpServer {
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 {
+func SetStaticPath(url string, path string) *HttpServer {
if !strings.HasPrefix(url, "/") {
url = "/" + url
}
@@ -387,7 +387,7 @@ func SetStaticPath(url string, path string) *App {
}
// DelStaticPath removes the static folder setting in this url pattern in beego application.
-func DelStaticPath(url string) *App {
+func DelStaticPath(url string) *HttpServer {
if !strings.HasPrefix(url, "/") {
url = "/" + url
}
@@ -399,7 +399,7 @@ func DelStaticPath(url string) *App {
}
// AddTemplateEngine add a new templatePreProcessor which support extension
-func AddTemplateEngine(extension string, fn templatePreProcessor) *App {
+func AddTemplateEngine(extension string, fn templatePreProcessor) *HttpServer {
AddTemplateExt(extension)
beeTemplateEngines[extension] = fn
return BeeApp