likes
comments
collection
share

「GO标准库」net/http 包的全面解析 :初识

作者站长头像
站长
· 阅读数 61

前篇

net/http 包,作为 golang 最核心的一个标准库,核心知识点较多,所以咱们大致拆分为三部分:

  1. 原理篇
  2. Client 服务端篇
  3. 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 上。

「GO标准库」net/http 包的全面解析 :初识

HTTP 与传输层协议

Go 语言标准库通过 net/http 包提供 HTTP 的客户端和服务端实现,在分析内部的实现原理之前,我们先来了解一下 HTTP 协议相关的一些设计以及标准库内部的层级结构和模块之间的关系。

HTTP 协议中最常见的概念是 HTTP 请求与响应,我们可以将它们理解成客户端和服务端之间传递的消息,客户端向服务端发送 HTTP 请求,服务端收到 HTTP 请求后会做出计算后以 HTTP 响应的形式发送给客户端。

「GO标准库」net/http 包的全面解析 :初识

HTTP 实现的接口

  • http.RoundTripper 用来表示执行单个 HTTP 请求的接口,调用方将请求作为参数可以获取请求对应的响应
// 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)
}

「GO标准库」net/http 包的全面解析 :初识

// 处理程序响应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)
}
// HTTP处理程序使用ResponseWriter接口来构造HTTP响应。
//
// 在Handler.ServeHTTP方法返回后,不能使用ResponseWriter。
type ResponseWriter interface {
    // Header返回将由WriteHeader发送的标头映射。Header映射也是Handlers设置HTTP尾部的机制。
    //
    // 在调用WriteHeader(或Write)后更改标头映射无效,除非HTTP状态代码是1xx类或修改后的标头是尾部。
    // 设置Trailers有两种方法。
    //
    // 首选方法是在标题中预先声明您稍后将发送的拖车,方法是将“拖车”标题设置为稍后将要发送的拖车钥匙的名称。
    // 在这种情况下,Header映射的那些键将被视为预告片。请参见示例。
    //
    // 第二种方法是,对于处理程序在第一次写入之后才知道的尾部关键字,使用TrailerPrefix常数值为Header映射关键字加前缀。请参见尾部前缀。
    //
    //要抑制自动响应标头(如“Date”),请将其值设置为nil。
    Header() Header

    // Write将数据写入连接,作为HTTP回复的一部分。
    //
    // 如果尚未调用WriteHeader,Write会在写入数据之前调用WriteHeader(http.StatusOK)。
    // 如果Header不包含Content-Type行,Write会将Content-Type集添加到将写入数据的初始512字节传递给DetectContentType的结果中。
    // 此外,如果所有写入数据的总大小小于几KB,并且没有Flush调用,则会自动添加Content-Length标头。
    //
    // 根据HTTP协议版本和客户端的不同,调用Write或WriteHeader可能会阻止以后对Request.Body的读取。
    // 对于HTTP/1.x请求,处理程序应在写入响应之前读取任何所需的请求正文数据。
    // 一旦头被刷新(由于显式Flusher.Flush调用或写入足够的数据以触发刷新),请求主体可能不可用。
    // 对于HTTP/2请求,Go HTTP服务器允许处理程序在同时写入响应的同时继续读取请求体。
    // 然而,并非所有HTTP/2客户端都支持这种行为。
    // 如果可能的话,处理程序应该先读后写,以最大限度地提高兼容性。
    Write([]byte) (int, error)

    // WriteHeader发送一个HTTP响应标头,其中包含所提供的状态代码。
    //
    // 如果没有显式调用WriteHeader,则对Write的第一次调用将触发隐式WriteHeader(http.StatusOK)。
    // 因此,对WriteHeader的显式调用主要用于发送错误代码或1xx信息响应。
    //
    // 提供的代码必须是有效的HTTP 1xx-5xx状态代码。
    // 可以写入任意数量的1xx标头,然后最多写入一个2xx-5xx标头。
    // 1xx报头被立即发送,但是2xx-5xx报头可以被缓冲。
    // 使用Flusher接口发送缓冲数据。发送2xx-5xx标头时会清除标头映射,但不使用1xx标头。
    //
    //如果请求具有“Expect:100 Continue”标头,则服务器将在第一次从请求正文读取时自动发送100(Continue)标头。
    WriteHeader(statusCode int)
}

它提供的三个接口 HeaderWrite 和 WriteHeader 分别会获取 HTTP 响应、将数据写入负载以及写入响应头

结束语

主要讲解了 HTTP 的定义,传输协议,设计原理,net/http 两大核心接口。

转载自:https://juejin.cn/post/7297035708430450722
评论
请登录