Skip to content

Latest commit

 

History

History
399 lines (355 loc) · 10.8 KB

handle-request.md

File metadata and controls

399 lines (355 loc) · 10.8 KB

请求处理主流程

接着 启动流程,来看看请求处理流程。

app.Handlers的类型为:*ControllerRegistor,其ServeHTTP方法,也即请求处理的入口,实现如下:

// Implement http.Handler interface.
func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
    // 请求处理开始时间
	startTime := time.Now()
	var (
		runRouter  reflect.Type
		findRouter bool
		runMethod  string
		routerInfo *controllerInfo
	)
	context := p.pool.Get().(*beecontext.Context)
	context.Reset(rw, r)
	defer p.pool.Put(context)
	// 处理出错时,交给recoverPanic来做错误处理,并响应
   // 还记得 **启动流程** 中的 registerDefaultErrorHandler() 吗?
	defer p.recoverPanic(context)

	context.Output.EnableGzip = BConfig.EnableGzip

	if BConfig.RunMode == DEV {
		context.Output.Header("Server", BConfig.ServerName)
	}

	var urlPath string
	if !BConfig.RouterCaseSensitive {
		urlPath = strings.ToLower(r.URL.Path)
	} else {
		urlPath = r.URL.Path
	}

	// filter wrong http method
	if _, ok := HTTPMETHOD[r.Method]; !ok {
		http.Error(rw, "Method Not Allowed", 405)
		goto Admin
	}

	// filter for static file
	if p.execFilter(context, BeforeStatic, urlPath) {
		goto Admin
	}

    // 尝试处理静态文件请求
	serverStaticRouter(context)
	// 当前请求的可能确实是个静态文件,所以在serverStaticRouter中已经做出了响应
	if context.ResponseWriter.Started {
	   // 那么以下也就不用再接着去查找非静态文件的路由了
		findRouter = true
		goto Admin
	}

	// session init
	if BConfig.WebConfig.Session.SessionOn {
		var err error
		context.Input.CruSession, err = GlobalSessions.SessionStart(rw, r)
		if err != nil {
			Error(err)
			exception("503", context)
			return
		}
		// 得记得释放呀
		defer func() {
			context.Input.CruSession.SessionRelease(rw)
		}()
	}

    // 解析请求体
	if r.Method != "GET" && r.Method != "HEAD" {
		if BConfig.CopyRequestBody && !context.Input.IsUpload() {
			context.Input.CopyBody(BConfig.MaxMemory)
		}
		context.Input.ParseFormOrMulitForm(BConfig.MaxMemory)
	}

	if p.execFilter(context, BeforeRouter, urlPath) {
		goto Admin
	}

	if !findRouter {
		httpMethod := r.Method
		if t, ok := p.routers[httpMethod]; ok {
		    // 如何匹配的?
			runObject := t.Match(urlPath, context)
			// 这里貌似很牛逼的样子
			if r, ok := runObject.(*controllerInfo); ok {
				routerInfo = r
				findRouter = true
				if splat := context.Input.Param(":splat"); splat != "" {
					for k, v := range strings.Split(splat, "/") {
						context.Input.SetParam(strconv.Itoa(k), v)
					}
				}
			}
		}

	}

	//if no matches to url, throw a not found exception
	if !findRouter {
		exception("404", context)
		goto Admin
	}

	if findRouter {
		//execute middleware filters
		if p.execFilter(context, BeforeExec, urlPath) {
			goto Admin
		}
		isRunnable := false
		if routerInfo != nil {
			if routerInfo.routerType == routerTypeRESTFul {
				if _, ok := routerInfo.methods[r.Method]; ok {
					isRunnable = true
					routerInfo.runFunction(context)
				} else {
					exception("405", context)
					goto Admin
				}
			} else if routerInfo.routerType == routerTypeHandler {
				isRunnable = true
				routerInfo.handler.ServeHTTP(rw, r)
			} else {
				runRouter = routerInfo.controllerType
				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 !isRunnable {
			//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 BConfig.WebConfig.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 !context.ResponseWriter.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) {
						var in []reflect.Value
						method := vc.MethodByName(runMethod)
						method.Call(in)
					}
				}

				//render template
				if !context.ResponseWriter.Started && context.Output.Status == 0 {
					if BConfig.WebConfig.AutoRender {
						if err := execController.Render(); err != nil {
							panic(err)
						}
					}
				}
			}

			// finish all runRouter. release resource
			execController.Finish()
		}

		//execute middleware filters
		if p.execFilter(context, AfterExec, urlPath) {
			goto Admin
		}
	}

	p.execFilter(context, FinishRouter, urlPath)

