前篇
net/http 包,作为 golang 最核心的一个标准库,核心知识点较多,所以咱们大致拆分为三部分:
- 原理篇
- Client 服务端篇
- Server 客户端篇
回顾:什么是 HTTP ?
超文本传输协议(Hypertext Transfer Protocol、HTTP 协议)是今天使用最广泛的应用层协议,1989 年由 Tim Berners-Lee 在 CERN 起草的协议已经成为了互联网的数据传输的核心。
在过去几年的时间里,HTTP/2 和 HTTP/3 也对现有的协议进行了更新,提供更加安全和快速的传输功能。
多数的编程语言都会在标准库中实现 HTTP/1.1 和 HTTP/2.0 已满足工程师的日常开发需求,今天要介绍的 Go 语言的网络库也实现了这两个大版本的 HTTP 协议。
HTTP 设计原理
HTTP 协议是应用层协议,在通常情况下我们都会使用 TCP 作为底层的传输层协议传输数据包,但是 HTTP/3 在 UDP 协议上实现了新的传输层协议 QUIC 并使用 QUIC 传输数据,这也意味着 HTTP 既可以跑在 TCP 上,也可以跑在 UDP 上。
HTTP 与传输层协议
Go 语言标准库通过 net/http
包提供 HTTP 的客户端和服务端实现,在分析内部的实现原理之前,我们先来了解一下 HTTP 协议相关的一些设计以及标准库内部的层级结构和模块之间的关系。
HTTP 协议中最常见的概念是 HTTP 请求与响应,我们可以将它们理解成客户端和服务端之间传递的消息,客户端向服务端发送 HTTP 请求,服务端收到 HTTP 请求后会做出计算后以 HTTP 响应的形式发送给客户端。
HTTP 实现的接口
http.RoundTripper
用来表示执行单个 HTTP 请求的接口,调用方将请求作为参数可以获取请求对应的响应
go
// RoundTripper是一个接口,表示执行单个HTTP事务的能力,从而获得给定请求的响应。
//
// RoundTripper对于多个goroutine的并发使用必须是安全的。
type RoundTripper interface {
// RoundTrip 执行单个HTTP事务,返回对所提供请求的响应.
//
// RoundTrip 不应试图解释响应。
// 特别是,如果RoundTrip 获得响应,则无论响应的 HTTP 状态代码如何,都必须返回 err == nil。
// 应为未能获得响应保留非零错误。
// 同样,RoundTrip 不应试图处理更高级别的协议细节,如重定向、身份验证或 cookie 。
//
// RoundTrip 不应修改请求,除非消耗和关闭请求的正文。
// RoundTrip 可以在单独的 goroutine 中读取请求的字段。
// 在关闭响应的正文之前,调用者不应更改或重复使用请求。
// RoundTrip 必须始终关闭主体,
// 包括出现错误时,但根据实现情况,即使在 RoundTrip 返回后,也可以在单独的 goroutine 中关闭主体。
// 这意味着,想要在后续请求中重用正文的调用者必须安排等待 Close 调用,然后才能这样做。
//
// 必须初始化请求的URL和标题字段。
RoundTrip(*Request) (*Response, error)
}
net/http.Handler
用于 HTTP 服务器响应客户端的请求
go
// 处理程序响应HTTP请求。
//
// ServeHTTP 应该将回复头和数据写入 ResponseWriter,然后返回请求已完成的信号。
// 在完成 ServeHTTP 调用之后或同时使用 ResponseWriter 或从 Request.Body 读取是无效的。
//
// 根据 HTTP 客户端软件、HTTP 协议版本以及客户端和 Go 服务器之间的任何中介
// 在写入 ResponseWriter 后可能无法读取 Request.Body。
// 谨慎的处理程序应该先阅读 Request.Body,然后再回复。
//
// 除了读取正文之外,处理程序不应修改所提供的 Request。
//
// 如果 ServeHTTP 死机,服务器( ServeHTTP 的调用方)会认为死机的影响与活动请求无关。
// 它恢复死机,将堆栈跟踪记录到服务器错误日志中,并根据HTTP协议关闭网络连接或发送 HTTP/2 RST_STREAM。
// 要中止处理程序,以便客户端看到中断的响应,但服务器没有记录错误,请使用值 ErrArtrtHandler 进行死机。
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
-
net/http.ResponseWriter
用于构造 HTTP 响应
go
// A ResponseWriter interface is used by an HTTP handler to
// construct an HTTP response.
//
// A ResponseWriter may not be used after the Handler.ServeHTTP method
// has returned.
type ResponseWriter interface {
// Header returns the header map that will be sent by
// WriteHeader. The Header map also is the mechanism with which
// Handlers can set HTTP trailers.
//
// Changing the header map after a call to WriteHeader (or
// Write) has no effect unless the HTTP status code was of the
// 1xx class or the modified headers are trailers.
//
// There are two ways to set Trailers. The preferred way is to
// predeclare in the headers which trailers you will later
// send by setting the "Trailer" header to the names of the
// trailer keys which will come later. In this case, those
// keys of the Header map are treated as if they were
// trailers. See the example. The second way, for trailer
// keys not known to the Handler until after the first Write,
// is to prefix the Header map keys with the TrailerPrefix
// constant value. See TrailerPrefix.
//
// To suppress automatic response headers (such as "Date"), set
// their value to nil.
Header() Header
// Write writes the data to the connection as part of an HTTP reply.
//
// If WriteHeader has not yet been called, Write calls
// WriteHeader(http.StatusOK) before writing the data. If the Header
// does not contain a Content-Type line, Write adds a Content-Type set
// to the result of passing the initial 512 bytes of written data to
// DetectContentType. Additionally, if the total size of all written
// data is under a few KB and there are no Flush calls, the
// Content-Length header is added automatically.
//
// Depending on the HTTP protocol version and the client, calling
// Write or WriteHeader may prevent future reads on the
// Request.Body. For HTTP/1.x requests, handlers should read any
// needed request body data before writing the response. Once the
// headers have been flushed (due to either an explicit Flusher.Flush
// call or writing enough data to trigger a flush), the request body
// may be unavailable. For HTTP/2 requests, the Go HTTP server permits
// handlers to continue to read the request body while concurrently
// writing the response. However, such behavior may not be supported
// by all HTTP/2 clients. Handlers should read before writing if
// possible to maximize compatibility.
Write([]byte) (int, error)
// WriteHeader sends an HTTP response header with the provided
// status code.
//
// If WriteHeader is not called explicitly, the first call to Write
// will trigger an implicit WriteHeader(http.StatusOK).
// Thus explicit calls to WriteHeader are mainly used to
// send error codes or 1xx informational responses.
//
// The provided code must be a valid HTTP 1xx-5xx status code.
// Any number of 1xx headers may be written, followed by at most
// one 2xx-5xx header. 1xx headers are sent immediately, but 2xx-5xx
// headers may be buffered. Use the Flusher interface to send
// buffered data. The header map is cleared when 2xx-5xx headers are
// sent, but not with 1xx headers.
//
// The server will automatically send a 100 (Continue) header
// on the first read from the request body if the request has
// an "Expect: 100-continue" header.
WriteHeader(statusCode int)
}
它提供的三个接口
Header
、Write
和WriteHeader
分别会获取 HTTP 响应、将数据写入负载以及写入响应头
结束语
主要讲解了 HTTP 的定义,传输协议,设计原理,net/http 两大核心接口。