Gin源码分析-HTTP请求处理

410 阅读2分钟

Golang中的HTTP请求也是依赖Trees和net的Http库实现,Trees负责找到对应请求的调用连,ServeHttp负责触发请求,具体我们往下进一步分析。

下面是一个GoHTTP请求的简单的流程图,基本可以知道请求大概经过了哪些地方,由哪些模块进行处理了,接下来我将进一步详细的分析每个过程。

image.png

HTTP请求接收

go c.serve(connCtx) // 处理请求的入口,此处说明了所有请求都是独立协程

1.客户端请求到达Serve函数,通过c.serve发起独立请求,也就是说,go处理每个请求都是单独协程的,找到http请求过后,执行对应的HTTP服务。

serverHandler{c.server}.ServeHTTP(w, w.req) // 创建server处理器并执行触发ServeHTTP

2.获取对应的处理器,这里的处理器就是Gin之前实现的Engine,在New的时候创建的,具体的逻辑可以看我上一篇文章中最后所提到的,最后handler.ServeHTTP(rw, req),就前往了engine具体实现的ServeHTTP方法中去了。

  1. 拿到引擎请求的上下文。
  2. 给Engine对象的请求赋值。
  3. 执行handleHTTPRequest请求。
//  ServeHTTP 这里就是Gin实现的ServeHTTP方法(这里和我们上一篇文章提到的Engine实现ServeHTTP有关
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	c := engine.pool.Get().(*Context)
	c.writermem.reset(w)
	c.Request = req
	c.reset()
	
	engine.handleHTTPRequest(c)

	engine.pool.Put(c)
}

请求处理

handleHTTPRequest负责进行进一步的请求处理,此函数主要的工作有这么几个。

1.通过RequestMethod匹配到对应的handler,这个是从trees提取的,也就是之前New初始化的时候新建的Trees,这里面存了所有的请求handler。

2.匹配到handler之后执行Handler,然后判断链式调用是否结束,如果没有C.Next继续执行下一个handler。

func (engine *Engine) handleHTTPRequest(c *Context) {
	httpMethod := c.Request.Method  //获取请求方法
	rPath := c.Request.URL.Path // 获取请求路径
	unescape := false // 是否转义
	if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
		rPath = c.Request.URL.RawPath
		unescape = engine.UnescapePathValues
	}
         // 是否格式化多余的斜杠
	if engine.RemoveExtraSlash {
		rPath = cleanPath(rPath)
	}

	// 遍历树找到对应的调用handler链
	t := engine.trees
	for i, tl := 0, len(t); i < tl; i++ {
		if t[i].method != httpMethod {
			continue
		}
		root := t[i].root
		// 在树中找到对应的路由
		value := root.getValue(rPath, c.params, c.skippedNodes, unescape)
		if value.params != nil {
			c.Params = *value.params
		}
                // 处理路由
		if value.handlers != nil {
			// 找到对应的handler,并赋值
			c.handlers = value.handlers
			c.fullPath = value.fullPath
			c.Next()
			c.writermem.WriteHeaderNow()
			return
		}
		if httpMethod != http.MethodConnect && rPath != "/" {
			if value.tsr && engine.RedirectTrailingSlash {
				redirectTrailingSlash(c)
				return
			}
			if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) {
				return
			}
		}
		break
	}
	// 如果方法不允许
	if engine.HandleMethodNotAllowed {
		for _, tree := range engine.trees {
			if tree.method == httpMethod {
				continue
			}
			if value := tree.root.getValue(rPath, nil, c.skippedNodes, unescape); value.handlers != nil {
				c.handlers = engine.allNoMethod
				serveError(c, http.StatusMethodNotAllowed, default405Body)
				return
			}
		}
	}
	// 找到对应的handler,并赋值
	c.handlers = engine.allNoRoute
	// 跑出异常
	serveError(c, http.StatusNotFound, default404Body)
}