gin-net-http 2

目前知道gin是基于net/http进一步封装,来看下net/http是怎样写web的

package main

import (
  "fmt"
  "net/http"
)

func index(w http.ResponseWriter, r *http.Request) {
  fmt.Fprintln(w, "Hello World")
}

func main() {
  http.HandleFunc("/", index)
  http.ListenAndServe(":8080", nil)
}

首先,调用http.HandleFunc("/", index)注册路径处理函数,这里将路径/的处理函数设置为index。处理函数的类型必须是:、

func (http.ResponseWriter, *http.Request)

 其中*http.Request表示 HTTP 请求对象,该对象包含请求的所有信息,如 URL、首部、表单内容、请求的其他内容等。

http.ResponseWriter是一个接口类型:

// net/http/server.go
type ResponseWriter interface {
  Header() Header
  Write([]byte) (int, error)
  WriteHeader(statusCode int)
}

用于向客户端发送响应,实现了ResponseWriter接口的类型显然也实现了io.Writer接口。所以在处理函数index中,可以调用fmt.Fprintln()ResponseWriter写入响应信息。

net/http包中HandleFunc()函数的源码:

// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}

接调用了一个名为DefaultServeMux对象的HandleFunc()方法。DefaultServeMuxServeMux类型的实例:


type ServeMux struct {
	mu    sync.RWMutex
	m     map[string]muxEntry
	es    []muxEntry // slice of entries sorted from longest to shortest.
	hosts bool       // whether any patterns contain hostnames
}
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux

像这种提供默认类型实例的用法在 Go 语言的各个库中非常常见,在默认参数就已经足够的场景中使用默认实现很方便ServeMux保存了注册的所有路径和处理函数的对应关系。ServeMux.HandleFunc()方法如下:

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
  mux.Handle(pattern, HandlerFunc(handler))
}

这里将处理函数handler转为HandlerFunc类型,然后调用ServeMux.Handle()方法注册。注意这里的HandlerFunc(handler)是类型转换,而非函数调用,类型HandlerFunc的定义如下:

type HandlerFunc func(ResponseWriter, *Request)

func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
  f(w, r)
}

HandlerFunc实际上是以函数类型func(ResponseWriter, *Request)为底层类型,为HandlerFunc类型定义了方法ServeHTTP

mux.Handle(pattern, HandlerFunc(handler)) 调用了func (mux *ServeMux) Handle(pattern string, handler Handler)

// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (mux *ServeMux) Handle(pattern string, handler Handler) {
	mux.mu.Lock()
	defer mux.mu.Unlock()
	if _, exist := mux.m[pattern]; exist {
		panic("http: multiple registrations for " + pattern)
	}

	if mux.m == nil {
		mux.m = make(map[string]muxEntry)
	}
	e := muxEntry{h: handler, pattern: pattern}
	mux.m[pattern] = e
	if pattern[len(pattern)-1] == '/' {
		mux.es = appendSorted(mux.es, e)
	}
	if pattern[0] != '/' {
		mux.hosts = true
	}
}

也就是:

mux.Handle(pattern, HandlerFunc(handler))------>func (mux *ServeMux) Handle(pattern string, handler Handler)

HandlerFunc(handler)------>Handler

type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}

HandlerFunc类型只是为了方便注册函数类型的处理器

通过HandleFunc()注册的称为处理函数,将通过Handle()注册的称为处理器

注册了处理逻辑后,调用http.ListenAndServe(":8080", nil)监听本地计算机的 8080 端口,开始处理请求。下面看源码的处理:

func ListenAndServe(addr string, handler Handler) error {
  server := &Server{Addr: addr, Handler: handler}
  return server.ListenAndServe()
}

ListenAndServe创建了一个Server类型的对象:

调用此对象的ListenAndServe。里面先调用net.Listen()监听端口,将返回的net.Listener作为参数调用Server.Serve()方法:

Server.Serve()方法中,使用一个无限的for循环,不停地调用Listener.Accept()方法接受新连接,开启新 goroutine 处理新连接:

func (srv *Server) Serve(l net.Listener) error {
  var tempDelay time.Duration // how long to sleep on accept failure
  for {
    rw, err := l.Accept()
    if err != nil {
     -----------------
      return err
    }
    tempDelay = 0
    c := srv.newConn(rw)
    go c.serve(connCtx)
  }
}

func (c *conn) serve(ctx context.Context) {
    ----------------
  for {
      ---------------------
    w, err := c.readRequest(ctx)
    serverHandler{c.server}.ServeHTTP(w, w.req)
    w.finishRequest()
  }
}

 serve()方法其实就是不停地读取客户端发送地请求,创建serverHandler对象调用其ServeHTTP()方法去处理请求,然后做一些清理工作。serverHandler只是一个中间的辅助结构,代码如下:

type serverHandler struct {
  srv *Server
}

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
  handler := sh.srv.Handler
  if handler == nil {
    handler = DefaultServeMux
  }
  handler.ServeHTTP(rw, req)
}

 从Server对象中获取Handler,这个Handler就是调用http.ListenAndServe()时传入的第二个参数。在Hello World的示例代码中,我们传入了nil。所以这里handler会取默认值DefaultServeMux。调用DefaultServeMux.ServeHTTP()方法处理请求:

而对于gin框架 这个位置handler部位nil,此时调用注册进来的ServerHTTP

// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
	if r.RequestURI == "*" {
		if r.ProtoAtLeast(1, 1) {
			w.Header().Set("Connection", "close")
		}
		w.WriteHeader(StatusBadRequest)
		return
	}
	h, _ := mux.Handler(r)
	h.ServeHTTP(w, r)
}

 mux.Handler(r)通过请求的路径信息查找处理器,然后调用处理器的ServeHTTP()方法处理请求:

