golang中net/http源码剖析
net/http/server.go
首先,文件开头定义了一些错误变量,这些错误变量用于表示在处理HTTP请求和响应过程中可能出现的一些错误情况
go
var (
//表示当HTTP方法或响应状态码不允许有请求体时,ResponseWriter.Write调用会返回此错误
ErrBodyNotAllowed = errors.New("http: request method or response status code does not allow body")
//表示当底层连接已被Hijacker接口劫持时,调用ResponseWriter.Write会返回此错误
//在被劫持的连接上进行零字节写入将会返回ErrHijacked而不会有其他副作用
ErrHijacked = errors.New("http: connection has been hijacked")
//表示当处理程序设置了带有声明大小的Content-Length响应头,并尝试写入的字节数超过声明大小时,ResponseWriter.Write调用会返回此错误
ErrContentLength = errors.New("http: wrote more than the declared Content-Length")
//一个已废弃的错误变量,不再被net/http包中的任何内容返回
//调用者不应该将错误与此变量进行比较
ErrWriteAfterFlush = errors.New("unused")
)
Handler
go
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
Handler接口用于处理HTTP请求
Handler接口包含了一个ServeHTTP方法,该方法接收一个ResponseWriter和一个*Request作为参数。当HTTP服务器接收到请求时,会调用ServeHTTP方法来处理请求并生成响应
根据 http 请求 Request 中的请求路径 path 映射到对应的 handler 处理函数,对请求进行处理和响应
其中关于源码中的注释
- ServeHTTP方法的作用是写入响应头和数据到ResponseWriter,然后返回。返回信号表示请求处理完成;在ServeHTTP调用完成之后或并发地使用ResponseWriter或读取Request.Body是无效的。
- 由于HTTP客户端软件、HTTP协议版本以及客户端和Go服务器之间的中间件的不同,可能无法在向ResponseWriter写入数据之后从Request.Body中读取数据。因此,谨慎的处理程序应该先读取Request.Body,然后再进行响应。
- 除了读取请求体之外,处理程序不应修改提供的Request。
- 如果ServeHTTP方法发生panic(意外的运行时错误),服务器会假定panic的影响仅限于当前的请求。服务器会恢复panic,将堆栈跟踪记录到服务器错误日志,并关闭网络连接或者发送HTTP/2的RST_STREAM,具体取决于HTTP协议。如果要中断处理程序,以便客户端看到中断的响应但服务器不记录错误,可以panic并使用值ErrAbortHandler
ResponseWriter
ResponseWriter接口定义,用于处理HTTP响应的方法
go
type ResponseWriter interface {
//其中type Header map[string][]string 表示HTTP标头中的键值对
Header() Header
Write([]byte) (int, error)
WriteHeader(statusCode int)
}
WriteHeader(statusCode int)
WriteHeader(statusCode int)方法发送具有提供的状态码的HTTP响应头。如果没有显式调用WriteHeader,则对Write的第一次调用会触发隐式的WriteHeader(http.StatusOK)。因此,显式调用WriteHeader主要用于发送错误代码或1xx信息响应。提供的代码必须是有效的HTTP 1xx-5xx状态代码。可以写入任意数量的1xx头部,然后最多写入一个2xx-5xx头部。1xx头部会立即发送,但2xx-5xx头部可能会被缓冲。使用Flusher接口来发送缓冲的数据。在发送2xx-5xx头部时,头部映射会被清除,但在发送1xx头部时不会被清除。
Write([]byte) (int, error)
Write([]byte) (int, error)方法将数据作为HTTP响应的一部分写入到连接中。它返回写入的字节数和可能的错误。如果在调用WriteHeader之前尚未调用WriteHeader,则Write在写入数据之前会调用WriteHeader(http.StatusOK)。如果头部不包含Content-Type行,则Write会添加一个Content-Type行,该行设置为将前512个字节的写入数据传递给DetectContentType的结果。此外,如果所有写入数据的总大小不到几KB,并且没有调用Flush,则Content-Length头部会被自动添加。
Header()
Header()方法返回一个Header类型的对象,该对象表示将被WriteHeader发送的响应头。Header类型是一个映射,用于存储HTTP响应头的键值对。Header对象也是处理程序设置HTTP尾部的机制。如果在调用WriteHeader(或Write)之后修改了头部映射,则除非HTTP状态码属于1xx类或修改的头部是尾部,否则不会产生任何效果
这些方法和规范定义了ResponseWriter接口,该接口用于生成HTTP响应。这些规范非常重要,因为它们确保了HTTP服务器和处理程序之间的正确交互,以及在处理并发请求时的正确行为。
Server
最基本的结构体
go
type Server struct {
//表示服务器的网络地址,格式为host:port
Addr string
//表示处理HTTP请求的处理程序。它是一个接口类型,通常会使用http.Handler或http.HandlerFunc
Handler Handler
//一个布尔值,用于指示是否禁用通用选项处理程序
DisableGeneralOptionsHandler bool
//表示服务器的TLS配置,用于启用HTTPS
TLSConfig *tls.Config
//表示从接收请求开始到读取请求主体的超时时间
ReadTimeout time.Duration
//表示读取请求头的超时时间
ReadHeaderTimeout time.Duration
//表示写入响应的超时时间
WriteTimeout time.Duration
//表示空闲连接的超时时间
IdleTimeout time.Duration
//表示请求头的最大字节数
MaxHeaderBytes int
//表示用于处理TLS下一级协议的映射
TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
//表示连接状态变化时的回调函数
ConnState func(net.Conn, ConnState)
//表示用于记录错误日志的Logger
ErrorLog *log.Logger
//表示用于创建基础上下文的函数
BaseContext func(net.Listener) context.Context
//表示用于创建连接上下文的函数
ConnContext func(ctx context.Context, c net.Conn) context.Context
//表示服务器是否处于关闭状态的原子布尔值
inShutdown atomic.Bool
//表示是否禁用长连接的原子布尔值
//长连接是指在客户端和服务器之间建立的TCP连接,在这个连接上可以发送多个HTTP请求和响应
//相当于短连接是指在客户端和服务器之间建立的TCP连接只用于发送一个单独的HTTP请求和响应
disableKeepAlives atomic.Bool
//确保nextProtoErr只被设置一次的sync.Once对象
nextProtoOnce sync.Once
//表示下一级协议的错误
nextProtoErr error
//用于保护listeners和activeConn字段的互斥锁
mu sync.Mutex
//表示服务器的监听器
listeners map[*net.Listener]struct{}
//表示当前活动的连接
activeConn map[*conn]struct{}
//表示在服务器关闭时需要执行的函数列表
onShutdown []func()
//用于等待所有监听器都关闭的WaitGroup
listenerGroup sync.WaitGroup
}
ServeMux
ServeMux是一个HTTP请求多路复用器,它是net/http包中的一个结构体,用于匹配每个传入请求的URL,并根据注册的模式调用与URL最匹配的处理程序
ServeMux 是对 Handler 的具体实现,内部通过一个 map 维护了从 path 到 handler 的映射关系
go
type ServeMux struct {
//用于保护m和es字段的读写锁
mu sync.RWMutex
//用于将URL模式映射到对应的处理程序
//muxEntry结构体包含了实际的处理程序和一些其他信息
m map[string]muxEntry
es []muxEntry //从最长到最短排序的条目切片
hosts bool // 是否有任何模式包含主机名
}
ServeMux结构体的主要作用是根据注册的模式来选择处理程序,并根据请求的URL来调用匹配的处理程序。它还负责对URL请求路径和Host头进行清理,去除端口号,并将包含.或...元素或重复斜杠的请求重定向到等效的更干净的URL
muxEntry
muxEntry用于表示ServeMux中的一个条目,其中包含了URL模式和对应的处理程序
go
type muxEntry struct {
//表示一个处理程序,通常是实现了http.Handler接口的对象,用于处理与该URL模式匹配的请求
h Handler
//表示一个URL模式,用于匹配传入请求的URL
//这个URL模式可以包含通配符或者特定的路径,用于指定与之匹配的请求路径
pattern string
}
其中在ServeMux中,muxEntry结构体被用来表示每一个注册的URL模式和对应的处理程序,它们被存储在m和es字段中,用于在接收到请求时进行匹配和调度。muxEntry结构体是ServeMux实现请求路由功能的重要组成部分