Admin:
	timeDur := time.Since(startTime)
	//admin module record QPS
	if BConfig.Listen.EnableAdmin {
		if FilterMonitorFunc(r.Method, r.URL.Path, timeDur) {
			if runRouter != nil {
				go toolbox.StatisticsMap.AddStatistics(r.Method, r.URL.Path, runRouter.Name(), timeDur)
			} else {
				go toolbox.StatisticsMap.AddStatistics(r.Method, r.URL.Path, "", timeDur)
			}
		}
	}

	if BConfig.RunMode == DEV || BConfig.Log.AccessLogs {
		var devInfo string
		if findRouter {
			if routerInfo != nil {
				devInfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s | % -40s |", r.Method, r.URL.Path, timeDur.String(), "match", routerInfo.pattern)
			} else {
				devInfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s |", r.Method, r.URL.Path, timeDur.String(), "match")
			}
		} else {
			devInfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s |", r.Method, r.URL.Path, timeDur.String(), "notmatch")
		}
		if DefaultAccessLogFilter == nil || !DefaultAccessLogFilter.Filter(context) {
			Debug(devInfo)
		}
	}

	// Call WriteHeader if status code has been set changed
	if context.Output.Status != 0 {
		context.ResponseWriter.WriteHeader(context.Output.Status)
	}
}

上述代码中使用了 responseWriter,其定义如下:

//responseWriter is a wrapper for the http.ResponseWriter
//started set to true if response was written to then don't execute other handler
type responseWriter struct {
	writer  http.ResponseWriter
	started bool
	status  int
}

静态文件请求处理函数 serverStaticRouter 定义在文件 staticfile.go中,实现如下所示:

func serverStaticRouter(ctx *context.Context) {
    // 非GET、HEAD请求不认为是静态文件请求
	if ctx.Input.Method() != "GET" && ctx.Input.Method() != "HEAD" {
		return
	}

	forbidden, filePath, fileInfo, err := lookupFile(ctx)
	if err == errNotStaticRequest {
		return
	}

	if forbidden {
		exception("403", ctx)
		return
	}

	if filePath == "" || fileInfo == nil {
		if BConfig.RunMode == DEV {
			Warn("Can't find/open the file:", filePath, err)
		}
		http.NotFound(ctx.ResponseWriter, ctx.Request)
		return
	}
	if fileInfo.IsDir() {
		//serveFile will list dir
		http.ServeFile(ctx.ResponseWriter, ctx.Request, filePath)
		return
	}

	var enableCompress = BConfig.EnableGzip && isStaticCompress(filePath)
	var acceptEncoding string
	if enableCompress {
		acceptEncoding = context.ParseEncoding(ctx.Request)
	}
	b, n, sch, err := openFile(filePath, fileInfo, acceptEncoding)
	if err != nil {
		if BConfig.RunMode == DEV {
			Warn("Can't compress the file:", filePath, err)
		}
		http.NotFound(ctx.ResponseWriter, ctx.Request)
		return
	}

	if b {
		ctx.Output.Header("Content-Encoding", n)
	} else {
		ctx.Output.Header("Content-Length", strconv.FormatInt(sch.size, 10))
	}

	http.ServeContent(ctx.ResponseWriter, ctx.Request, filePath, sch.modTime, sch)
	return
}

开启session的 context.Input.CruSession, err = GlobalSessions.SessionStart(w, r) 一行中的 GlobalSessions 想必还记得吧?在启动流程中有 GlobalSessions, err = session.NewManager(SessionProvider, sessionConfig) 一行。 GlobalSessions.SessionStart 方法实现如下所示:

// Start session. generate or read the session id from http request.
// if session id exists, return SessionStore with this id.
func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session SessionStore, err error) {
   // 看看请求头中是否有session id的cookie
	cookie, errs := r.Cookie(manager.config.CookieName)
	if errs != nil || cookie.Value == "" {
	   // 没有则新生成一个
		sid, errs := manager.sessionId(r)
		if errs != nil {
			return nil, errs
		}
		session, err = manager.provider.SessionRead(sid)
		cookie = &http.Cookie{
			Name:     manager.config.CookieName,
			Value:    url.QueryEscape(sid),
			Path:     "/",
			HttpOnly: true,
			Secure:   manager.isSecure(r),
			Domain:   manager.config.Domain,
		}
		if manager.config.CookieLifeTime > 0 {
			cookie.MaxAge = manager.config.CookieLifeTime
			cookie.Expires = time.Now().Add(time.Duration(manager.config.CookieLifeTime) * time.Second)
		}
		if manager.config.EnableSetCookie {
			http.SetCookie(w, cookie)
		}
		r.AddCookie(cookie)
	} else {
	   // 有,则取出目标session id,接着读取session数据
		sid, errs := url.QueryUnescape(cookie.Value)
		if errs != nil {
			return nil, errs
		}
		// 但可能目标session已经过期,不存在了,则也自动生成一个新的
		if manager.provider.SessionExist(sid) {
			session, err = manager.provider.SessionRead(sid)
		} else {
		   // 这代码拷贝得让我有些不爽呀
			sid, err = manager.sessionId(r)
			if err != nil {
				return nil, err
			}
			session, err = manager.provider.SessionRead(sid)
			cookie = &http.Cookie{
				Name:     manager.config.CookieName,
				Value:    url.QueryEscape(sid),
				Path:     "/",
				HttpOnly: true,
				Secure:   manager.isSecure(r),
				Domain:   manager.config.Domain,
			}
			if manager.config.CookieLifeTime > 0 {
				cookie.MaxAge = manager.config.CookieLifeTime
				cookie.Expires = time.Now().Add(time.Duration(manager.config.CookieLifeTime) * time.Second)
			}
			// 这是啥意思?
			if manager.config.EnableSetCookie {
				http.SetCookie(w, cookie)
			}
			r.AddCookie(cookie)
		}
	}
	return
}