// Implement http.Handler interface. func (p *ControllerRegistor) 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{writer: rw} if RunMode == "dev" { w.Header().Set("Server", BeegoServerName) } // init context context := &beecontext.Context{ ResponseWriter: w, Request: r, Input: beecontext.NewInput(r), Output: beecontext.NewOutput(), } context.Output.Context = context context.Output.EnableGzip = EnableGzip defer p.recoverPanic(context) var urlPath string if !RouterCaseSensitive { urlPath = strings.ToLower(r.URL.Path) } else { urlPath = r.URL.Path } // defined filter function do_filter := func(pos int) (started bool) { if p.enableFilter { if l, ok := p.filters[pos]; ok { for _, filterR := range l { if filterR.returnOnOutput && w.started { return true } if ok, params := filterR.ValidRouter(urlPath); ok { for k, v := range params { if context.Input.Params == nil { context.Input.Params = make(map[string]string) } context.Input.Params[k] = v } filterR.filterFunc(context) } if filterR.returnOnOutput && w.started { return true } } } } return false } // filter wrong httpmethod if _, ok := HTTPMETHOD[r.Method]; !ok { http.Error(w, "Method Not Allowed", 405) goto Admin } // session init if SessionOn { var err error context.Input.CruSession, err = GlobalSessions.SessionStart(w, r) if err != nil { Error(err) exception("503", context) return } defer func() { context.Input.CruSession.SessionRelease(w) }() } if r.Method != "GET" && r.Method != "HEAD" { if CopyRequestBody && !context.Input.IsUpload() { context.Input.CopyBody() } context.Input.ParseFormOrMulitForm(MaxMemory) } if do_filter(BeforeRouter) { goto Admin } if context.Input.RunController != nil && context.Input.RunMethod != "" { findrouter = true runMethod = context.Input.RunMethod runrouter = context.Input.RunController } if !findrouter { http_method := r.Method if http_method == "POST" && context.Input.Query("_method") == "PUT" { http_method = "PUT" } if http_method == "POST" && context.Input.Query("_method") == "DELETE" { http_method = "DELETE" } if t, ok := p.routers[http_method]; ok { runObject, p := t.Match(urlPath) if r, ok := runObject.(*controllerInfo); ok { routerInfo = r findrouter = true if splat, ok := p[":splat"]; ok { splatlist := strings.Split(splat, "/") for k, v := range splatlist { p[strconv.Itoa(k)] = v } } if p != nil { context.Input.Params = p } } } } //if no matches to url, throw a not found exception if !findrouter { exception("404", context) goto Admin } if findrouter { //execute middleware filters if do_filter(BeforeExec) { goto Admin } isRunable := false if routerInfo != nil { if routerInfo.routerType == routerTypeRESTFul { if _, ok := routerInfo.methods[r.Method]; ok { isRunable = true routerInfo.runfunction(context) } else { exception("405", context) goto Admin } } else if routerInfo.routerType == routerTypeHandler { isRunable = true routerInfo.handler.ServeHTTP(rw, r) } else { runrouter = routerInfo.controllerType method := r.Method if r.Method == "POST" && context.Input.Query("_method") == "PUT" { method = "PUT" } if r.Method == "POST" && context.Input.Query("_method") == "DELETE" { method = "DELETE" } if m, ok := routerInfo.methods[method]; ok { runMethod = m } else if m, ok = routerInfo.methods["*"]; ok { runMethod = m } else { runMethod = method } } } // also defined runrouter & runMethod from filter if !isRunable { //Invoke the request handler vc := reflect.New(runrouter) execController, ok := vc.Interface().(ControllerInterface) if !ok { panic("controller is not ControllerInterface") } //call the controller init function execController.Init(context, runrouter.Name(), runMethod, vc.Interface()) //call prepare function execController.Prepare() //if XSRF is Enable then check cookie where there has any cookie in the request's cookie _csrf if EnableXSRF { execController.XsrfToken() if r.Method == "POST" || r.Method == "DELETE" || r.Method == "PUT" || (r.Method == "POST" && (context.Input.Query("_method") == "DELETE" || context.Input.Query("_method") == "PUT")) { execController.CheckXsrfCookie() } } execController.URLMapping() if !w.started { //exec main logic switch runMethod { case "GET": execController.Get() case "POST": execController.Post() case "DELETE": execController.Delete() case "PUT": execController.Put() case "HEAD": execController.Head() case "PATCH": execController.Patch() case "OPTIONS": execController.Options() default: if !execController.HandlerFunc(runMethod) { in := make([]reflect.Value, 0) method := vc.MethodByName(runMethod) method.Call(in) } } //render template if !w.started && context.Output.Status == 0 { if AutoRender { if err := execController.Render(); err != nil { panic(err) } } } } // finish all runrouter. release resource execController.Finish() } //execute middleware filters if do_filter(AfterExec) { goto Admin } } do_filter(FinishRouter) Admin: timeend := time.Since(starttime) if RunMode == "dev" || AccessLogs { var devinfo string if findrouter { if routerInfo != nil { devinfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s | % -40s |", r.Method, r.URL.Path, timeend.String(), "match", routerInfo.pattern) } else { devinfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s |", r.Method, r.URL.Path, timeend.String(), "match") } } else { devinfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s |", r.Method, r.URL.Path, timeend.String(), "notmatch") } if DefaultLogFilter == nil || !DefaultLogFilter.Filter(context) { Debug(devinfo) } } // Call WriteHeader if status code has been set changed if context.Output.Status != 0 { w.writer.WriteHeader(context.Output.Status) } }
// Implement http.Handler interface. func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) { defer func() { if err := recover(); err != nil { if err == USERSTOPRUN { return } if _, ok := err.(middleware.HTTPException); ok { // catch intented errors, only for HTTP 4XX and 5XX } else { if RunMode == "dev" { if !RecoverPanic { panic(err) } else { if ErrorsShow { if handler, ok := middleware.ErrorMaps[fmt.Sprint(err)]; ok { handler(rw, r) return } } var stack string Critical("the request url is ", r.URL.Path) Critical("Handler crashed with error", err) for i := 1; ; i++ { _, file, line, ok := runtime.Caller(i) if !ok { break } Critical(file, line) stack = stack + fmt.Sprintln(file, line) } middleware.ShowErr(err, rw, r, stack) } } else { if !RecoverPanic { panic(err) } else { // in production model show all infomation if ErrorsShow { handler := p.getErrorHandler(fmt.Sprint(err)) handler(rw, r) return } else { Critical("the request url is ", r.URL.Path) Critical("Handler crashed with error", err) for i := 1; ; i++ { _, file, line, ok := runtime.Caller(i) if !ok { break } Critical(file, line) } } } } } } }() starttime := time.Now() requestPath := r.URL.Path var runrouter reflect.Type var findrouter bool var runMethod string params := make(map[string]string) w := &responseWriter{writer: rw} w.Header().Set("Server", BeegoServerName) // init context context := &beecontext.Context{ ResponseWriter: w, Request: r, Input: beecontext.NewInput(r), Output: beecontext.NewOutput(), } context.Output.Context = context context.Output.EnableGzip = EnableGzip if context.Input.IsWebsocket() { context.ResponseWriter = rw } // defined filter function do_filter := func(pos int) (started bool) { if p.enableFilter { if l, ok := p.filters[pos]; ok { for _, filterR := range l { if ok, p := filterR.ValidRouter(r.URL.Path); ok { context.Input.Params = p filterR.filterFunc(context) if w.started { return true } } } } } return false } // session init if SessionOn { context.Input.CruSession = GlobalSessions.SessionStart(w, r) defer context.Input.CruSession.SessionRelease() } if !utils.InSlice(strings.ToLower(r.Method), HTTPMETHOD) { http.Error(w, "Method Not Allowed", 405) goto Admin } if do_filter(BeforeRouter) { goto Admin } //static file server for prefix, staticDir := range StaticDir { if r.URL.Path == "/favicon.ico" { file := staticDir + r.URL.Path http.ServeFile(w, r, file) w.started = true goto Admin } if strings.HasPrefix(r.URL.Path, prefix) { file := staticDir + r.URL.Path[len(prefix):] finfo, err := os.Stat(file) if err != nil { if RunMode == "dev" { Warn(err) } http.NotFound(w, r) goto Admin } //if the request is dir and DirectoryIndex is false then if finfo.IsDir() && !DirectoryIndex { middleware.Exception("403", rw, r, "403 Forbidden") goto Admin } //This block obtained from (https://github.com/smithfox/beego) - it should probably get merged into astaxie/beego after a pull request isStaticFileToCompress := false if StaticExtensionsToGzip != nil && len(StaticExtensionsToGzip) > 0 { for _, statExtension := range StaticExtensionsToGzip { if strings.HasSuffix(strings.ToLower(file), strings.ToLower(statExtension)) { isStaticFileToCompress = true break } } } if isStaticFileToCompress { if EnableGzip { w.contentEncoding = GetAcceptEncodingZip(r) } memzipfile, err := OpenMemZipFile(file, w.contentEncoding) if err != nil { return } w.InitHeadContent(finfo.Size()) http.ServeContent(w, r, file, finfo.ModTime(), memzipfile) } else { http.ServeFile(w, r, file) } w.started = true goto Admin } } if do_filter(AfterStatic) { goto Admin } if CopyRequestBody { context.Input.Body() } //first find path from the fixrouters to Improve Performance for _, route := range p.fixrouters { n := len(requestPath) if requestPath == route.pattern { runMethod = p.getRunMethod(r.Method, context, route) if runMethod != "" { runrouter = route.controllerType findrouter = true break } } // pattern /admin url /admin 200 /admin/ 200 // pattern /admin/ url /admin 301 /admin/ 200 if requestPath[n-1] != '/' && len(route.pattern) == n+1 && route.pattern[n] == '/' && route.pattern[:n] == requestPath { http.Redirect(w, r, requestPath+"/", 301) goto Admin } if requestPath[n-1] == '/' && n >= 2 && requestPath[:n-2] == route.pattern { runMethod = p.getRunMethod(r.Method, context, route) if runMethod != "" { runrouter = route.controllerType findrouter = true break } } } //find regex's router if !findrouter { //find a matching Route for _, route := range p.routers { //check if Route pattern matches url if !route.regex.MatchString(requestPath) { continue } //get submatches (params) matches := route.regex.FindStringSubmatch(requestPath) //double check that the Route matches the URL pattern. if len(matches[0]) != len(requestPath) { continue } if len(route.params) > 0 { //add url parameters to the query param map values := r.URL.Query() for i, match := range matches[1:] { values.Add(route.params[i], match) params[route.params[i]] = match } //reassemble query params and add to RawQuery r.URL.RawQuery = url.Values(values).Encode() } runMethod = p.getRunMethod(r.Method, context, route) if runMethod != "" { runrouter = route.controllerType context.Input.Params = params findrouter = true break } } } if !findrouter && p.enableAuto { // deal with url with diffirent ext // /controller/simple // /controller/simple.html // /controller/simple.json // /controller/simple.rss lastindex := strings.LastIndex(requestPath, "/") lastsub := requestPath[lastindex+1:] if subindex := strings.LastIndex(lastsub, "."); subindex != -1 { context.Input.Params[":ext"] = lastsub[subindex+1:] r.URL.Query().Add(":ext", lastsub[subindex+1:]) r.URL.RawQuery = r.URL.Query().Encode() requestPath = requestPath[:len(requestPath)-len(lastsub[subindex:])] } for cName, methodmap := range p.autoRouter { // if prev already find the router break if findrouter { break } if strings.ToLower(requestPath) == "/"+cName { http.Redirect(w, r, requestPath+"/", 301) goto Admin } // if there's no action, set the default action to index if strings.ToLower(requestPath) == "/"+cName+"/" { requestPath = requestPath + "index" } // if the request path start with controllerName if strings.HasPrefix(strings.ToLower(requestPath), "/"+cName+"/") { for mName, controllerType := range methodmap { if strings.ToLower(requestPath) == "/"+cName+"/"+strings.ToLower(mName) || (strings.HasPrefix(strings.ToLower(requestPath), "/"+cName+"/"+strings.ToLower(mName)) && requestPath[len("/"+cName+"/"+strings.ToLower(mName)):len("/"+cName+"/"+strings.ToLower(mName))+1] == "/") { runrouter = controllerType runMethod = mName findrouter = true //parse params otherurl := requestPath[len("/"+cName+"/"+strings.ToLower(mName)):] if len(otherurl) > 1 { plist := strings.Split(otherurl, "/") for k, v := range plist[1:] { context.Input.Params[strconv.Itoa(k)] = v } } break } } } } } //if no matches to url, throw a not found exception if !findrouter { middleware.Exception("404", rw, r, "") goto Admin } if findrouter { if r.Method == "POST" { r.ParseMultipartForm(MaxMemory) } //execute middleware filters if do_filter(BeforeExec) { goto Admin } //Invoke the request handler vc := reflect.New(runrouter) execController, ok := vc.Interface().(ControllerInterface) if !ok { panic("controller is not ControllerInterface") } //call the controller init function execController.Init(context, runrouter.Name(), runMethod, vc.Interface()) //if XSRF is Enable then check cookie where there has any cookie in the request's cookie _csrf if EnableXSRF { execController.XsrfToken() if r.Method == "POST" || r.Method == "DELETE" || r.Method == "PUT" || (r.Method == "POST" && (r.Form.Get("_method") == "delete" || r.Form.Get("_method") == "put")) { execController.CheckXsrfCookie() } } //call prepare function execController.Prepare() if !w.started { //exec main logic switch runMethod { case "Get": execController.Get() case "Post": execController.Post() case "Delete": execController.Delete() case "Put": execController.Put() case "Head": execController.Head() case "Patch": execController.Patch() case "Options": execController.Options() default: in := make([]reflect.Value, 0) method := vc.MethodByName(runMethod) method.Call(in) } //render template if !w.started && !context.Input.IsWebsocket() { if AutoRender { if err := execController.Render(); err != nil { panic(err) } } } } // finish all runrouter. release resource execController.Finish() //execute middleware filters if do_filter(AfterExec) { goto Admin } } Admin: do_filter(FinishRouter) //admin module record QPS if EnableAdmin { timeend := time.Since(starttime) if FilterMonitorFunc(r.Method, requestPath, timeend) { if runrouter != nil { go toolbox.StatisticsMap.AddStatistics(r.Method, requestPath, runrouter.Name(), timeend) } else { go toolbox.StatisticsMap.AddStatistics(r.Method, requestPath, "", timeend) } } } }