func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {

	--------
-------------------
	// All other requests have any port stripped and path cleaned
	// before passing to mux.handler.
	host := stripHostPort(r.Host)
	path := cleanPath(r.URL.Path)

	// If the given path is /tree and its handler is not registered,
	// redirect for /tree/.  如果则注册8080/hello; 请求8080/hello/ 则会匹配到8080/hello 然后返回301
	if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {
		return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
	}
    // 注意这个位置  如果则注册8080/hello/; 请求8080/hello 则会匹配到8080/hello/ 然后返回301
	if path != r.URL.Path { // 注意这个位置  如果则注册8080/hello; 请求8080/hello/ 则会匹配到8080/hello 然后返回301
		_, pattern = mux.handler(host, path)
		u := &url.URL{Path: path, RawQuery: r.URL.RawQuery}
		return RedirectHandler(u.String(), StatusMovedPermanently), pattern
	}

	return mux.handler(host, r.URL.Path)
}
// handler is the main implementation of Handler.
// The path is known to be in canonical form, except for CONNECT methods.
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
	mux.mu.RLock()
	defer mux.mu.RUnlock()

	// Host-specific pattern takes precedence over generic ones
	if mux.hosts {
		h, pattern = mux.match(host + path)
	}
	if h == nil {
		h, pattern = mux.match(path)
	}
	if h == nil {
		h, pattern = NotFoundHandler(), ""
	}
	return
}

 这是默认net-http的逻辑,那么gin呢。由于gin是基于net/http封装的?那么其回调函数是啥?

 

package main
import (
	"net/http"
	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
	r.GET("/", func(c *gin.Context) {
		c.String(200, "Hello!!!!")
	})
    r.Run()
}

 来看r.Run函数

// Run attaches the router to a http.Server and starts listening and serving HTTP requests.
// It is a shortcut for http.ListenAndServe(addr, router)
// Note: this method will block the calling goroutine indefinitely unless an error happens.
func (engine *Engine) Run(addr ...string) (err error) {
	defer func() { debugPrintError(err) }()
	address := resolveAddress(addr)
	debugPrint("Listening and serving HTTP on %s\n", address)
	err = http.ListenAndServe(address, engine.Handler())
	return
}

和net-http web使用中唯一区别就是: http.ListenAndServe(address, engine.Handler())  传入的第二个参数不一样

所以net-http会调用时gin 的handler.ServeHTTP(rw, req)函数也就是

http.ListenAndServe(address, engine.Handler())

func (engine *Engine) Handler() http.Handler {
	if !engine.UseH2C {
		return engine
	}

	h2s := &http2.Server{}
	return h2c.NewHandler(engine, h2s)
}

func NewHandler(h http.Handler, s *http2.Server) http.Handler {
	return &h2cHandler{
		Handler: h,
		s:       s,
	}
}
---->http.ListenAndServe(address, NewHandler())

 注册的是h2cHandler  mux, 所以调用的是h2cHandler的ServeHTTP

 

/ ServeHTTP implement the h2c support that is enabled by h2c.GetH2CHandler.
func (s h2cHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Handle h2c with prior knowledge (RFC 7540 Section 3.4)
	if r.Method == "PRI" && len(r.Header) == 0 && r.URL.Path == "*" && r.Proto == "HTTP/2.0" {
		if http2VerboseLogs {
			log.Print("h2c: attempting h2c with prior knowledge.")
		}
		conn, err := initH2CWithPriorKnowledge(w)
		if err != nil {
			if http2VerboseLogs {
				log.Printf("h2c: error h2c with prior knowledge: %v", err)
			}
			return
		}
		defer conn.Close()
		s.s.ServeConn(conn, &http2.ServeConnOpts{
			Context:          r.Context(),
			BaseConfig:       extractServer(r),
			Handler:          s.Handler,
			SawClientPreface: true,
		})
		return
	}
	// Handle Upgrade to h2c (RFC 7540 Section 3.2)
	if isH2CUpgrade(r.Header) {
		conn, settings, err := h2cUpgrade(w, r)
		if err != nil {
			if http2VerboseLogs {
				log.Printf("h2c: error h2c upgrade: %v", err)
			}
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		defer conn.Close()
		s.s.ServeConn(conn, &http2.ServeConnOpts{
			Context:        r.Context(),
			BaseConfig:     extractServer(r),
			Handler:        s.Handler,
			UpgradeRequest: r,
			Settings:       settings,
		})
		return
	}
    s.Handler.ServeHTTP(w, r) ----->Engine.ServerHTTP(w.r)
	return
}

 gin 中Engine的ServerHTTP

// ServeHTTP conforms to the http.Handler interface.
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	// 这里使用了对象池
	c := engine.pool.Get().(*Context)
	// Get对象后做初始化
	c.writermem.reset(w)
	c.Request = req
	c.reset()
	// 处理HTTP请求的函数
	engine.handleHTTPRequest(c)
	// 处理完请求后将对象放回池子
	engine.pool.Put(c)
}

 后面的逻辑见gin路由

对于HTTP2.0逻辑 如下:

conn, err := initH2CWithPriorKnowledge(w)
		if err != nil {
			if http2VerboseLogs {
				log.Printf("h2c: error h2c with prior knowledge: %v", err)
			}
			return
		}
		defer conn.Close()
		s.s.ServeConn(conn, &http2.ServeConnOpts{
			Context:          r.Context(),
			BaseConfig:       extractServer(r),
			Handler:          s.Handler,
			SawClientPreface: true,
		})

 

posted @   codestacklinuxer  阅读(47)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示