bd61dd9f by astaxie

change a log about new version

1 parent 5b9ae544
1 package beego
2
3 import (
4 "fmt"
5 "github.com/astaxie/beego/context"
6 "net"
7 "net/http"
8 "net/http/fcgi"
9 "time"
10 )
11
12 type FilterFunc func(*context.Context)
13
14 type App struct {
15 Handlers *ControllerRegistor
16 }
17
18 // New returns a new PatternServeMux.
19 func NewApp() *App {
20 cr := NewControllerRegistor()
21 app := &App{Handlers: cr}
22 return app
23 }
24
25 func (app *App) Run() {
26 addr := HttpAddr
27
28 if HttpPort != 0 {
29 addr = fmt.Sprintf("%s:%d", HttpAddr, HttpPort)
30 }
31 var (
32 err error
33 l net.Listener
34 )
35 if UseFcgi {
36 if HttpPort == 0 {
37 l, err = net.Listen("unix", addr)
38 } else {
39 l, err = net.Listen("tcp", addr)
40 }
41 if err != nil {
42 BeeLogger.Fatal("Listen: ", err)
43 }
44 err = fcgi.Serve(l, app.Handlers)
45 } else {
46 if EnableHotUpdate {
47 server := &http.Server{
48 Handler: app.Handlers,
49 ReadTimeout: time.Duration(HttpServerTimeOut) * time.Second,
50 WriteTimeout: time.Duration(HttpServerTimeOut) * time.Second,
51 }
52 laddr, err := net.ResolveTCPAddr("tcp", addr)
53 if nil != err {
54 BeeLogger.Fatal("ResolveTCPAddr:", err)
55 }
56 l, err = GetInitListner(laddr)
57 theStoppable = newStoppable(l)
58 err = server.Serve(theStoppable)
59 theStoppable.wg.Wait()
60 CloseSelf()
61 } else {
62 s := &http.Server{
63 Addr: addr,
64 Handler: app.Handlers,
65 ReadTimeout: time.Duration(HttpServerTimeOut) * time.Second,
66 WriteTimeout: time.Duration(HttpServerTimeOut) * time.Second,
67 }
68 if HttpTLS {
69 err = s.ListenAndServeTLS(HttpCertFile, HttpKeyFile)
70 } else {
71 err = s.ListenAndServe()
72 }
73 }
74 }
75 if err != nil {
76 BeeLogger.Fatal("ListenAndServe: ", err)
77 }
78 }
79
80 func (app *App) Router(path string, c ControllerInterface, mappingMethods ...string) *App {
81 app.Handlers.Add(path, c, mappingMethods...)
82 return app
83 }
84
85 func (app *App) AutoRouter(c ControllerInterface) *App {
86 app.Handlers.AddAuto(c)
87 return app
88 }
89
90 func (app *App) Filter(pattern, action string, filter FilterFunc) *App {
91 app.Handlers.AddFilter(pattern, action, filter)
92 return app
93 }
94
95 func (app *App) SetViewsPath(path string) *App {
96 ViewsPath = path
97 return app
98 }
99
100 func (app *App) SetStaticPath(url string, path string) *App {
101 StaticDir[url] = path
102 return app
103 }
104
105 func (app *App) DelStaticPath(url string) *App {
106 delete(StaticDir, url)
107 return app
108 }
1 package beego 1 package beego
2 2
3 import ( 3 import (
4 "fmt"
5 "github.com/astaxie/beego/session" 4 "github.com/astaxie/beego/session"
6 "html/template"
7 "net"
8 "net/http" 5 "net/http"
9 "net/http/fcgi"
10 "os"
11 "path" 6 "path"
12 "runtime"
13 "time"
14 ) 7 )
15 8
16 const VERSION = "0.9.0" 9 const VERSION = "0.9.0"
17 10
18 var (
19 BeeApp *App
20 AppName string
21 AppPath string
22 AppConfigPath string
23 StaticDir map[string]string
24 TemplateCache map[string]*template.Template
25 HttpAddr string
26 HttpPort int
27 RecoverPanic bool
28 AutoRender bool
29 PprofOn bool
30 ViewsPath string
31 RunMode string //"dev" or "prod"
32 AppConfig *Config
33 //related to session
34 GlobalSessions *session.Manager //GlobalSessions
35 SessionOn bool // whether auto start session,default is false
36 SessionProvider string // default session provider memory mysql redis
37 SessionName string // sessionName cookie's name
38 SessionGCMaxLifetime int64 // session's gc maxlifetime
39 SessionSavePath string // session savepath if use mysql/redis/file this set to the connectinfo
40 UseFcgi bool
41 MaxMemory int64
42 EnableGzip bool // enable gzip
43 DirectoryIndex bool //enable DirectoryIndex default is false
44 EnableHotUpdate bool //enable HotUpdate default is false
45 HttpServerTimeOut int64 //set httpserver timeout
46 ErrorsShow bool //set weather show errors
47 XSRFKEY string //set XSRF
48 EnableXSRF bool
49 XSRFExpire int
50 CopyRequestBody bool //When in raw application, You want to the reqeustbody
51 TemplateLeft string
52 TemplateRight string
53 )
54
55 func init() {
56 os.Chdir(path.Dir(os.Args[0]))
57 BeeApp = NewApp()
58 AppPath, _ = os.Getwd()
59 StaticDir = make(map[string]string)
60 TemplateCache = make(map[string]*template.Template)
61 HttpAddr = ""
62 HttpPort = 8080
63 AppName = "beego"
64 RunMode = "dev" //default runmod
65 AutoRender = true
66 RecoverPanic = true
67 PprofOn = false
68 ViewsPath = "views"
69 SessionOn = false
70 SessionProvider = "memory"
71 SessionName = "beegosessionID"
72 SessionGCMaxLifetime = 3600
73 SessionSavePath = ""
74 UseFcgi = false
75 MaxMemory = 1 << 26 //64MB
76 EnableGzip = false
77 StaticDir["/static"] = "static"
78 AppConfigPath = path.Join(AppPath, "conf", "app.conf")
79 HttpServerTimeOut = 0
80 ErrorsShow = true
81 XSRFKEY = "beegoxsrf"
82 XSRFExpire = 60
83 TemplateLeft = "{{"
84 TemplateRight = "}}"
85 ParseConfig()
86 runtime.GOMAXPROCS(runtime.NumCPU())
87 }
88
89 type App struct {
90 Handlers *ControllerRegistor
91 }
92
93 // New returns a new PatternServeMux.
94 func NewApp() *App {
95 cr := NewControllerRegistor()
96 app := &App{Handlers: cr}
97 return app
98 }
99
100 func (app *App) Run() {
101 addr := HttpAddr
102
103 if HttpPort != 0 {
104 addr = fmt.Sprintf("%s:%d", HttpAddr, HttpPort)
105 }
106 var (
107 err error
108 l net.Listener
109 )
110 if UseFcgi {
111 if HttpPort == 0 {
112 l, err = net.Listen("unix", addr)
113 } else {
114 l, err = net.Listen("tcp", addr)
115 }
116 if err != nil {
117 BeeLogger.Fatal("Listen: ", err)
118 }
119 err = fcgi.Serve(l, app.Handlers)
120 } else {
121 if EnableHotUpdate {
122 server := &http.Server{
123 Handler: app.Handlers,
124 ReadTimeout: time.Duration(HttpServerTimeOut) * time.Second,
125 WriteTimeout: time.Duration(HttpServerTimeOut) * time.Second,
126 }
127 laddr, err := net.ResolveTCPAddr("tcp", addr)
128 if nil != err {
129 BeeLogger.Fatal("ResolveTCPAddr:", err)
130 }
131 l, err = GetInitListner(laddr)
132 theStoppable = newStoppable(l)
133 err = server.Serve(theStoppable)
134 theStoppable.wg.Wait()
135 CloseSelf()
136 } else {
137 s := &http.Server{
138 Addr: addr,
139 Handler: app.Handlers,
140 ReadTimeout: time.Duration(HttpServerTimeOut) * time.Second,
141 WriteTimeout: time.Duration(HttpServerTimeOut) * time.Second,
142 }
143 err = s.ListenAndServe()
144 }
145 }
146 if err != nil {
147 BeeLogger.Fatal("ListenAndServe: ", err)
148 }
149 }
150
151 func (app *App) Router(path string, c ControllerInterface, mappingMethods ...string) *App {
152 app.Handlers.Add(path, c, mappingMethods...)
153 return app
154 }
155
156 func (app *App) AutoRouter(c ControllerInterface) *App {
157 app.Handlers.AddAuto(c)
158 return app
159 }
160
161 func (app *App) Filter(filter http.HandlerFunc) *App {
162 app.Handlers.Filter(filter)
163 return app
164 }
165
166 func (app *App) FilterParam(param string, filter http.HandlerFunc) *App {
167 app.Handlers.FilterParam(param, filter)
168 return app
169 }
170
171 func (app *App) FilterPrefixPath(path string, filter http.HandlerFunc) *App {
172 app.Handlers.FilterPrefixPath(path, filter)
173 return app
174 }
175
176 func (app *App) FilterAfter(filter http.HandlerFunc) *App {
177 app.Handlers.FilterAfter(filter)
178 return app
179 }
180
181 func (app *App) FilterParamAfter(param string, filter http.HandlerFunc) *App {
182 app.Handlers.FilterParamAfter(param, filter)
183 return app
184 }
185
186 func (app *App) FilterPrefixPathAfter(path string, filter http.HandlerFunc) *App {
187 app.Handlers.FilterPrefixPathAfter(path, filter)
188 return app
189 }
190
191 func (app *App) SetViewsPath(path string) *App {
192 ViewsPath = path
193 return app
194 }
195
196 func (app *App) SetStaticPath(url string, path string) *App {
197 StaticDir[url] = path
198 return app
199 }
200
201 func (app *App) DelStaticPath(url string) *App {
202 delete(StaticDir, url)
203 return app
204 }
205
206 func (app *App) ErrorLog(ctx *Context) {
207 BeeLogger.Printf("[ERR] host: '%s', request: '%s %s', proto: '%s', ua: '%s', remote: '%s'\n", ctx.Request.Host, ctx.Request.Method, ctx.Request.URL.Path, ctx.Request.Proto, ctx.Request.UserAgent(), ctx.Request.RemoteAddr)
208 }
209
210 func (app *App) AccessLog(ctx *Context) {
211 BeeLogger.Printf("[ACC] host: '%s', request: '%s %s', proto: '%s', ua: '%s', remote: '%s'\n", ctx.Request.Host, ctx.Request.Method, ctx.Request.URL.Path, ctx.Request.Proto, ctx.Request.UserAgent(), ctx.Request.RemoteAddr)
212 }
213
214 func RegisterController(path string, c ControllerInterface) *App {
215 BeeApp.Router(path, c)
216 return BeeApp
217 }
218
219 func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *App { 11 func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *App {
220 BeeApp.Router(rootpath, c, mappingMethods...) 12 BeeApp.Router(rootpath, c, mappingMethods...)
221 return BeeApp 13 return BeeApp
...@@ -232,11 +24,6 @@ func AutoRouter(c ControllerInterface) *App { ...@@ -232,11 +24,6 @@ func AutoRouter(c ControllerInterface) *App {
232 return BeeApp 24 return BeeApp
233 } 25 }
234 26
235 func RouterHandler(path string, c http.Handler) *App {
236 BeeApp.Handlers.AddHandler(path, c)
237 return BeeApp
238 }
239
240 func Errorhandler(err string, h http.HandlerFunc) *App { 27 func Errorhandler(err string, h http.HandlerFunc) *App {
241 ErrorMaps[err] = h 28 ErrorMaps[err] = h
242 return BeeApp 29 return BeeApp
...@@ -257,37 +44,18 @@ func DelStaticPath(url string) *App { ...@@ -257,37 +44,18 @@ func DelStaticPath(url string) *App {
257 return BeeApp 44 return BeeApp
258 } 45 }
259 46
260 func Filter(filter http.HandlerFunc) *App { 47 //action has four values:
261 BeeApp.Filter(filter) 48 //BeforRouter
262 return BeeApp 49 //AfterStatic
263 } 50 //BeforExec
264 51 //AfterExec
265 func FilterParam(param string, filter http.HandlerFunc) *App { 52 func AddFilter(pattern, action string, filter FilterFunc) *App {
266 BeeApp.FilterParam(param, filter) 53 BeeApp.Filter(pattern, action, filter)
267 return BeeApp
268 }
269
270 func FilterPrefixPath(path string, filter http.HandlerFunc) *App {
271 BeeApp.FilterPrefixPath(path, filter)
272 return BeeApp
273 }
274
275 func FilterAfter(filter http.HandlerFunc) *App {
276 BeeApp.FilterAfter(filter)
277 return BeeApp
278 }
279
280 func FilterParamAfter(param string, filter http.HandlerFunc) *App {
281 BeeApp.FilterParamAfter(param, filter)
282 return BeeApp
283 }
284
285 func FilterPrefixPathAfter(path string, filter http.HandlerFunc) *App {
286 BeeApp.FilterPrefixPathAfter(path, filter)
287 return BeeApp 54 return BeeApp
288 } 55 }
289 56
290 func Run() { 57 func Run() {
58 //if AppConfigPath not In the conf/app.conf reParse config
291 if AppConfigPath != path.Join(AppPath, "conf", "app.conf") { 59 if AppConfigPath != path.Join(AppPath, "conf", "app.conf") {
292 err := ParseConfig() 60 err := ParseConfig()
293 if err != nil { 61 if err != nil {
...@@ -296,18 +64,18 @@ func Run() { ...@@ -296,18 +64,18 @@ func Run() {
296 } 64 }
297 } 65 }
298 } 66 }
299 if PprofOn { 67
300 BeeApp.Router(`/debug/pprof`, &ProfController{})
301 BeeApp.Router(`/debug/pprof/:pp([\w]+)`, &ProfController{})
302 }
303 if SessionOn { 68 if SessionOn {
304 GlobalSessions, _ = session.NewManager(SessionProvider, SessionName, SessionGCMaxLifetime, SessionSavePath) 69 GlobalSessions, _ = session.NewManager(SessionProvider, SessionName, SessionGCMaxLifetime, SessionSavePath)
305 go GlobalSessions.GC() 70 go GlobalSessions.GC()
306 } 71 }
307 err := BuildTemplate(ViewsPath) 72
308 if err != nil { 73 if AutoRender {
309 if RunMode == "dev" { 74 err := BuildTemplate(ViewsPath)
310 Warn(err) 75 if err != nil {
76 if RunMode == "dev" {
77 Warn(err)
78 }
311 } 79 }
312 } 80 }
313 registerErrorHander() 81 registerErrorHander()
......
1 package beego 1 package beego
2 2
3 import ( 3 import (
4 "bufio" 4 "github.com/astaxie/beego/config"
5 "bytes" 5 "github.com/astaxie/beego/session"
6 "errors" 6 "html/template"
7 "io"
8 "os" 7 "os"
8 "path"
9 "runtime"
9 "strconv" 10 "strconv"
10 "strings"
11 "sync"
12 "unicode"
13 ) 11 )
14 12
15 var ( 13 var (
16 bComment = []byte{'#'} 14 BeeApp *App
17 bEmpty = []byte{} 15 AppName string
18 bEqual = []byte{'='} 16 AppPath string
19 bDQuote = []byte{'"'} 17 AppConfigPath string
18 StaticDir map[string]string
19 TemplateCache map[string]*template.Template
20 HttpAddr string
21 HttpPort int
22 HttpTLS bool
23 HttpCertFile string
24 HttpKeyFile string
25 RecoverPanic bool
26 AutoRender bool
27 PprofOn bool
28 ViewsPath string
29 RunMode string //"dev" or "prod"
30 AppConfig config.ConfigContainer
31 //related to session
32 GlobalSessions *session.Manager //GlobalSessions
33 SessionOn bool // whether auto start session,default is false
34 SessionProvider string // default session provider memory mysql redis
35 SessionName string // sessionName cookie's name
36 SessionGCMaxLifetime int64 // session's gc maxlifetime
37 SessionSavePath string // session savepath if use mysql/redis/file this set to the connectinfo
38 UseFcgi bool
39 MaxMemory int64
40 EnableGzip bool // enable gzip
41 DirectoryIndex bool //enable DirectoryIndex default is false
42 EnableHotUpdate bool //enable HotUpdate default is false
43 HttpServerTimeOut int64 //set httpserver timeout
44 ErrorsShow bool //set weather show errors
45 XSRFKEY string //set XSRF
46 EnableXSRF bool
47 XSRFExpire int
48 CopyRequestBody bool //When in raw application, You want to the reqeustbody
49 TemplateLeft string
50 TemplateRight string
20 ) 51 )
21 52
22 // A Config represents the configuration. 53 func init() {
23 type Config struct { 54 os.Chdir(path.Dir(os.Args[0]))
24 filename string 55 BeeApp = NewApp()
25 comment map[int][]string // id: []{comment, key...}; id 1 is for main comment. 56 AppPath, _ = os.Getwd()
26 data map[string]string // key: value 57 StaticDir = make(map[string]string)
27 offset map[string]int64 // key: offset; for editing. 58 TemplateCache = make(map[string]*template.Template)
28 sync.RWMutex 59 HttpAddr = ""
29 } 60 HttpPort = 8080
30 61 AppName = "beego"
31 // ParseFile creates a new Config and parses the file configuration from the 62 RunMode = "dev" //default runmod
32 // named file. 63 AutoRender = true
33 func LoadConfig(name string) (*Config, error) { 64 RecoverPanic = true
34 file, err := os.Open(name) 65 PprofOn = false
35 if err != nil { 66 ViewsPath = "views"
36 return nil, err 67 SessionOn = false
37 } 68 SessionProvider = "memory"
38 69 SessionName = "beegosessionID"
39 cfg := &Config{ 70 SessionGCMaxLifetime = 3600
40 file.Name(), 71 SessionSavePath = ""
41 make(map[int][]string), 72 UseFcgi = false
42 make(map[string]string), 73 MaxMemory = 1 << 26 //64MB
43 make(map[string]int64), 74 EnableGzip = false
44 sync.RWMutex{}, 75 StaticDir["/static"] = "static"
45 } 76 AppConfigPath = path.Join(AppPath, "conf", "app.conf")
46 cfg.Lock() 77 HttpServerTimeOut = 0
47 defer cfg.Unlock() 78 ErrorsShow = true
48 defer file.Close() 79 XSRFKEY = "beegoxsrf"
49 80 XSRFExpire = 60
50 var comment bytes.Buffer 81 TemplateLeft = "{{"
51 buf := bufio.NewReader(file) 82 TemplateRight = "}}"
52 83 ParseConfig()
53 for nComment, off := 0, int64(1); ; { 84 runtime.GOMAXPROCS(runtime.NumCPU())
54 line, _, err := buf.ReadLine()
55 if err == io.EOF {
56 break
57 }
58 if bytes.Equal(line, bEmpty) {
59 continue
60 }
61
62 off += int64(len(line))
63
64 if bytes.HasPrefix(line, bComment) {
65 line = bytes.TrimLeft(line, "#")
66 line = bytes.TrimLeftFunc(line, unicode.IsSpace)
67 comment.Write(line)
68 comment.WriteByte('\n')
69 continue
70 }
71 if comment.Len() != 0 {
72 cfg.comment[nComment] = []string{comment.String()}
73 comment.Reset()
74 nComment++
75 }
76
77 val := bytes.SplitN(line, bEqual, 2)
78 if bytes.HasPrefix([]byte(strings.TrimSpace(string(val[1]))), bDQuote) {
79 val[1] = bytes.Trim([]byte(strings.TrimSpace(string(val[1]))), `"`)
80 }
81
82 key := strings.TrimSpace(string(val[0]))
83 cfg.comment[nComment-1] = append(cfg.comment[nComment-1], key)
84 cfg.data[key] = strings.TrimSpace(string(val[1]))
85 cfg.offset[key] = off
86 }
87 return cfg, nil
88 }
89
90 // Bool returns the boolean value for a given key.
91 func (c *Config) Bool(key string) (bool, error) {
92 return strconv.ParseBool(c.data[key])
93 }
94
95 // Int returns the integer value for a given key.
96 func (c *Config) Int(key string) (int, error) {
97 return strconv.Atoi(c.data[key])
98 }
99
100 func (c *Config) Int64(key string) (int64, error) {
101 return strconv.ParseInt(c.data[key], 10, 64)
102 }
103
104 // Float returns the float value for a given key.
105 func (c *Config) Float(key string) (float64, error) {
106 return strconv.ParseFloat(c.data[key], 64)
107 }
108
109 // String returns the string value for a given key.
110 func (c *Config) String(key string) string {
111 return c.data[key]
112 }
113
114 // WriteValue writes a new value for key.
115 func (c *Config) SetValue(key, value string) error {
116 c.Lock()
117 defer c.Unlock()
118
119 if _, found := c.data[key]; !found {
120 return errors.New("key not found: " + key)
121 }
122
123 c.data[key] = value
124 return nil
125 } 85 }
126 86
127 func ParseConfig() (err error) { 87 func ParseConfig() (err error) {
128 AppConfig, err = LoadConfig(AppConfigPath) 88 AppConfig, err := config.NewConfig("ini", AppConfigPath)
129 if err != nil { 89 if err != nil {
130 return err 90 return err
131 } else { 91 } else {
...@@ -204,6 +164,15 @@ func ParseConfig() (err error) { ...@@ -204,6 +164,15 @@ func ParseConfig() (err error) {
204 if tplright := AppConfig.String("templateright"); tplright != "" { 164 if tplright := AppConfig.String("templateright"); tplright != "" {
205 TemplateRight = tplright 165 TemplateRight = tplright
206 } 166 }
167 if httptls, err := AppConfig.Bool("HttpTLS"); err == nil {
168 HttpTLS = httptls
169 }
170 if certfile := AppConfig.String("HttpCertFile"); certfile != "" {
171 HttpCertFile = certfile
172 }
173 if keyfile := AppConfig.String("HttpKeyFile"); keyfile != "" {
174 HttpKeyFile = keyfile
175 }
207 } 176 }
208 return nil 177 return nil
209 } 178 }
......
...@@ -16,6 +16,11 @@ func (ctx *Context) Redirect(status int, localurl string) { ...@@ -16,6 +16,11 @@ func (ctx *Context) Redirect(status int, localurl string) {
16 ctx.Output.SetStatus(status) 16 ctx.Output.SetStatus(status)
17 } 17 }
18 18
19 func (ctx *Context) Abort(status int, body string) {
20 ctx.Output.SetStatus(status)
21 ctx.Output.Body([]byte(body))
22 }
23
19 func (ctx *Context) WriteString(content string) { 24 func (ctx *Context) WriteString(content string) {
20 ctx.Output.Body([]byte(content)) 25 ctx.Output.Body([]byte(content))
21 } 26 }
......
...@@ -17,7 +17,7 @@ import ( ...@@ -17,7 +17,7 @@ import (
17 ) 17 )
18 18
19 type BeegoOutput struct { 19 type BeegoOutput struct {
20 context *Context 20 Context *Context
21 Status int 21 Status int
22 EnableGzip bool 22 EnableGzip bool
23 res http.ResponseWriter 23 res http.ResponseWriter
...@@ -35,8 +35,8 @@ func (output *BeegoOutput) Header(key, val string) { ...@@ -35,8 +35,8 @@ func (output *BeegoOutput) Header(key, val string) {
35 35
36 func (output *BeegoOutput) Body(content []byte) { 36 func (output *BeegoOutput) Body(content []byte) {
37 output_writer := output.res.(io.Writer) 37 output_writer := output.res.(io.Writer)
38 if output.EnableGzip == true && output.context.Input.Header("Accept-Encoding") != "" { 38 if output.EnableGzip == true && output.Context.Input.Header("Accept-Encoding") != "" {
39 splitted := strings.SplitN(output.context.Input.Header("Accept-Encoding"), ",", -1) 39 splitted := strings.SplitN(output.Context.Input.Header("Accept-Encoding"), ",", -1)
40 encodings := make([]string, len(splitted)) 40 encodings := make([]string, len(splitted))
41 41
42 for i, val := range splitted { 42 for i, val := range splitted {
...@@ -120,23 +120,40 @@ func sanitizeValue(v string) string { ...@@ -120,23 +120,40 @@ func sanitizeValue(v string) string {
120 return cookieValueSanitizer.Replace(v) 120 return cookieValueSanitizer.Replace(v)
121 } 121 }
122 122
123 func (output *BeegoOutput) Json(data string) error { 123 func (output *BeegoOutput) Json(data interface{}, hasIndent bool, coding bool) error {
124 output.Header("Content-Type", "application/json;charset=UTF-8") 124 output.Header("Content-Type", "application/json;charset=UTF-8")
125 content, err := json.Marshal(data) 125 var content []byte
126 var err error
127 if hasIndent {
128 content, err = json.MarshalIndent(data, "", " ")
129 } else {
130 content, err = json.Marshal(data)
131 }
126 if err != nil { 132 if err != nil {
133 http.Error(output.res, err.Error(), http.StatusInternalServerError)
127 return err 134 return err
128 } 135 }
136 if coding {
137 content = []byte(stringsToJson(string(content)))
138 }
129 output.Body(content) 139 output.Body(content)
130 return nil 140 return nil
131 } 141 }
132 142
133 func (output *BeegoOutput) Jsonp(data string) error { 143 func (output *BeegoOutput) Jsonp(data interface{}, hasIndent bool) error {
134 output.Header("Content-Type", "application/javascript;charset=UTF-8") 144 output.Header("Content-Type", "application/javascript;charset=UTF-8")
135 content, err := json.Marshal(data) 145 var content []byte
146 var err error
147 if hasIndent {
148 content, err = json.MarshalIndent(data, "", " ")
149 } else {
150 content, err = json.Marshal(data)
151 }
136 if err != nil { 152 if err != nil {
153 http.Error(output.res, err.Error(), http.StatusInternalServerError)
137 return err 154 return err
138 } 155 }
139 callback := output.context.Input.Query("callback") 156 callback := output.Context.Input.Query("callback")
140 if callback == "" { 157 if callback == "" {
141 return errors.New(`"callback" parameter required`) 158 return errors.New(`"callback" parameter required`)
142 } 159 }
...@@ -148,10 +165,17 @@ func (output *BeegoOutput) Jsonp(data string) error { ...@@ -148,10 +165,17 @@ func (output *BeegoOutput) Jsonp(data string) error {
148 return nil 165 return nil
149 } 166 }
150 167
151 func (output *BeegoOutput) Xml(data string) error { 168 func (output *BeegoOutput) Xml(data string, hasIndent bool) error {
152 output.Header("Content-Type", "application/xml;charset=UTF-8") 169 output.Header("Content-Type", "application/xml;charset=UTF-8")
153 content, err := xml.Marshal(data) 170 var content []byte
171 var err error
172 if hasIndent {
173 content, err = xml.MarshalIndent(data, "", " ")
174 } else {
175 content, err = xml.Marshal(data)
176 }
154 if err != nil { 177 if err != nil {
178 http.Error(output.res, err.Error(), http.StatusInternalServerError)
155 return err 179 return err
156 } 180 }
157 output.Body(content) 181 output.Body(content)
...@@ -166,7 +190,7 @@ func (output *BeegoOutput) Download(file string) { ...@@ -166,7 +190,7 @@ func (output *BeegoOutput) Download(file string) {
166 output.Header("Expires", "0") 190 output.Header("Expires", "0")
167 output.Header("Cache-Control", "must-revalidate") 191 output.Header("Cache-Control", "must-revalidate")
168 output.Header("Pragma", "public") 192 output.Header("Pragma", "public")
169 http.ServeFile(output.res, output.context.Request, file) 193 http.ServeFile(output.res, output.Context.Request, file)
170 } 194 }
171 195
172 func (output *BeegoOutput) ContentType(ext string) { 196 func (output *BeegoOutput) ContentType(ext string) {
...@@ -219,3 +243,17 @@ func (output *BeegoOutput) IsClientError(status int) bool { ...@@ -219,3 +243,17 @@ func (output *BeegoOutput) IsClientError(status int) bool {
219 func (output *BeegoOutput) IsServerError(status int) bool { 243 func (output *BeegoOutput) IsServerError(status int) bool {
220 return output.Status >= 500 && output.Status < 600 244 return output.Status >= 500 && output.Status < 600
221 } 245 }
246
247 func stringsToJson(str string) string {
248 rs := []rune(str)
249 jsons := ""
250 for _, r := range rs {
251 rint := int(r)
252 if rint < 128 {
253 jsons += string(r)
254 } else {
255 jsons += "\\u" + strconv.FormatInt(int64(rint), 16) // json
256 }
257 }
258 return jsons
259 }
......
...@@ -2,15 +2,12 @@ package beego ...@@ -2,15 +2,12 @@ package beego
2 2
3 import ( 3 import (
4 "bytes" 4 "bytes"
5 "compress/flate"
6 "compress/gzip"
7 "crypto/hmac" 5 "crypto/hmac"
8 "crypto/sha1" 6 "crypto/sha1"
9 "encoding/base64" 7 "encoding/base64"
10 "encoding/json"
11 "encoding/xml"
12 "errors" 8 "errors"
13 "fmt" 9 "fmt"
10 "github.com/astaxie/beego/context"
14 "github.com/astaxie/beego/session" 11 "github.com/astaxie/beego/session"
15 "html/template" 12 "html/template"
16 "io" 13 "io"
...@@ -26,7 +23,7 @@ import ( ...@@ -26,7 +23,7 @@ import (
26 ) 23 )
27 24
28 type Controller struct { 25 type Controller struct {
29 Ctx *Context 26 Ctx *context.Context
30 Data map[interface{}]interface{} 27 Data map[interface{}]interface{}
31 ChildName string 28 ChildName string
32 TplNames string 29 TplNames string
...@@ -39,7 +36,7 @@ type Controller struct { ...@@ -39,7 +36,7 @@ type Controller struct {
39 } 36 }
40 37
41 type ControllerInterface interface { 38 type ControllerInterface interface {
42 Init(ct *Context, cn string) 39 Init(ct *Context, childName string)
43 Prepare() 40 Prepare()
44 Get() 41 Get()
45 Post() 42 Post()
...@@ -52,11 +49,11 @@ type ControllerInterface interface { ...@@ -52,11 +49,11 @@ type ControllerInterface interface {
52 Render() error 49 Render() error
53 } 50 }
54 51
55 func (c *Controller) Init(ctx *Context, cn string) { 52 func (c *Controller) Init(ctx *context.Context, childName string) {
56 c.Data = make(map[interface{}]interface{}) 53 c.Data = make(map[interface{}]interface{})
57 c.Layout = "" 54 c.Layout = ""
58 c.TplNames = "" 55 c.TplNames = ""
59 c.ChildName = cn 56 c.ChildName = childName
60 c.Ctx = ctx 57 c.Ctx = ctx
61 c.TplExt = "tpl" 58 c.TplExt = "tpl"
62 } 59 }
...@@ -109,8 +106,8 @@ func (c *Controller) Render() error { ...@@ -109,8 +106,8 @@ func (c *Controller) Render() error {
109 if err != nil { 106 if err != nil {
110 return err 107 return err
111 } else { 108 } else {
112 c.Ctx.ResponseWriter.Header().Set("Content-Type", "text/html; charset=utf-8") 109 c.Ctx.Output.Header("Content-Type", "text/html; charset=utf-8")
113 c.writeToWriter(rb) 110 c.Ctx.Output.Body(rb)
114 } 111 }
115 return nil 112 return nil
116 } 113 }
...@@ -172,41 +169,6 @@ func (c *Controller) RenderBytes() ([]byte, error) { ...@@ -172,41 +169,6 @@ func (c *Controller) RenderBytes() ([]byte, error) {
172 return []byte{}, nil 169 return []byte{}, nil
173 } 170 }
174 171
175 func (c *Controller) writeToWriter(rb []byte) {
176 output_writer := c.Ctx.ResponseWriter.(io.Writer)
177 if EnableGzip == true && c.Ctx.Request.Header.Get("Accept-Encoding") != "" {
178 splitted := strings.SplitN(c.Ctx.Request.Header.Get("Accept-Encoding"), ",", -1)
179 encodings := make([]string, len(splitted))
180
181 for i, val := range splitted {
182 encodings[i] = strings.TrimSpace(val)
183 }
184 for _, val := range encodings {
185 if val == "gzip" {
186 c.Ctx.ResponseWriter.Header().Set("Content-Encoding", "gzip")
187 output_writer, _ = gzip.NewWriterLevel(c.Ctx.ResponseWriter, gzip.BestSpeed)
188
189 break
190 } else if val == "deflate" {
191 c.Ctx.ResponseWriter.Header().Set("Content-Encoding", "deflate")
192 output_writer, _ = flate.NewWriter(c.Ctx.ResponseWriter, flate.BestSpeed)
193 break
194 }
195 }
196 } else {
197 c.Ctx.SetHeader("Content-Length", strconv.Itoa(len(rb)), true)
198 }
199 output_writer.Write(rb)
200 switch output_writer.(type) {
201 case *gzip.Writer:
202 output_writer.(*gzip.Writer).Close()
203 case *flate.Writer:
204 output_writer.(*flate.Writer).Close()
205 case io.WriteCloser:
206 output_writer.(io.WriteCloser).Close()
207 }
208 }
209
210 func (c *Controller) Redirect(url string, code int) { 172 func (c *Controller) Redirect(url string, code int) {
211 c.Ctx.Redirect(code, url) 173 c.Ctx.Redirect(code, url)
212 } 174 }
...@@ -216,63 +178,37 @@ func (c *Controller) Abort(code string) { ...@@ -216,63 +178,37 @@ func (c *Controller) Abort(code string) {
216 } 178 }
217 179
218 func (c *Controller) ServeJson(encoding ...bool) { 180 func (c *Controller) ServeJson(encoding ...bool) {
219 var content []byte 181 var hasIndent bool
220 var err error 182 var hasencoding bool
221 if RunMode == "prod" { 183 if RunMode == "prod" {
222 content, err = json.Marshal(c.Data["json"]) 184 hasIndent = false
223 } else { 185 } else {
224 content, err = json.MarshalIndent(c.Data["json"], "", " ") 186 hasIndent = true
225 } 187 }
226 if err != nil {
227 http.Error(c.Ctx.ResponseWriter, err.Error(), http.StatusInternalServerError)
228 return
229 }
230 c.Ctx.ResponseWriter.Header().Set("Content-Type", "application/json;charset=UTF-8")
231 if len(encoding) > 0 && encoding[0] == true { 188 if len(encoding) > 0 && encoding[0] == true {
232 content = []byte(stringsToJson(string(content))) 189 hasencoding = true
233 } 190 }
234 c.writeToWriter(content) 191 c.Ctx.Output.Json(c.Data["json"], hasIndent, hasencoding)
235 } 192 }
236 193
237 func (c *Controller) ServeJsonp() { 194 func (c *Controller) ServeJsonp() {
238 var content []byte 195 var hasIndent bool
239 var err error
240 if RunMode == "prod" { 196 if RunMode == "prod" {
241 content, err = json.Marshal(c.Data["jsonp"]) 197 hasIndent = false
242 } else { 198 } else {
243 content, err = json.MarshalIndent(c.Data["jsonp"], "", " ") 199 hasIndent = true
244 }
245 if err != nil {
246 http.Error(c.Ctx.ResponseWriter, err.Error(), http.StatusInternalServerError)
247 return
248 }
249 callback := c.Ctx.Request.Form.Get("callback")
250 if callback == "" {
251 http.Error(c.Ctx.ResponseWriter, `"callback" parameter required`, http.StatusInternalServerError)
252 return
253 } 200 }
254 callback_content := bytes.NewBufferString(callback) 201 c.Ctx.Output.Jsonp(c.Data["jsonp"], hasIndent)
255 callback_content.WriteString("(")
256 callback_content.Write(content)
257 callback_content.WriteString(");\r\n")
258 c.Ctx.ResponseWriter.Header().Set("Content-Type", "application/javascript;charset=UTF-8")
259 c.writeToWriter(callback_content.Bytes())
260 } 202 }
261 203
262 func (c *Controller) ServeXml() { 204 func (c *Controller) ServeXml() {
263 var content []byte 205 var hasIndent bool
264 var err error
265 if RunMode == "prod" { 206 if RunMode == "prod" {
266 content, err = xml.Marshal(c.Data["xml"]) 207 hasIndent = false
267 } else { 208 } else {
268 content, err = xml.MarshalIndent(c.Data["xml"], "", " ") 209 hasIndent = true
269 } 210 }
270 if err != nil { 211 c.Ctx.Output.Xml(c.Data["xml"], hasIndent)
271 http.Error(c.Ctx.ResponseWriter, err.Error(), http.StatusInternalServerError)
272 return
273 }
274 c.Ctx.ResponseWriter.Header().Set("Content-Type", "application/xml;charset=UTF-8")
275 c.writeToWriter(content)
276 } 212 }
277 213
278 func (c *Controller) Input() url.Values { 214 func (c *Controller) Input() url.Values {
...@@ -313,6 +249,10 @@ func (c *Controller) GetBool(key string) (bool, error) { ...@@ -313,6 +249,10 @@ func (c *Controller) GetBool(key string) (bool, error) {
313 return strconv.ParseBool(c.Input().Get(key)) 249 return strconv.ParseBool(c.Input().Get(key))
314 } 250 }
315 251
252 func (c *Controller) GetFloat(key string) (float64, error) {
253 return strconv.ParseFloat(c.Input().Get(key), 64)
254 }
255
316 func (c *Controller) GetFile(key string) (multipart.File, *multipart.FileHeader, error) { 256 func (c *Controller) GetFile(key string) (multipart.File, *multipart.FileHeader, error) {
317 return c.Ctx.Request.FormFile(key) 257 return c.Ctx.Request.FormFile(key)
318 } 258 }
...@@ -365,7 +305,7 @@ func (c *Controller) DestroySession() { ...@@ -365,7 +305,7 @@ func (c *Controller) DestroySession() {
365 } 305 }
366 306
367 func (c *Controller) IsAjax() bool { 307 func (c *Controller) IsAjax() bool {
368 return (c.Ctx.Request.Header.Get("HTTP_X_REQUESTED_WITH") == "XMLHttpRequest") 308 return c.Ctx.Input.IsAjax()
369 } 309 }
370 310
371 func (c *Controller) XsrfToken() string { 311 func (c *Controller) XsrfToken() string {
......
1 package beego
2
3 import (
4 "regexp"
5 )
6
7 type FilterRouter struct {
8 pattern string
9 regex *regexp.Regexp
10 filterFunc FilterFunc
11 hasregex bool
12 }
13
14 func (mr *FilterRouter) ValidRouter(router string) bool {
15 if mr.pattern == "" {
16 return true
17 }
18 if router == mr.pattern {
19 return true
20 }
21 if mr.hasregex {
22 if mr.regex.MatchString(router) {
23 return true
24 }
25 matches := mr.regex.FindStringSubmatch(router)
26 if len(matches[0]) == len(router) {
27 return true
28 }
29 }
30 return false
31 }
1 package middleware
2
3 import (
4 "fmt"
5 "html/template"
6 "net/http"
7 "runtime"
8 )
9
10 var tpl = `
11 <!DOCTYPE html>
12 <html>
13 <head>
14 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
15 <title>beego application error</title>
16 <style>
17 html, body, body * {padding: 0; margin: 0;}
18 #header {background:#ffd; border-bottom:solid 2px #A31515; padding: 20px 10px;}
19 #header h2{ }
20 #footer {border-top:solid 1px #aaa; padding: 5px 10px; font-size: 12px; color:green;}
21 #content {padding: 5px;}
22 #content .stack b{ font-size: 13px; color: red;}
23 #content .stack pre{padding-left: 10px;}
24 table {}
25 td.t {text-align: right; padding-right: 5px; color: #888;}
26 </style>
27 <script type="text/javascript">
28 </script>
29 </head>
30 <body>
31 <div id="header">
32 <h2>{{.AppError}}</h2>
33 </div>
34 <div id="content">
35 <table>
36 <tr>
37 <td class="t">Request Method: </td><td>{{.RequestMethod}}</td>
38 </tr>
39 <tr>
40 <td class="t">Request URL: </td><td>{{.RequestURL}}</td>
41 </tr>
42 <tr>
43 <td class="t">RemoteAddr: </td><td>{{.RemoteAddr }}</td>
44 </tr>
45 </table>
46 <div class="stack">
47 <b>Stack</b>
48 <pre>{{.Stack}}</pre>
49 </div>
50 </div>
51 <div id="footer">
52 <p>beego {{ .BeegoVersion }} (beego framework)</p>
53 <p>golang version: {{.GoVersion}}</p>
54 </div>
55 </body>
56 </html>
57 `
58
59 func ShowErr(err interface{}, rw http.ResponseWriter, r *http.Request, Stack string) {
60 t, _ := template.New("beegoerrortemp").Parse(tpl)
61 data := make(map[string]string)
62 data["AppError"] = AppName + ":" + fmt.Sprint(err)
63 data["RequestMethod"] = r.Method
64 data["RequestURL"] = r.RequestURI
65 data["RemoteAddr"] = r.RemoteAddr
66 data["Stack"] = Stack
67 data["BeegoVersion"] = VERSION
68 data["GoVersion"] = runtime.Version()
69 t.Execute(rw, data)
70 }
71
72 var errtpl = `
73 <!DOCTYPE html>
74 <html lang="en">
75 <head>
76 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
77 <title>{{.Title}}</title>
78 <style type="text/css">
79 * {
80 margin:0;
81 padding:0;
82 }
83
84 body {
85 background-color:#EFEFEF;
86 font: .9em "Lucida Sans Unicode", "Lucida Grande", sans-serif;
87 }
88
89 #wrapper{
90 width:600px;
91 margin:40px auto 0;
92 text-align:center;
93 -moz-box-shadow: 5px 5px 10px rgba(0,0,0,0.3);
94 -webkit-box-shadow: 5px 5px 10px rgba(0,0,0,0.3);
95 box-shadow: 5px 5px 10px rgba(0,0,0,0.3);
96 }
97
98 #wrapper h1{
99 color:#FFF;
100 text-align:center;
101 margin-bottom:20px;
102 }
103
104 #wrapper a{
105 display:block;
106 font-size:.9em;
107 padding-top:20px;
108 color:#FFF;
109 text-decoration:none;
110 text-align:center;
111 }
112
113 #container {
114 width:600px;
115 padding-bottom:15px;
116 background-color:#FFFFFF;
117 }
118
119 .navtop{
120 height:40px;
121 background-color:#24B2EB;
122 padding:13px;
123 }
124
125 .content {
126 padding:10px 10px 25px;
127 background: #FFFFFF;
128 margin:;
129 color:#333;
130 }
131
132 a.button{
133 color:white;
134 padding:15px 20px;
135 text-shadow:1px 1px 0 #00A5FF;
136 font-weight:bold;
137 text-align:center;
138 border:1px solid #24B2EB;
139 margin:0px 200px;
140 clear:both;
141 background-color: #24B2EB;
142 border-radius:100px;
143 -moz-border-radius:100px;
144 -webkit-border-radius:100px;
145 }
146
147 a.button:hover{
148 text-decoration:none;
149 background-color: #24B2EB;
150 }
151
152 </style>
153 </head>
154 <body>
155 <div id="wrapper">
156 <div id="container">
157 <div class="navtop">
158 <h1>{{.Title}}</h1>
159 </div>
160 <div id="content">
161 {{.Content}}
162 <a href="/" title="Home" class="button">Go Home</a><br />
163
164 <br>power by beego {{.BeegoVersion}}
165 </div>
166 </div>
167 </div>
168 </body>
169 </html>
170 `
171
172 var ErrorMaps map[string]http.HandlerFunc
173
174 func init() {
175 ErrorMaps = make(map[string]http.HandlerFunc)
176 }
177
178 //404
179 func NotFound(rw http.ResponseWriter, r *http.Request) {
180 t, _ := template.New("beegoerrortemp").Parse(errtpl)
181 data := make(map[string]interface{})
182 data["Title"] = "Page Not Found"
183 data["Content"] = template.HTML("<br>The Page You have requested flown the coop." +
184 "<br>Perhaps you are here because:" +
185 "<br><br><ul>" +
186 "<br>The page has moved" +
187 "<br>The page no longer exists" +
188 "<br>You were looking for your puppy and got lost" +
189 "<br>You like 404 pages" +
190 "</ul>")
191 data["BeegoVersion"] = VERSION
192 rw.WriteHeader(http.StatusNotFound)
193 t.Execute(rw, data)
194 }
195
196 //401
197 func Unauthorized(rw http.ResponseWriter, r *http.Request) {
198 t, _ := template.New("beegoerrortemp").Parse(errtpl)
199 data := make(map[string]interface{})
200 data["Title"] = "Unauthorized"
201 data["Content"] = template.HTML("<br>The Page You have requested can't authorized." +
202 "<br>Perhaps you are here because:" +
203 "<br><br><ul>" +
204 "<br>Check the credentials that you supplied" +
205 "<br>Check the address for errors" +
206 "</ul>")
207 data["BeegoVersion"] = VERSION
208 rw.WriteHeader(http.StatusUnauthorized)
209 t.Execute(rw, data)
210 }
211
212 //403
213 func Forbidden(rw http.ResponseWriter, r *http.Request) {
214 t, _ := template.New("beegoerrortemp").Parse(errtpl)
215 data := make(map[string]interface{})
216 data["Title"] = "Forbidden"
217 data["Content"] = template.HTML("<br>The Page You have requested forbidden." +
218 "<br>Perhaps you are here because:" +
219 "<br><br><ul>" +
220 "<br>Your address may be blocked" +
221 "<br>The site may be disabled" +
222 "<br>You need to log in" +
223 "</ul>")
224 data["BeegoVersion"] = VERSION
225 rw.WriteHeader(http.StatusForbidden)
226 t.Execute(rw, data)
227 }
228
229 //503
230 func ServiceUnavailable(rw http.ResponseWriter, r *http.Request) {
231 t, _ := template.New("beegoerrortemp").Parse(errtpl)
232 data := make(map[string]interface{})
233 data["Title"] = "Service Unavailable"
234 data["Content"] = template.HTML("<br>The Page You have requested unavailable." +
235 "<br>Perhaps you are here because:" +
236 "<br><br><ul>" +
237 "<br><br>The page is overloaded" +
238 "<br>Please try again later." +
239 "</ul>")
240 data["BeegoVersion"] = VERSION
241 rw.WriteHeader(http.StatusServiceUnavailable)
242 t.Execute(rw, data)
243 }
244
245 //500
246 func InternalServerError(rw http.ResponseWriter, r *http.Request) {
247 t, _ := template.New("beegoerrortemp").Parse(errtpl)
248 data := make(map[string]interface{})
249 data["Title"] = "Internal Server Error"
250 data["Content"] = template.HTML("<br>The Page You have requested has down now." +
251 "<br><br><ul>" +
252 "<br>simply try again later" +
253 "<br>you should report the fault to the website administrator" +
254 "</ul>")
255 data["BeegoVersion"] = VERSION
256 rw.WriteHeader(http.StatusInternalServerError)
257 t.Execute(rw, data)
258 }
259
260 func registerErrorHander() {
261 if _, ok := ErrorMaps["404"]; !ok {
262 ErrorMaps["404"] = NotFound
263 }
264
265 if _, ok := ErrorMaps["401"]; !ok {
266 ErrorMaps["401"] = Unauthorized
267 }
268
269 if _, ok := ErrorMaps["403"]; !ok {
270 ErrorMaps["403"] = Forbidden
271 }
272
273 if _, ok := ErrorMaps["503"]; !ok {
274 ErrorMaps["503"] = ServiceUnavailable
275 }
276
277 if _, ok := ErrorMaps["500"]; !ok {
278 ErrorMaps["500"] = InternalServerError
279 }
280 }
1 package middleware
...\ No newline at end of file ...\ No newline at end of file
1 package middleware
2
3 import (
4 "fmt"
5 "log"
6 "os"
7 "runtime"
8 "runtime/debug"
9 "runtime/pprof"
10 "strconv"
11 "sync/atomic"
12 "time"
13 )
14
15 var heapProfileCounter int32
16 var startTime = time.Now()
17 var pid int
18
19 func init() {
20 pid = os.Getpid()
21 }
22
23 func StartCPUProfile() {
24 f, err := os.Create("cpu-" + strconv.Itoa(pid) + ".pprof")
25 if err != nil {
26 log.Fatal(err)
27 }
28 pprof.StartCPUProfile(f)
29 }
30
31 func StopCPUProfile() {
32 pprof.StopCPUProfile()
33 }
34
35 func StartBlockProfile(rate int) {
36 runtime.SetBlockProfileRate(rate)
37 }
38
39 func StopBlockProfile() {
40 filename := "block-" + strconv.Itoa(pid) + ".pprof"
41 f, err := os.Create(filename)
42 if err != nil {
43 log.Fatal(err)
44 }
45 if err = pprof.Lookup("block").WriteTo(f, 0); err != nil {
46 log.Fatalf(" can't write %s: %s", filename, err)
47 }
48 f.Close()
49 }
50
51 func SetMemProfileRate(rate int) {
52 runtime.MemProfileRate = rate
53 }
54
55 func GC() {
56 runtime.GC()
57 }
58
59 func DumpHeap() {
60 filename := "heap-" + strconv.Itoa(pid) + "-" + strconv.Itoa(int(atomic.AddInt32(&heapProfileCounter, 1))) + ".pprof"
61 f, err := os.Create(filename)
62 if err != nil {
63 fmt.Fprintf(os.Stderr, "testing: %s", err)
64 return
65 }
66 if err = pprof.WriteHeapProfile(f); err != nil {
67 fmt.Fprintf(os.Stderr, "testing: can't write %s: %s", filename, err)
68 }
69 f.Close()
70 }
71
72 //func showSystemStat(interval time.Duration, count int) {
73
74 // usage1 := &syscall.Rusage{}
75 // var lastUtime int64
76 // var lastStime int64
77
78 // counter := 0
79 // for {
80
81 // //http://man7.org/linux/man-pages/man3/vtimes.3.html
82 // syscall.Getrusage(syscall.RUSAGE_SELF, usage1)
83
84 // utime := (usage1.Utime.Sec * 1000000000) + int64(usage1.Utime.Usec)
85 // stime := (usage1.Stime.Sec * 1000000000) + int64(usage1.Stime.Usec)
86 // userCPUUtil := float64(utime-lastUtime) * 100 / float64(interval)
87 // sysCPUUtil := float64(stime-lastStime) * 100 / float64(interval)
88 // memUtil := usage1.Maxrss * 1024
89
90 // lastUtime = utime
91 // lastStime = stime
92
93 // if counter > 0 {
94 // fmt.Printf("cpu: %3.2f%% us %3.2f%% sy, mem:%s \n", userCPUUtil, sysCPUUtil, toH(uint64(memUtil)))
95 // }
96
97 // counter += 1
98 // if count >= 1 && count < counter {
99 // return
100 // }
101 // time.Sleep(interval)
102 // }
103
104 //}
105
106 //func ShowSystemStat(seconds int) {
107 // go func() {
108 // interval := time.Duration(seconds) * time.Second
109 // showSystemStat(interval, 0)
110 // }()
111 //}
112
113 //func PrintSystemStats() {
114 // interval := time.Duration(1) * time.Second
115 // showSystemStat(interval, 1)
116 //}
117
118 func ShowGCStat() {
119 go func() {
120 var numGC int64
121
122 interval := time.Duration(100) * time.Millisecond
123 gcstats := &debug.GCStats{PauseQuantiles: make([]time.Duration, 100)}
124 memStats := &runtime.MemStats{}
125 for {
126 debug.ReadGCStats(gcstats)
127 if gcstats.NumGC > numGC {
128 runtime.ReadMemStats(memStats)
129
130 printGC(memStats, gcstats)
131 numGC = gcstats.NumGC
132 }
133 time.Sleep(interval)
134 }
135 }()
136 }
137
138 func PrintGCSummary() {
139 memStats := &runtime.MemStats{}
140 runtime.ReadMemStats(memStats)
141 gcstats := &debug.GCStats{PauseQuantiles: make([]time.Duration, 100)}
142 debug.ReadGCStats(gcstats)
143
144 printGC(memStats, gcstats)
145 }
146
147 func printGC(memStats *runtime.MemStats, gcstats *debug.GCStats) {
148
149 if gcstats.NumGC > 0 {
150 lastPause := gcstats.Pause[0]
151 elapsed := time.Now().Sub(startTime)
152 overhead := float64(gcstats.PauseTotal) / float64(elapsed) * 100
153 allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds()
154
155 fmt.Printf("NumGC:%d Pause:%s Pause(Avg):%s Overhead:%3.2f%% Alloc:%s Sys:%s Alloc(Rate):%s/s Histogram:%s %s %s \n",
156 gcstats.NumGC,
157 toS(lastPause),
158 toS(avg(gcstats.Pause)),
159 overhead,
160 toH(memStats.Alloc),
161 toH(memStats.Sys),
162 toH(uint64(allocatedRate)),
163 toS(gcstats.PauseQuantiles[94]),
164 toS(gcstats.PauseQuantiles[98]),
165 toS(gcstats.PauseQuantiles[99]))
166 } else {
167 // while GC has disabled
168 elapsed := time.Now().Sub(startTime)
169 allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds()
170
171 fmt.Printf("Alloc:%s Sys:%s Alloc(Rate):%s/s\n",
172 toH(memStats.Alloc),
173 toH(memStats.Sys),
174 toH(uint64(allocatedRate)))
175 }
176 }
177
178 func avg(items []time.Duration) time.Duration {
179 var sum time.Duration
180 for _, item := range items {
181 sum += item
182 }
183 return time.Duration(int64(sum) / int64(len(items)))
184 }
185
186 // human readable format
187 func toH(bytes uint64) string {
188 switch {
189 case bytes < 1024:
190 return fmt.Sprintf("%dB", bytes)
191 case bytes < 1024*1024:
192 return fmt.Sprintf("%.2fK", float64(bytes)/1024)
193 case bytes < 1024*1024*1024:
194 return fmt.Sprintf("%.2fM", float64(bytes)/1024/1024)
195 default:
196 return fmt.Sprintf("%.2fG", float64(bytes)/1024/1024/1024)
197 }
198 }
199
200 // short string format
201 func toS(d time.Duration) string {
202
203 u := uint64(d)
204 if u < uint64(time.Second) {
205 switch {
206 case u == 0:
207 return "0"
208 case u < uint64(time.Microsecond):
209 return fmt.Sprintf("%.2fns", float64(u))
210 case u < uint64(time.Millisecond):
211 return fmt.Sprintf("%.2fus", float64(u)/1000)
212 default:
213 return fmt.Sprintf("%.2fms", float64(u)/1000/1000)
214 }
215 } else {
216 switch {
217 case u < uint64(time.Minute):
218 return fmt.Sprintf("%.2fs", float64(u)/1000/1000/1000)
219 case u < uint64(time.Hour):
220 return fmt.Sprintf("%.2fm", float64(u)/1000/1000/1000/60)
221 default:
222 return fmt.Sprintf("%.2fh", float64(u)/1000/1000/1000/60/60)
223 }
224 }
225
226 }
1 package middleware
2
3 import (
4 "github.com/astaxie/beego/session"
5 )
6
7 var (
8 GlobalSessions *session.Manager
9 )
10
11 func StartSession(provideName, cookieName string, maxlifetime int64, savePath string) {
12 GlobalSessions, _ = session.NewManager(provideName, cookieName, maxlifetime, savePath)
13 go GlobalSessions.GC()
14 }
1 package beego
2
3 import (
4 "net/http/pprof"
5 )
6
7 type ProfController struct {
8 Controller
9 }
10
11 func (this *ProfController) Get() {
12 switch this.Ctx.Params[":pp"] {
13 default:
14 pprof.Index(this.Ctx.ResponseWriter, this.Ctx.Request)
15 case "":
16 pprof.Index(this.Ctx.ResponseWriter, this.Ctx.Request)
17 case "cmdline":
18 pprof.Cmdline(this.Ctx.ResponseWriter, this.Ctx.Request)
19 case "profile":
20 pprof.Profile(this.Ctx.ResponseWriter, this.Ctx.Request)
21 case "symbol":
22 pprof.Symbol(this.Ctx.ResponseWriter, this.Ctx.Request)
23 }
24 this.Ctx.ResponseWriter.WriteHeader(200)
25 }
1 package beego 1 package beego
2 2
3 import ( 3 import (
4 "bytes"
5 "fmt" 4 "fmt"
6 "io/ioutil" 5 beecontext "github.com/astaxie/beego/context"
7 "net/http" 6 "net/http"
8 "net/url" 7 "net/url"
9 "os" 8 "os"
...@@ -25,31 +24,20 @@ type controllerInfo struct { ...@@ -25,31 +24,20 @@ type controllerInfo struct {
25 hasMethod bool 24 hasMethod bool
26 } 25 }
27 26
28 type userHandler struct {
29 pattern string
30 regex *regexp.Regexp
31 params map[int]string
32 h http.Handler
33 }
34
35 type ControllerRegistor struct { 27 type ControllerRegistor struct {
36 routers []*controllerInfo 28 routers []*controllerInfo
37 fixrouters []*controllerInfo 29 fixrouters []*controllerInfo
38 enableFilter bool 30 enableFilter bool
39 filters []http.HandlerFunc 31 filters map[string][]*FilterRouter
40 enableAfter bool
41 afterFilters []http.HandlerFunc
42 enableUser bool
43 userHandlers map[string]*userHandler
44 enableAuto bool 32 enableAuto bool
45 autoRouter map[string]map[string]reflect.Type //key:controller key:method value:reflect.type 33 autoRouter map[string]map[string]reflect.Type //key:controller key:method value:reflect.type
46 } 34 }
47 35
48 func NewControllerRegistor() *ControllerRegistor { 36 func NewControllerRegistor() *ControllerRegistor {
49 return &ControllerRegistor{ 37 return &ControllerRegistor{
50 routers: make([]*controllerInfo, 0), 38 routers: make([]*controllerInfo, 0),
51 userHandlers: make(map[string]*userHandler), 39 autoRouter: make(map[string]map[string]reflect.Type),
52 autoRouter: make(map[string]map[string]reflect.Type), 40 filters: make(map[string][]*FilterRouter),
53 } 41 }
54 } 42 }
55 43
...@@ -182,35 +170,39 @@ func (p *ControllerRegistor) AddAuto(c ControllerInterface) { ...@@ -182,35 +170,39 @@ func (p *ControllerRegistor) AddAuto(c ControllerInterface) {
182 } 170 }
183 } 171 }
184 172
185 func (p *ControllerRegistor) AddHandler(pattern string, c http.Handler) { 173 // Filter adds the middleware filter.
186 p.enableUser = true 174 func (p *ControllerRegistor) AddFilter(pattern, action string, filter FilterFunc) {
187 parts := strings.Split(pattern, "/") 175 p.enableFilter = true
176 mr := new(FilterRouter)
177 mr.filterFunc = filter
188 178
179 parts := strings.Split(pattern, "/")
189 j := 0 180 j := 0
190 params := make(map[int]string)
191 for i, part := range parts { 181 for i, part := range parts {
192 if strings.HasPrefix(part, ":") { 182 if strings.HasPrefix(part, ":") {
193 expr := "([^/]+)" 183 expr := "(.+)"
194 //a user may choose to override the defult expression 184 //a user may choose to override the defult expression
195 // similar to expressjs: ‘/user/:id([0-9]+)’ 185 // similar to expressjs: ‘/user/:id([0-9]+)’
196 if index := strings.Index(part, "("); index != -1 { 186 if index := strings.Index(part, "("); index != -1 {
197 expr = part[index:] 187 expr = part[index:]
198 part = part[:index] 188 part = part[:index]
189 //match /user/:id:int ([0-9]+)
190 //match /post/:username:string ([\w]+)
191 } else if lindex := strings.LastIndex(part, ":"); lindex != 0 {
192 switch part[lindex:] {
193 case ":int":
194 expr = "([0-9]+)"
195 part = part[:lindex]
196 case ":string":
197 expr = `([\w]+)`
198 part = part[:lindex]
199 }
199 } 200 }
200 params[j] = part
201 parts[i] = expr 201 parts[i] = expr
202 j++ 202 j++
203 } 203 }
204 } 204 }
205 if j == 0 { 205 if j != 0 {
206 //now create the Route
207 uh := &userHandler{}
208 uh.pattern = pattern
209 uh.h = c
210 p.userHandlers[pattern] = uh
211 } else { // add regexp routers
212 //recreate the url pattern, with parameters replaced
213 //by regular expressions. then compile the regex
214 pattern = strings.Join(parts, "/") 206 pattern = strings.Join(parts, "/")
215 regex, regexErr := regexp.Compile(pattern) 207 regex, regexErr := regexp.Compile(pattern)
216 if regexErr != nil { 208 if regexErr != nil {
...@@ -218,73 +210,11 @@ func (p *ControllerRegistor) AddHandler(pattern string, c http.Handler) { ...@@ -218,73 +210,11 @@ func (p *ControllerRegistor) AddHandler(pattern string, c http.Handler) {
218 panic(regexErr) 210 panic(regexErr)
219 return 211 return
220 } 212 }
221 213 mr.regex = regex
222 //now create the Route 214 mr.hasregex = true
223 uh := &userHandler{}
224 uh.regex = regex
225 uh.params = params
226 uh.pattern = pattern
227 uh.h = c
228 p.userHandlers[pattern] = uh
229 } 215 }
230 } 216 mr.pattern = pattern
231 217 p.filters[action] = append(p.filters[action], mr)
232 // Filter adds the middleware filter.
233 func (p *ControllerRegistor) Filter(filter http.HandlerFunc) {
234 p.enableFilter = true
235 p.filters = append(p.filters, filter)
236 }
237
238 // FilterParam adds the middleware filter if the REST URL parameter exists.
239 func (p *ControllerRegistor) FilterParam(param string, filter http.HandlerFunc) {
240 if !strings.HasPrefix(param, ":") {
241 param = ":" + param
242 }
243
244 p.Filter(func(w http.ResponseWriter, r *http.Request) {
245 p := r.URL.Query().Get(param)
246 if len(p) > 0 {
247 filter(w, r)
248 }
249 })
250 }
251
252 // FilterPrefixPath adds the middleware filter if the prefix path exists.
253 func (p *ControllerRegistor) FilterPrefixPath(path string, filter http.HandlerFunc) {
254 p.Filter(func(w http.ResponseWriter, r *http.Request) {
255 if strings.HasPrefix(r.URL.Path, path) {
256 filter(w, r)
257 }
258 })
259 }
260
261 // Filter adds the middleware after filter.
262 func (p *ControllerRegistor) FilterAfter(filter http.HandlerFunc) {
263 p.enableAfter = true
264 p.afterFilters = append(p.afterFilters, filter)
265 }
266
267 // FilterParam adds the middleware filter if the REST URL parameter exists.
268 func (p *ControllerRegistor) FilterParamAfter(param string, filter http.HandlerFunc) {
269 if !strings.HasPrefix(param, ":") {
270 param = ":" + param
271 }
272
273 p.FilterAfter(func(w http.ResponseWriter, r *http.Request) {
274 p := r.URL.Query().Get(param)
275 if len(p) > 0 {
276 filter(w, r)
277 }
278 })
279 }
280
281 // FilterPrefixPath adds the middleware filter if the prefix path exists.
282 func (p *ControllerRegistor) FilterPrefixPathAfter(path string, filter http.HandlerFunc) {
283 p.FilterAfter(func(w http.ResponseWriter, r *http.Request) {
284 if strings.HasPrefix(r.URL.Path, path) {
285 filter(w, r)
286 }
287 })
288 } 218 }
289 219
290 // AutoRoute 220 // AutoRoute
...@@ -320,13 +250,30 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -320,13 +250,30 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
320 }() 250 }()
321 251
322 w := &responseWriter{writer: rw} 252 w := &responseWriter{writer: rw}
323
324 w.Header().Set("Server", "beegoServer") 253 w.Header().Set("Server", "beegoServer")
254 context := &beecontext.Context{
255 ResponseWriter: w,
256 Request: r,
257 Input: beecontext.NewInput(r),
258 Output: beecontext.NewOutput(w),
259 }
260 context.Output.Context = context
325 var runrouter *controllerInfo 261 var runrouter *controllerInfo
326 var findrouter bool 262 var findrouter bool
327 263
328 params := make(map[string]string) 264 params := make(map[string]string)
329 265
266 context.Input.Param = params
267 if p.enableFilter {
268 if l, ok := p.filters["BeforRouter"]; ok {
269 for _, filterR := range l {
270 if filterR.ValidRouter(r.URL.Path) {
271 filterR.filterFunc(context)
272 }
273 }
274 }
275 }
276
330 //static file server 277 //static file server
331 for prefix, staticDir := range StaticDir { 278 for prefix, staticDir := range StaticDir {
332 if r.URL.Path == "/favicon.ico" { 279 if r.URL.Path == "/favicon.ico" {
...@@ -359,62 +306,23 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -359,62 +306,23 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
359 } 306 }
360 } 307 }
361 308
309 if p.enableFilter {
310 if l, ok := p.filters["AfterStatic"]; ok {
311 for _, filterR := range l {
312 if filterR.ValidRouter(r.URL.Path) {
313 filterR.filterFunc(context)
314 }
315 }
316 }
317 }
362 requestPath := r.URL.Path 318 requestPath := r.URL.Path
363 319
364 var requestbody []byte
365
366 if CopyRequestBody { 320 if CopyRequestBody {
367 requestbody, _ = ioutil.ReadAll(r.Body) 321 context.Input.Body()
368
369 r.Body.Close()
370
371 bf := bytes.NewBuffer(requestbody)
372
373 r.Body = ioutil.NopCloser(bf)
374 } 322 }
375 323
376 r.ParseMultipartForm(MaxMemory) 324 r.ParseMultipartForm(MaxMemory)
377 325
378 //user defined Handler
379 if p.enableUser {
380 for pattern, c := range p.userHandlers {
381 if c.regex == nil && pattern == requestPath {
382 c.h.ServeHTTP(rw, r)
383 return
384 } else if c.regex == nil {
385 continue
386 }
387
388 //check if Route pattern matches url
389 if !c.regex.MatchString(requestPath) {
390 continue
391 }
392
393 //get submatches (params)
394 matches := c.regex.FindStringSubmatch(requestPath)
395
396 //double check that the Route matches the URL pattern.
397 if len(matches[0]) != len(requestPath) {
398 continue
399 }
400
401 if len(c.params) > 0 {
402 //add url parameters to the query param map
403 values := r.URL.Query()
404 for i, match := range matches[1:] {
405 values.Add(c.params[i], match)
406 r.Form.Add(c.params[i], match)
407 params[c.params[i]] = match
408 }
409 //reassemble query params and add to RawQuery
410 r.URL.RawQuery = url.Values(values).Encode() + "&" + r.URL.RawQuery
411 //r.URL.RawQuery = url.Values(values).Encode()
412 }
413 c.h.ServeHTTP(rw, r)
414 return
415 }
416 }
417
418 //first find path from the fixrouters to Improve Performance 326 //first find path from the fixrouters to Improve Performance
419 for _, route := range p.fixrouters { 327 for _, route := range p.fixrouters {
420 n := len(requestPath) 328 n := len(requestPath)
...@@ -471,23 +379,21 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -471,23 +379,21 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
471 if runrouter != nil { 379 if runrouter != nil {
472 //execute middleware filters 380 //execute middleware filters
473 if p.enableFilter { 381 if p.enableFilter {
474 for _, filter := range p.filters { 382 if l, ok := p.filters["BeforExec"]; ok {
475 filter(w, r) 383 for _, filterR := range l {
476 if w.started { 384 if filterR.ValidRouter(r.URL.Path) {
477 return 385 filterR.filterFunc(context)
386 }
478 } 387 }
479 } 388 }
480 } 389 }
481
482 //Invoke the request handler 390 //Invoke the request handler
483 vc := reflect.New(runrouter.controllerType) 391 vc := reflect.New(runrouter.controllerType)
484 392
485 //call the controller init function 393 //call the controller init function
486 init := vc.MethodByName("Init") 394 init := vc.MethodByName("Init")
487 in := make([]reflect.Value, 2) 395 in := make([]reflect.Value, 2)
488 ct := &Context{ResponseWriter: w, Request: r, Params: params, RequestBody: requestbody} 396 in[0] = reflect.ValueOf(context)
489
490 in[0] = reflect.ValueOf(ct)
491 in[1] = reflect.ValueOf(runrouter.controllerType.Name()) 397 in[1] = reflect.ValueOf(runrouter.controllerType.Name())
492 init.Call(in) 398 init.Call(in)
493 //call prepare function 399 //call prepare function
...@@ -617,14 +523,16 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -617,14 +523,16 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
617 } 523 }
618 } 524 }
619 } 525 }
526
620 method = vc.MethodByName("Finish") 527 method = vc.MethodByName("Finish")
621 method.Call(in) 528 method.Call(in)
622 //execute middleware filters 529 //execute middleware filters
623 if p.enableAfter { 530 if p.enableFilter {
624 for _, filter := range p.afterFilters { 531 if l, ok := p.filters["AfterExec"]; ok {
625 filter(w, r) 532 for _, filterR := range l {
626 if w.started { 533 if filterR.ValidRouter(r.URL.Path) {
627 return 534 filterR.filterFunc(context)
535 }
628 } 536 }
629 } 537 }
630 } 538 }
...@@ -651,10 +559,11 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -651,10 +559,11 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
651 if strings.HasPrefix(strings.ToLower(requestPath), "/"+cName+"/"+strings.ToLower(mName)) { 559 if strings.HasPrefix(strings.ToLower(requestPath), "/"+cName+"/"+strings.ToLower(mName)) {
652 //execute middleware filters 560 //execute middleware filters
653 if p.enableFilter { 561 if p.enableFilter {
654 for _, filter := range p.filters { 562 if l, ok := p.filters["BeforExec"]; ok {
655 filter(w, r) 563 for _, filterR := range l {
656 if w.started { 564 if filterR.ValidRouter(r.URL.Path) {
657 return 565 filterR.filterFunc(context)
566 }
658 } 567 }
659 } 568 }
660 } 569 }
...@@ -672,9 +581,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -672,9 +581,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
672 //call the controller init function 581 //call the controller init function
673 init := vc.MethodByName("Init") 582 init := vc.MethodByName("Init")
674 in := make([]reflect.Value, 2) 583 in := make([]reflect.Value, 2)
675 ct := &Context{ResponseWriter: w, Request: r, Params: params, RequestBody: requestbody} 584 in[0] = reflect.ValueOf(context)
676
677 in[0] = reflect.ValueOf(ct)
678 in[1] = reflect.ValueOf(controllerType.Name()) 585 in[1] = reflect.ValueOf(controllerType.Name())
679 init.Call(in) 586 init.Call(in)
680 //call prepare function 587 //call prepare function
...@@ -702,11 +609,12 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -702,11 +609,12 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
702 method = vc.MethodByName("Finish") 609 method = vc.MethodByName("Finish")
703 method.Call(in) 610 method.Call(in)
704 //execute middleware filters 611 //execute middleware filters
705 if p.enableAfter { 612 if p.enableFilter {
706 for _, filter := range p.afterFilters { 613 if l, ok := p.filters["AfterExec"]; ok {
707 filter(w, r) 614 for _, filterR := range l {
708 if w.started { 615 if filterR.ValidRouter(r.URL.Path) {
709 return 616 filterR.filterFunc(context)
617 }
710 } 618 }
711 } 619 }
712 } 620 }
...@@ -714,6 +622,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -714,6 +622,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
714 method.Call(in) 622 method.Call(in)
715 // set find 623 // set find
716 findrouter = true 624 findrouter = true
625 goto Last
717 } 626 }
718 } 627 }
719 } 628 }
...@@ -721,6 +630,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) ...@@ -721,6 +630,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
721 } 630 }
722 } 631 }
723 632
633 Last:
724 //if no matches to url, throw a not found exception 634 //if no matches to url, throw a not found exception
725 if !findrouter { 635 if !findrouter {
726 if h, ok := ErrorMaps["404"]; ok { 636 if h, ok := ErrorMaps["404"]; ok {
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!