diff --git a/context/context.go b/context/context.go index 8f1144f3..a7df6f8d 100644 --- a/context/context.go +++ b/context/context.go @@ -35,6 +35,14 @@ import ( "github.com/astaxie/beego/utils" ) +// NewContext return the Context with Input and Output +func NewContext() *Context { + return &Context{ + Input: NewInput(), + Output: NewOutput(), + } +} + // Context Http request context struct including BeegoInput, BeegoOutput, http.Request and http.ResponseWriter. // BeegoInput and BeegoOutput provides some api to operate request and response more easily. type Context struct { @@ -45,6 +53,14 @@ type Context struct { _xsrfToken string } +// Reset init Context, BeegoInput and BeegoOutput +func (ctx *Context) Reset(rw http.ResponseWriter, r *http.Request) { + ctx.Request = r + ctx.ResponseWriter = rw + ctx.Input.Reset(ctx) + ctx.Output.Reset(ctx) +} + // Redirect does redirection to localurl with http header status code. // It sends http response header directly. func (ctx *Context) Redirect(status int, localurl string) { diff --git a/context/input.go b/context/input.go index 53cc3e88..9649e14f 100644 --- a/context/input.go +++ b/context/input.go @@ -18,7 +18,6 @@ import ( "bytes" "errors" "io/ioutil" - "net/http" "net/url" "reflect" "regexp" @@ -39,37 +38,43 @@ var ( // BeegoInput operates the http request header, data, cookie and body. // it also contains router params and current session. type BeegoInput struct { - CruSession session.Store - Params map[string]string - Data map[interface{}]interface{} // store some values in this context when calling context in filter or controller. - Request *http.Request - RequestBody []byte - RunController reflect.Type - RunMethod string + Context *Context + CruSession session.Store + Params map[string]string + Data map[interface{}]interface{} // store some values in this context when calling context in filter or controller. + RequestBody []byte } -// NewInput return BeegoInput generated by http.Request. -func NewInput(req *http.Request) *BeegoInput { +// NewInput return BeegoInput generated by Context. +func NewInput() *BeegoInput { return &BeegoInput{ - Params: make(map[string]string), - Data: make(map[interface{}]interface{}), - Request: req, + Params: make(map[string]string), + Data: make(map[interface{}]interface{}), } } +// Reset init the BeegoInput +func (input *BeegoInput) Reset(ctx *Context) { + input.Context = ctx + input.CruSession = nil + input.Params = make(map[string]string) + input.Data = make(map[interface{}]interface{}) + input.RequestBody = []byte{} +} + // Protocol returns request protocol name, such as HTTP/1.1 . func (input *BeegoInput) Protocol() string { - return input.Request.Proto + return input.Context.Request.Proto } // URI returns full request url with query string, fragment. func (input *BeegoInput) URI() string { - return input.Request.RequestURI + return input.Context.Request.RequestURI } // URL returns request url path (without query string, fragment). func (input *BeegoInput) URL() string { - return input.Request.URL.Path + return input.Context.Request.URL.Path } // Site returns base site url as scheme://domain type. @@ -79,10 +84,10 @@ func (input *BeegoInput) Site() string { // Scheme returns request scheme as "http" or "https". func (input *BeegoInput) Scheme() string { - if input.Request.URL.Scheme != "" { - return input.Request.URL.Scheme + if input.Context.Request.URL.Scheme != "" { + return input.Context.Request.URL.Scheme } - if input.Request.TLS == nil { + if input.Context.Request.TLS == nil { return "http" } return "https" @@ -97,19 +102,19 @@ func (input *BeegoInput) Domain() string { // Host returns host name. // if no host info in request, return localhost. func (input *BeegoInput) Host() string { - if input.Request.Host != "" { - hostParts := strings.Split(input.Request.Host, ":") + if input.Context.Request.Host != "" { + hostParts := strings.Split(input.Context.Request.Host, ":") if len(hostParts) > 0 { return hostParts[0] } - return input.Request.Host + return input.Context.Request.Host } return "localhost" } // Method returns http request method. func (input *BeegoInput) Method() string { - return input.Request.Method + return input.Context.Request.Method } // Is returns boolean of this request is on given method, such as Is("POST"). @@ -196,7 +201,7 @@ func (input *BeegoInput) IP() string { rip := strings.Split(ips[0], ":") return rip[0] } - ip := strings.Split(input.Request.RemoteAddr, ":") + ip := strings.Split(input.Context.Request.RemoteAddr, ":") if len(ip) > 0 { if ip[0] != "[" { return ip[0] @@ -236,7 +241,7 @@ func (input *BeegoInput) SubDomains() string { // Port returns request client port. // when error or empty, return 80. func (input *BeegoInput) Port() int { - parts := strings.Split(input.Request.Host, ":") + parts := strings.Split(input.Context.Request.Host, ":") if len(parts) == 2 { port, _ := strconv.Atoi(parts[1]) return port @@ -262,22 +267,22 @@ func (input *BeegoInput) Query(key string) string { if val := input.Param(key); val != "" { return val } - if input.Request.Form == nil { - input.Request.ParseForm() + if input.Context.Request.Form == nil { + input.Context.Request.ParseForm() } - return input.Request.Form.Get(key) + return input.Context.Request.Form.Get(key) } // Header returns request header item string by a given string. // if non-existed, return empty string. func (input *BeegoInput) Header(key string) string { - return input.Request.Header.Get(key) + return input.Context.Request.Header.Get(key) } // Cookie returns request cookie item string by a given key. // if non-existed, return empty string. func (input *BeegoInput) Cookie(key string) string { - ck, err := input.Request.Cookie(key) + ck, err := input.Context.Request.Cookie(key) if err != nil { return "" } @@ -292,10 +297,10 @@ func (input *BeegoInput) Session(key interface{}) interface{} { // CopyBody returns the raw request body data as bytes. func (input *BeegoInput) CopyBody() []byte { - requestbody, _ := ioutil.ReadAll(input.Request.Body) - input.Request.Body.Close() + requestbody, _ := ioutil.ReadAll(input.Context.Request.Body) + input.Context.Request.Body.Close() bf := bytes.NewBuffer(requestbody) - input.Request.Body = ioutil.NopCloser(bf) + input.Context.Request.Body = ioutil.NopCloser(bf) input.RequestBody = requestbody return requestbody } @@ -318,10 +323,10 @@ func (input *BeegoInput) SetData(key, val interface{}) { func (input *BeegoInput) ParseFormOrMulitForm(maxMemory int64) error { // Parse the body depending on the content type. if strings.Contains(input.Header("Content-Type"), "multipart/form-data") { - if err := input.Request.ParseMultipartForm(maxMemory); err != nil { + if err := input.Context.Request.ParseMultipartForm(maxMemory); err != nil { return errors.New("Error parsing request body:" + err.Error()) } - } else if err := input.Request.ParseForm(); err != nil { + } else if err := input.Context.Request.ParseForm(); err != nil { return errors.New("Error parsing request body:" + err.Error()) } return nil @@ -386,13 +391,13 @@ func (input *BeegoInput) bind(key string, typ reflect.Type) reflect.Value { } rv = input.bindBool(val, typ) case reflect.Slice: - rv = input.bindSlice(&input.Request.Form, key, typ) + rv = input.bindSlice(&input.Context.Request.Form, key, typ) case reflect.Struct: - rv = input.bindStruct(&input.Request.Form, key, typ) + rv = input.bindStruct(&input.Context.Request.Form, key, typ) case reflect.Ptr: rv = input.bindPoint(key, typ) case reflect.Map: - rv = input.bindMap(&input.Request.Form, key, typ) + rv = input.bindMap(&input.Context.Request.Form, key, typ) } return rv } diff --git a/context/output.go b/context/output.go index f0d66f36..2d756e27 100644 --- a/context/output.go +++ b/context/output.go @@ -43,6 +43,12 @@ func NewOutput() *BeegoOutput { return &BeegoOutput{} } +// Reset init BeegoOutput +func (output *BeegoOutput) Reset(ctx *Context) { + output.Context = ctx + output.Status = 0 +} + // Header sets response header item string via given key. func (output *BeegoOutput) Header(key, val string) { output.Context.ResponseWriter.Header().Set(key, val) @@ -55,7 +61,7 @@ func (output *BeegoOutput) Body(content []byte) { var encoding string var buf = &bytes.Buffer{} if output.EnableGzip { - encoding = ParseEncoding(output.Context.Input.Request) + encoding = ParseEncoding(output.Context.Request) } if b, n, _ := WriteBody(encoding, buf, content); b { output.Header("Content-Encoding", n) diff --git a/docs.go b/docs.go index 5031dc12..0e0f13f7 100644 --- a/docs.go +++ b/docs.go @@ -21,13 +21,7 @@ import ( ) // GlobalDocAPI store the swagger api documents -var GlobalDocAPI map[string]interface{} - -func init() { - if BConfig.WebConfig.EnableDocs { - GlobalDocAPI = make(map[string]interface{}) - } -} +var GlobalDocAPI = make(map[string]interface{}) func serverDocs(ctx *context.Context) { var obj interface{} diff --git a/router.go b/router.go index 28d641d0..a575f9ed 100644 --- a/router.go +++ b/router.go @@ -24,6 +24,7 @@ import ( "runtime" "strconv" "strings" + "sync" "time" beecontext "github.com/astaxie/beego/context" @@ -83,7 +84,7 @@ type logFilter struct { } func (l *logFilter) Filter(ctx *beecontext.Context) bool { - requestPath := path.Clean(ctx.Input.Request.URL.Path) + requestPath := path.Clean(ctx.Request.URL.Path) if requestPath == "/favicon.ico" || requestPath == "/robots.txt" { return true } @@ -114,14 +115,19 @@ type ControllerRegister struct { routers map[string]*Tree enableFilter bool filters map[int][]*FilterRouter + pool sync.Pool } // NewControllerRegister returns a new ControllerRegister. func NewControllerRegister() *ControllerRegister { - return &ControllerRegister{ + cr := &ControllerRegister{ routers: make(map[string]*Tree), filters: make(map[int][]*FilterRouter), } + cr.pool.New = func() interface{} { + return beecontext.NewContext() + } + return cr } // Add controller handler and pattern rules to ControllerRegister. @@ -132,7 +138,7 @@ func NewControllerRegister() *ControllerRegister { // Add("/api/create",&RestController{},"post:CreateFood") // Add("/api/update",&RestController{},"put:UpdateFood") // Add("/api/delete",&RestController{},"delete:DeleteFood") -// Add("/api",&RestController{},"get,post:ApiFunc") +// Add("/api",&RestController{},"get,post:ApiFunc" // Add("/simple",&SimpleController{},"get:GetFunc;post:PostFunc") func (p *ControllerRegister) Add(pattern string, c ControllerInterface, mappingMethods ...string) { reflectVal := reflect.ValueOf(c) @@ -573,26 +579,18 @@ func (p *ControllerRegister) geturl(t *Tree, url, controllName, methodName strin // Implement http.Handler interface. func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) { starttime := time.Now() - var runrouter reflect.Type - var findrouter bool - var runMethod string - var routerInfo *controllerInfo - - w := &responseWriter{rw, false, 0} - + var ( + runrouter reflect.Type + findrouter bool + runMethod string + routerInfo *controllerInfo + w = &responseWriter{rw, false, 0} + ) if BConfig.RunMode == "dev" { w.Header().Set("Server", BConfig.ServerName) } - - // init context - context := &beecontext.Context{ - ResponseWriter: w, - Request: r, - Input: beecontext.NewInput(r), - Output: beecontext.NewOutput(), - } - context.Output.Context = context - context.Output.EnableGzip = BConfig.EnableGzip + context := p.pool.Get().(*beecontext.Context) + context.Reset(w, r) defer p.recoverPanic(context) @@ -670,23 +668,14 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) goto Admin } - if context.Input.RunController != nil && context.Input.RunMethod != "" { - findrouter = true - runMethod = context.Input.RunMethod - runrouter = context.Input.RunController - } - if !findrouter { httpMethod := r.Method - if httpMethod == "POST" && context.Input.Query("_method") == "PUT" { httpMethod = "PUT" } - if httpMethod == "POST" && context.Input.Query("_method") == "DELETE" { httpMethod = "DELETE" } - if t, ok := p.routers[httpMethod]; ok { runObject, p := t.Match(urlPath) if r, ok := runObject.(*controllerInfo); ok { @@ -897,7 +886,6 @@ type responseWriter struct { status int } - // Write writes the data to the connection as part of an HTTP reply, // and sets `started` to true. // started means the response has sent out. diff --git a/staticfile.go b/staticfile.go index 031667ed..f3ea59a9 100644 --- a/staticfile.go +++ b/staticfile.go @@ -144,7 +144,7 @@ func isStaticCompress(filePath string) bool { // searchFile search the file by url path // if none the static file prefix matches ,return notStaticRequestErr func searchFile(ctx *context.Context) (string, os.FileInfo, error) { - requestPath := filepath.ToSlash(filepath.Clean(ctx.Input.Request.URL.Path)) + requestPath := filepath.ToSlash(filepath.Clean(ctx.Request.URL.Path)) // special processing : favicon.ico/robots.txt can be in any static dir if requestPath == "/favicon.ico" || requestPath == "/robots.txt" { file := path.Join(".", requestPath)