http 包
介绍
http 包提供 HTTP/1.1,HTTP/2,WebSocket 协议的 server、client 端实现,关于协议的详细内容可参考 RFC 9110、9112、9113、9218、7541 等。
使用本包需要外部依赖 OpenSSL 3
的 ssl
和 crypto
动态库文件,故使用前需安装相关工具:
- 对于
Linux
操作系统,可参考以下方式:- 如果系统的包管理工具支持安装
OpenSSL 3
开发工具包,可通过这个方式安装,并确保系统安装目录下含有libssl.so
、libssl.so.3
、libcrypto.so
和libcrypto.so.3
这些动态库文件,例如Ubuntu 22.04
系统上可使用sudo apt install libssl-dev
命令安装libssl-dev
工具包; - 如果无法通过上面的方式安装,可自行下载
OpenSSL 3.x.x
源码编译安装软件包,并确保安装目录下含有libssl.so
、libssl.so.3
、libcrypto.so
和libcrypto.so.3
这些动态库文件,然后可选择下面任意一种方式来保证系统链接器可以找到这些文件:- 在系统未安装 OpenSSL 的场景,安装时选择直接安装到系统路径下;
- 安装在自定义目录的场景,将这些文件所在目录设置到环境变量
LD_LIBRARY_PATH
以及LIBRARY_PATH
中。
- 如果系统的包管理工具支持安装
- 对于
Windows
操作系统,可按照以下步骤:- 自行下载
OpenSSL 3.x.x
源码编译安装 x64 架构软件包或者自行下载安装第三方预编译的供开发人员使用的OpenSSL 3.x.x
软件包; - 确保安装目录下含有
libssl.dll.a
(或libssl.lib
)、libssl-3-x64.dll
、libcrypto.dll.a
(或libcrypto.lib
)、libcrypto-3-x64.dll
这些库文件; - 将
libssl.dll.a
(或libssl.lib
)、libcrypto.dll.a
(或libcrypto.lib
) 所在的目录路径设置到环境变量LIBRARY_PATH
中,将libssl-3-x64.dll
、libcrypto-3-x64.dll
所在的目录路径设置到环境变量PATH
中。
- 自行下载
- 对于
macOS
操作系统,可参考以下方式:- 使用
brew install openssl@3
安装,并确保系统安装目录下含有libcrypto.dylib
和libcrypto.3.dylib
这两个动态库文件; - 如果无法通过上面的方式安装,可自行下载
OpenSSL 3.x.x
源码编译安装软件包,并确保安装目录下含有libcrypto.dylib
和libcrypto.3.dylib
这两个动态库文件,然后可选择下面任意一种方式来保证系统链接器可以找到这些文件:- 在系统未安装 OpenSSL 的场景,安装时选择直接安装到系统路径下;
- 安装在自定义目录的场景,将这些文件所在目录设置到环境变量
DYLD_LIBRARY_PATH
以及LIBRARY_PATH
中。
- 使用
如果未安装OpenSSL 3
软件包或者安装低版本的软件包,程序可能无法使用并抛出相关异常 WebSocketException: Can not load openssl library or function xxx.
。
http
用户可以选择 http 协议的版本,如 HTTP/1.1、HTTP/2,http 包的多数 API 并不区分这两种协议版本,只有当用户用到某个版本的特有功能时,才需要做这种区分,如 HTTP/1.1 中的 chunked 的 transfer-encoding,HTTP/2 中的 server push。
http 库默认使用 HTTP/1.1 版本。当开发者需要使用 HTTP/2 协议时,需要为 Client/Server 配置 tls,并且设置 alpn 的值为 h2
;不支持 HTTP/1.1 通过 Upgrade: h2c
协议升级的方式升级到 HTTP/2。
如果创建 HTTP/2 连接握手失败,Client/Server 会自动将协议退回 HTTP/1.1。
用户通过 ClientBuilder 构建一个 Client 实例,构建过程可以指定多个参数,如 httpProxy、logger、cookieJar、是否自动 redirect、连接池大小等,详情参见 ClientBuilder。
用户通过 ServerBuilder 构建一个 Server 实例,构建过程可以指定多个参数,如 addr、port、logger、distributor 等,详情参见 ServerBuilder。
用户如果需要自己设置 Logger,需要保证它是线程安全的。
Client、Server 的大多数参数在构建后便不允许修改,如果想要更改,用户需要重新构建一个新的 Client 或 Server 实例;如果该参数支持动态修改,本实现会提供显式的功能,如 Server 端 cert、CA 的热更新。
通过 Client 实例,用户可以发送 http request、接收 http response。
通过 Server 实例,用户可以配置 request 转发处理器,启动 http server。在 server handler 中,用户可以通过 HttpContext 获取 client 发来的 request 的详细信息,构造发送给 client 的 response。 Server 端根据 Client 端请求,创建对应的 ProtocolService 实例,同一个 Server 实例可同时支持两种协议:HTTP/1.1、HTTP/2。
在 client 端,用户通过 HttpRequestBuilder 构造 request,构建过程可以指定多个参数,如 method、url、version、headers、body、trailers 等等;构建之后的request 不允许再进行修改。 在 server 端,用户通过 HttpResponseBuilder 构造 response,构建过程可以指定多个参数,如 status、headers、body、trailers 等等;构建之后的 response 不允许再进行修改。
另外,本实现提供一些工具类,方便用户构造一些常用 response,如 RedirectHandler 构造 redirect response,NotFoundHandler 构造 404 response。
WebSocket
本实现为 WebSocket 提供 sub-protocol 协商,基础的 frame 解码、读取、消息发送、frame 编码、ping、pong、关闭等功能。
用户通过 WebSocket.upgradeFromClient 从一个 HTTP/1.1 或 HTTP/2 Client 实例升级到 WebSocket 协议,之后通过返回的 WebSocket 实例进行 WebSocket 通讯。
用户在一个 server 端的 handler 中,通过 WebSocket.upgradeFromServer 从 HTTP/1.1 或 HTTP/2 协议升级到 WebSocket 协议,之后通过返回的 WebSocket 实例进行 WebSocket 通讯。
按照协议,HTTP/1.1 中,升级后的 WebSocket 连接是建立在 tcp 连接(https 则还会多一层 TLS)之上;HTTP/2 中,升级后的 WebSocket 连接是建立在 HTTP/2 connection 的一个 stream 之上。HTTP/1.1 中,close 最终会直接关闭 tcp 连接;HTTP/2 中,close 只会关闭 connection 上的一个 stream。
主要接口
class ServerBuilder
public class ServerBuilder
提供 Server 实例构建器。 支持通过如下参数构造一个 Http Server:
- 地址、端口;
- 线程安全的 logger;
- HttpRequestDistributor,用于注册 handler、分发 request;
- HTTP/2 的 settings;
- shutdown 回调;
- transport:listener、连接及其配置;
- protocol service:http 协议解析服务;
除地址端口、shutdown 回调外,均提供默认实现,用户在构造 server 过程中可不指定其他构建参数。 ServerBuilder 文档中未明确说明支持版本的配置,在 HTTP/1.1 与 HTTP/2 都会生效
init
public init()
功能:创建 ServerBuilder 实例。
func addr
public func addr(addr: String): ServerBuilder
功能:设置服务端监听地址,若 listener 被设定,此值被忽略。
参数:
- addr:地址值,格式为 IP 或者 域名
返回值:当前 ServerBuilder 的引用
func port
public func port(port: UInt16): ServerBuilder
功能:设置服务端监听端口,若 listener 被设定,此值被忽略。
参数:
- port:端口值
返回值:当前 ServerBuilder 的引用
func listener
public func listener(listener: ServerSocket): ServerBuilder
功能:服务端调用此函数对指定 socket 进行绑定监听。
参数:
- listener:所绑定的socket
返回值:当前 ServerBuilder 的引用
func logger
public func logger(logger: Logger): ServerBuilder
功能:设定服务器的 logger,默认 logger 级别为 INFO,logger 内容将写入 Console.stdout。
参数:
- logger:需要是线程安全的,默认使用内置线程安全 logger
返回值:当前 ServerBuilder 的引用
func distributor
public func distributor(distributor: HttpRequestDistributor): ServerBuilder
功能:设置请求分发器,请求分发器会根据 url 将请求分发给对应的 handler不设置时使用默认请求分发器。
参数:
- distributor:自定义请求分发器实例
返回值:当前 ServerBuilder 的引用
func protocolServiceFactory
public func protocolServiceFactory(factory: ProtocolServiceFactory): ServerBuilder
功能:设置协议服务工厂,服务协议工厂会生成每个协议所需的服务实例,不设置时使用默认工厂。
参数:
- factory:自定义工厂实例
返回值:当前 ServerBuilder 的引用
func transportConfig
public func transportConfig(config: TransportConfig): ServerBuilder
功能:设置传输层配置,默认配置详见 TransportConfig
结构体说明。
参数:
- config:设定的传输层配置信息
返回值:当前 ServerBuilder 的引用
func tlsConfig
public func tlsConfig(config: TlsServerConfig): ServerBuilder
功能:设置 TLS 层配置,默认不对其进行设置。
参数:
- config:设定支持 tls 服务所需要的配置信息
返回值:当前 ServerBuilder 的引用
func readTimeout
public func readTimeout(timeout: Duration): ServerBuilder
功能:设定服务端读取一个请求的最大时长,超过该时长将不再进行读取并关闭连接,默认不进行限制。
参数:
- timeout:设定读请求的超时时间,如果传入时间为负值将被替换为 Duration.Zero
返回值:当前 ServerBuilder 的引用
func writeTimeout
public func writeTimeout(timeout: Duration): ServerBuilder
功能:设定服务端发送一个响应的最大时长,超过该时长将不再进行写入并关闭连接,默认不进行限制。
参数:
- timeout:设定写响应的超时时间,如果传入时间为负值将被替换为 Duration.Zero
返回值:当前 ServerBuilder 的引用
func readHeaderTimeout
public func readHeaderTimeout(timeout: Duration): ServerBuilder
功能:设定服务端读取客户端发送一个请求的请求头最大时长,超过该时长将不再进行读取并关闭连接,默认不进行限制。
参数:
- timeout:设定的读请求头超时时间,如果传入负的 Duration 将被替换为 Duration.Zero
返回值:
- ServerBuilder:当前 ServerBuilder 的引用
func httpKeepAliveTimeout
public func httpKeepAliveTimeout(timeout: Duration): ServerBuilder
功能:HTTP/1.1 专用,设定服务端连接保活时长,该时长内客户端未再次发送请求,服务端将关闭长连接,默认不进行限制。
参数:
- timeout:设定保持长连接的超时时间,如果传入负的 Duration 将被替换为 Duration.Zero
返回值:当前 ServerBuilder 的引用
func maxRequestHeaderSize
public func maxRequestHeaderSize(size: Int64): ServerBuilder
功能:设定服务端允许客户端发送单个请求的请求头部分最大值,请求头部分大小超过该值时,将返回状态码为 431 的响应仅对 HTTP/1.1 生效,HTTP/2 中有专门的配置 maxHeaderListSize,默认值为 8192。
参数:
- size:设定允许接收请求的请求头大小最大值,值为 0 代表不作限制
返回值:当前 ServerBuilder 的引用
异常:
- IllegalArgumentException:size < 0
func maxRequestBodySize
public func maxRequestBodySize(size: Int64): ServerBuilder
功能:设置服务端允许客户端发送单个请求的请求体部分最大值,请求体部分大小超过该值时,将返回状态码为 413 的响应仅对于 HTTP/1.1 且未设置 "Transfer-Encoding: chunked" 的请求生效,默认值为 2M。
参数:
- size:设定允许接收请求的请求体大小最大值,值为 0 代表不作限制
返回值:当前 ServerBuilder 的引用
异常:
- IllegalArgumentException:size < 0
func headerTableSize
public func headerTableSize(size: UInt32): ServerBuilder
功能:HTTP/2 专用,设置本端对响应头编码时使用的压缩表的最大大小,默认值为 4096。
参数:
- size:本端对响应头编码时使用的最大
table size
返回值:当前 ServerBuilder 的引用
func maxConcurrentStreams
public func maxConcurrentStreams(size: UInt32): ServerBuilder
功能:HTTP/2 专用,设置本端同时处理的最大请求数量,限制对端并发发送请求的数量,默认值为 100。
参数:
- size:本端同时处理的最大请求数量
返回值:当前 ServerBuilder 的引用
func initialWindowSize
public func initialWindowSize(size: UInt32): ServerBuilder
功能:HTTP/2 专用,设置本端一个 stream 上接收报文的初始流量窗口大小,默认值为 65535. 取值范围为 0 至 2^31 - 1。
参数:
- size:本端一个 stream 上接收报文的初始流量窗口大小
返回值:当前 ServerBuilder 的引用
func maxFrameSize
public func maxFrameSize(size: UInt32): ServerBuilder
功能:HTTP/2 专用,设置本端接收的一个帧的最大长度,用来限制对端发送帧的长度,默认值为 16384. 取值范围为 2^14 至 2^24 - 1。
参数:
- size:本端接收的一个帧的最大长度
返回值:当前 ServerBuilder 的引用
func maxHeaderListSize
public func maxHeaderListSize(size: UInt32): ServerBuilder
功能:HTTP/2 专用,设置本端接收的报文头最大长度,用来限制对端发送的报文头最大长度。报文头长度计算方法:所有 header field 长度之和(所有 name 长度 + value 长度 + 32,包括自动添加的 HTTP/2 伪头),默认值为 8192。
参数:
- size:本端接收的报文头最大长度
返回值:当前 ServerBuilder 的引用
func enableConnectProtocol
public func enableConnectProtocol(flag: Bool): ServerBuilder
功能:HTTP/2 专用,设置本端是否接收 CONNECT 请求,默认 false。
参数:
- flag:本端是否接收 CONNECT 请求
返回值:当前 ServerBuilder 的引用
func afterBind
public func afterBind(f: ()->Unit): ServerBuilder
功能:注册服务器启动时的回调函数,服务内部 ServerSocket 实例 bind 之后,accept 之前将调用该函数。重复调用将覆盖之前注册的函数。
参数:
- f:回调函数,入参为空,返回值为 Unit 类型
返回值:当前 ServerBuilder 的引用
func onShutdown
public func onShutdown(f: ()->Unit): ServerBuilder
功能:注册服务器关闭时的回调函数,服务器关闭时将调用该回调函数,重复调用将覆盖之前注册的函数。
参数:
- f:回调函数,入参为空,返回值为 Unit 类型
返回值:当前 ServerBuilder 的引用
func servicePoolConfig
public func servicePoolConfig(cfg: ServicePoolConfig): ServerBuilder
功能:服务过程中使用的协程池相关设置,具体说明见 servicePoolConfig 结构体。
参数:
- cfg:协程池相关设置。
返回值:当前 ServerBuilder 的引用
func build
public func build(): Server
功能:构建 Server 实例。
返回值:根据设置的属性生成的 Server 实例
异常:
- IllegalArgumentException:设置的参数非法
class Server
public class Server
提供 HTTP 服务的 Server 类包含如下功能:
- 启动服务,在指定地址及端口等待用户连接、服务用户的 http request;
- 关闭服务,包括关闭所有已有连接;
- 提供注册处理 http request 的 handler 的机制,根据注册信息分发 request 到相应的 handler;
- 提供 tls 证书热机制;
- 提供 shutdown 回调机制;
- 通过 Logger.level 开启、关闭日志打印,包括按照用户要求打印相应级别的日志;
- Server 文档中未明确说明支持版本的配置,在 HTTP/1.1 与 HTTP/2 都会生效
prop addr
public prop addr: String
功能:获取服务端监听地址,格式为 IP 或者 域名。
prop port
public prop port: UInt16
功能:获取服务端监听端口。
prop listener
public prop listener: ServerSocket
功能:获取服务器绑定 socket。
prop logger
public prop logger: Logger
功能:获取服务器日志记录器,设置 logger.level 将立即生效,记录器应该是线程安全的。
prop distributor
public prop distributor: HttpRequestDistributor
功能:获取请求分发器,请求分发器会根据 url 将请求分发给对应的 handler。
prop protocolServiceFactory
public prop protocolServiceFactory: ProtocolServiceFactory
功能:获取协议服务工厂,服务协议工厂会生成每个协议所需的服务实例。
prop transportConfig
public prop transportConfig: TransportConfig
功能:获取服务器设定的传输层配置。
func getTlsConfig
public func getTlsConfig(): ?TlsServerConfig
功能:获取服务器设定的 TLS 层配置。
返回值:
prop readTimeout
public prop readTimeout: Duration
功能:获取服务器设定的读取整个请求的超时时间。
prop writeTimeout
public prop writeTimeout: Duration
功能:获取服务器设定的写响应的超时时间。
prop readHeaderTimeout
public prop readHeaderTimeout: Duration
功能:获取服务器设定的读取请求头的超时时间。
prop httpKeepAliveTimeout
public prop httpKeepAliveTimeout: Duration
功能:HTTP/1.1 专用,获取服务器设定的保持长连接的超时时间。
prop maxRequestHeaderSize
public prop maxRequestHeaderSize: Int64
功能:获取服务器设定的读取请求的请求头最大值。仅对 HTTP/1.1 生效,HTTP/2 中有专门的配置 maxHeaderListSize。
prop maxRequestBodySize
public prop maxRequestBodySize: Int64
功能:获取服务器设定的读取请求的请求体最大值,仅对于 HTTP/1.1 且未设置 "Transfer-Encoding: chunked" 的请求生效。
prop headerTableSize
public prop headerTableSize: UInt32
功能:HTTP/2 专用,用来限制对端发送的报文。
hpack encoder/decoder 的最大 table size
prop maxConcurrentStreams
public prop maxConcurrentStreams: UInt32
功能:HTTP/2 专用,用来限制对端发送的报文。
同时处理的最大请求数量
prop initialWindowSize
public prop initialWindowSize: UInt32
功能:HTTP/2 专用,用来限制对端发送的报文stream 初始流量窗口大小。默认值为 65535 ,取值范围为 0 至 2^31 - 1。
prop maxFrameSize
public prop maxFrameSize: UInt32
功能:HTTP/2 专用,用来限制对端发送的报文一个帧的最大长度。默认值为 16384. 取值范围为 2^14 至 2^24 - 1。
prop maxHeaderListSize
public prop maxHeaderListSize: UInt32
功能:HTTP/2 专用,用来限制对端发送的报文请求 headers 最大长度,为所有 header field 长度之和(所有 name 长度 + value 长度 + 32,包括自动添加的 h2 伪头),默认值为 UInt32.Max。
prop enableConnectProtocol
public prop enableConnectProtocol: Bool
功能:HTTP/2 专用,用来限制对端发送的报文是否支持通过 connect 方法升级协议,true 表示支持。
prop servicePoolConfig
public prop servicePoolConfig: ServicePoolConfig
功能:获取协程池配置实例。
func serve
public func serve(): Unit
功能:启动服务端进程不支持重复启动h1 request 检查和处理:
- request-line 不符合 rfc9112 中 request-line = method SP request-target SP HTTP-version 的规则,将会返回 400 响应
- method 由 tokens 组成,且大小写敏感,request-target 为能够被解析的 url,HTTP-version 为 HTTP/1.0 或 HTTP/1.1 否则将会返回 400 响应headers name 和 value 需符合特定规则,详见 HttpHeaders 类说明,否则返回 400 响应
- 当 headers 的大小超出 server 设定的 maxRequestHeaderSize 时将自动返回 431 响应
- headers 中必须包含 "host" 请求头,且值唯一,否则返回 400 响应headers 中不允许同时存在 "content-length" 与 "transfer-encoding" 请求头,否则返回 400 响应
- 请求头 "transfer-encoding" 的 value 经过 "," 分割后最后一个 value 必须为 "chunked",且之前的 value 不允许存在 "chunked",否则返回 400 响应
- 请求头 "content-length" 其 value 必须能解析为 Int64 类型,且不能为负值,否则返回 400 响应,当其 value 值超出 server 设定maxRequestBodySize,将返回 413 响应
- headers 中若不存在 "content-length" 和 "transfer-encoding: chunked" 时默认不存在 body
- 请求头 "trailer" 中,value 不允许存在 "transfer-encoding","trailer","content-length"
- 请求头 "expect" 中,value 中存在非 "100-continue" 的值,将会返回 417 响应
- HTTP/1.0 默认短连接,若想保持长连接需要包含请求头 "connection: keep-alive" 与 "keep-alive: timeout = XX, max = XX",将会自动保持 timeout 时长的连接。HTTP/1.1 默认长连接,当解析 request 失败则关闭连接
- 仅允许在 chunked 模式下存在 trailers,且 trailer 中条目的 name 必须被包含在 "trailers" 请求头中,否则将自动删除
h1 response 检查和处理:
- 若用户不对 response 进行配置,将会自动返回 200 响应
- 若接收到的 request 包含请求头 "connection: close" 而配置 response 未添加响应头 "connection" 或响应头 "connection" 的 value 不包含 "close",将自动添加 "connection: close",若接收到的 request 不包含请求头 "connection: close" 且响应头不存在 "connection: keep-alive",将会自动添加
- 如果 headers 包含逐跳响应头:"proxy-connection","keep-alive","te","transfer-encoding","upgrade",将会在响应头 "connection" 自动添加这些头作为 value
- 将自动添加 "date" field,用户提供的 "date" 将被忽略
- 若请求方法为 "HEAD" 或响应状态码为 "1XX\204\304" body将配置为空
- 若已知提供 body 的大小时,将会与响应头 "content-length" 进行比较,若不存在响应头 "content-length",将自动添加此响应头,其 value 值为 body 大小。若响应头 "content-length" 大小大于 body 大小将会在 handler 中抛出 HttpException,若小于 body 大小,将对 body 进行截断处理,发送的 body 大小将为 "content-length" 的值
- response 中 "set-cookie" header 将分条发送,其他 headers 同名条目将合成一条发送
- 在处理包含请求头:"Expect: 100-continue"的request时,在调用 request 的 body.read() 时将会自动发送状态码为100的响应给客户端。不允许用户主动发送状态码为100的response,若进行发送则被认定为服务器异常
启用 h2 服务:tlsConfig 中 supportedAlpnProtocols 需包含 "h2",此后如果 tls 层 alpn 协商结果为 h2,则启用 h2 服务
h2 request 检查和处理:
- headers name 和 value 需符合特定规则,详见 HttpHeaders 类说明,此外 name 不能包含大写字符,否则发送 RST 帧关闭流,即无法保证返回响应
- trailers name 和 value 需符合同样规则,否则关闭流
- headers 不能包含 "connection","transfer-encoding","keep-alive","upgrade","proxy-connection",否则关闭流
- 如果有 "te" header,其值只能为 "trailers",否则关闭流
- 如果有 "host" header 和 ":authority" pseudo header,"host" 值必须与 ":authority" 一致,否则关闭流
- 如果有 "content-length" header,需符合 "content-length" 每个值都能解析为 Int64 类型,且如果有多个值,必须相等,否则关闭流
- 如果有 "content-length" header,且有 body 大小,则 content-length 值与 body 大小必须相等,否则关闭流
- 如果有 "trailer" header,其值不能包含 "transfer-encoding","trailer","content-length",否则关闭流
- 仅在升级 WebSocket 场景下支持 CONNECT 方法,否则关闭流
- pseudo headers 中,必须包含 ":method"、":scheme"、":path",其中 ":method" 值必须由 tokens 字符组成,":scheme" 值必须为 "https",":path" 不能为空,否则关闭流
- trailer 中条目的 name 必须被包含在 "trailers" 头中,否则将自动删除
- request headers 大小不能超过 maxHeaderListSize,否则关闭连接
h2 response 检查和处理:
- 如果 HEAD 请求的响应包含 body,将自动删除
- 将自动添加 "date" field,用户提供的 "date" 将被忽略
- 如果 headers 包含 "connection","transfer-encoding","keep-alive","upgrade","proxy-connection",将自动删除
- response 中 "set-cookie" header 将分条发送,其他 headers 同名条目将合成一条发送
- 如果 headers 包含 "content-length",且 method 不为 "HEAD","content-length" 将被删除
- 如果 method 为 "HEAD",则
- headers 包含 "content-length",但 "content-length" 不合法(无法被解析为 Int64 值,或包含多个不同值),如果用户调用 HttpResponseWriter 类的 write 函数,将抛出 HttpException,如果用户 handler 已经结束,将打印日志
- headers 包含 "content-length",同时 response.body.length 不为 -1,"content-length" 值与 body.length 不符,同 6.1 处理
- headers 包含 "content-length",同时 response.body.length 为 -1,或 body.length 与 "content-length" 值一致,则保留 "content-length" header
- trailer 中条目必须被包含在 "trailers" 头中,否则将自动删除
- 如果 handler 中抛出异常,且用户未调用 write 发送部分响应,将返回 500 响应。如果用户已经调用 write 发送部分响应,将发送 RST 帧关闭 stream
h2 server 发完 response 之后,如果 stream 状态不是 CLOSED,会发送带 NO_ERROR 错误码的 RST 帧关闭 stream,避免已经处理完毕的 stream 继续占用服务器资源
h2 流量控制:
- connection 流量窗口初始值为 65535,每次收到 DATA 帧将返回一个 connection 层面的 WINDOW-UPDATE,发送 DATA 时,如果 connection 流量窗口值为负数,将阻塞至其变为正数
- stream 流量窗口初始值可由用户设置,默认值为 65535,每次收到 DATA 帧将返回一个 stream 层面的 WINDOW-UPDATE,发送 DATA 时,如果 stream 流量窗口值为负数,将阻塞至其变为正数
h2 请求优先级:
- 支持按 urgency 处理请求,h2 服务默认并发处理请求,当并发资源不足时,请求将按 urgency 处理,优先级高的请求优先处理
默认 ProtocolServiceFactory 协议选择:
- 如果连接是 tcp,使用 HTTP/1.1 server
- 如果连接是 tls,根据 alpn 协商结果确定 http 协议版本,如果协商结果为 "http/1.0","http/1.1" 或 "",使用 HTTP/1.1 server,如果协商结果为 "h2",使用 HTTP/2 server,否则不处理此次请求,打印日志关连接。
异常:
- SocketException:端口监听失败
func close
public func close(): Unit
功能:关闭服务器,服务器关闭后将不再对请求进行读取与处理重复关闭将只有第一次生效(包括 close 和 closeGracefully)。
func closeGracefully
public func closeGracefully(): Unit
功能:关闭服务器,服务器关闭后将不再对请求进行读取,当前正在进行处理的服务器待处理结束后进行关闭。
func afterBind
public func afterBind(f: ()->Unit): Unit
功能:注册服务器启动时的回调函数,服务内部 ServerSocket 实例 bind 之后,accept 之前将调用该函数。重复调用将覆盖之前注册的函数。
参数:
- f:回调函数,入参为空,返回值为 Unit 类型
func onShutdown
public func onShutdown(f: ()->Unit): Unit
功能:注册服务器关闭时的回调函数,服务器关闭时将调用该回调函数,重复调用将覆盖之前注册的函数。
参数:
- f:回调函数,入参为空,返回值为 Unit 类型
func updateCert
public func updateCert(certificateChainFile: String, privateKeyFile: String): Unit
功能:对 TLS 证书进行热更新。
参数:
- certificateChainFile:证书链文件
- privateKeyFile:证书匹配的私钥文件
异常:
- IllegalArgumentException:参数包含空字符
- HttpException:服务端未配置 tlsConfig
func updateCert
public func updateCert(certChain: Array<X509Certificate>, certKey: PrivateKey): Unit
功能:对 TLS 证书进行热更新。
参数:
- certChain:证书链
- certKey:证书匹配的私钥
异常:
- HttpException:服务端未配置 tlsConfig
func updateCA
public func updateCA(newCaFile: String): Unit
功能:对 CA 证书进行热更新。
参数:
- newCaFile:CA证书文件
异常:
- IllegalArgumentException:参数包含空字符
- HttpException:服务端未配置 tlsConfig
func updateCA
public func updateCA(newCa: Array<X509Certificate>): Unit
功能:对 CA 证书进行热更新。
参数:
- newCa:CA证书
异常:
- IllegalArgumentException:参数包含空字符
- HttpException:服务端未配置 tlsConfig
struct ServicePoolConfig
public struct ServicePoolConfig {
public let capacity: Int64
public let queueCapacity: Int64
public let preheat: Int64
public init(
capacity!: Int64 = 10 ** 4,
queueCapacity!: Int64 = 10 ** 4,
preheat!: Int64 = 0
)
}
Server 协程池配置类。
Server 每次收到一个请求,将从协程池取出一个协程进行处理,如果任务等待队列已满,将拒绝服务该次请求,并断开连接。
特别地,HTTP/2 Service 处理过程中会从协程池取出若干协程进行处理,如果任务等待队列已满,将阻塞直至有协程空闲。
capacity
public let capacity: Int64
功能:获取协程池容量,协程被创建后将循环从 queue 中获取任务并执行,直至协程池关闭
queueCapacity
public let queueCapacity: Int64
功能:获取缓冲区等待任务的最大数量
preheat
public let preheat: Int64
功能:获取服务启动时预先启动的协程数量
init
public init(
capacity!: Int64 = 10 ** 4,
queueCapacity!: Int64 = 10 ** 4,
preheat!: Int64 = 0
)
功能:构造一个 ServicePoolConfig
实例。
参数:
capacity
:协程池容量,默认值为 10000queueCapacity
:缓冲区等待任务的最大数量,默认值为 10000preheat
:服务启动时预先启动的协程数量,默认值为 0
异常:
- IllegalArgumentException:参数 capacity 小于 0,或参数 queueCapacity 小于0,或参数preheat小于 0,或大于 capacity
interface ProtocolServiceFactory
public interface ProtocolServiceFactory {
func create(protocol: Protocol, socket: StreamingSocket): ProtocolService
}
Http 服务实例工厂,用于生成 ProtocolService
实例。
ServerBuilder
提供默认的实现,默认实现提供仓颉标准库中 HTTP/1.1、HTTP/2 的 ProtocolService
实例。
func create
func create(protocol: Protocol, socket: StreamingSocket): ProtocolService
功能:根据协议创建协议服务实例。
参数:
protocol
:协议版本,如 HTTP1_0、HTTP1_1、HTTP2_0socket
:来自客户端的套接字
返回值:协议服务实例
abstract class ProtocolService
Http协议服务实例,为单个客户端连接提供 Http 服务,包括对客户端 request 报文的解析、 request 的分发处理、 response 的发送等。
public abstract class ProtocolService
prop server
open protected mut prop server: Server
Server 实例,提供默认实现,设置为绑定的 Server 实例
func serve
protected func serve(): Unit
功能:处理来自客户端连接的请求,不提供默认实现。
func closeGracefully
open protected func closeGracefully(): Unit
功能:优雅关闭连接,提供默认实现,无任何行为。
func close
open protected func close(): Unit
功能:强制关闭连接,提供默认实现,无任何行为。
interface HttpRequestDistributor
public interface HttpRequestDistributor {
func register(path: String, handler: HttpRequestHandler): Unit
func register(path: String, handler: (HttpContext) -> Unit): Unit
func distribute(path: String): HttpRequestHandler
}
Http request 分发器,将一个 request 按照 url 中的 path 分发给对应的 HttpRequestHandler 处理。 本实现提供一个默认的 HttpRequestDistributor,该 distributor 非线程安全,且只能在启动 server 前 register,启动后再次 register,结果未定义。 如果用户希望在启动 server 后还能够 register,需要自己提供一个线程安全的 HttpRequestDistributor 实现。
func register
func register(path: String, handler: HttpRequestHandler): Unit
功能:注册请求处理器。
参数:
- path:请求路径
- handler:请求处理器
异常:
- HttpException:请求路径已注册请求处理器
func register
func register(path: String, handler: (HttpContext) -> Unit): Unit
功能:注册请求处理函数,将调用 register(String, HttpRequestHandler): Unit 函数注册请求处理器。
参数:
- path:请求路径
- handler:请求处理函数
异常:
- HttpException:请求路径已注册请求处理器
func distribute
func distribute(path: String): HttpRequestHandler
功能:分发请求处理器,未找到对应请求处理器时,将返回 NotFoundHandler 以返回 404 状态码。 参数:
- path:请求路径
返回值:返回请求处理器
class HttpContext
Http 请求上下文,作为 HttpRequestHandler.handle 函数的参数在服务端使用。
public class HttpContext
prop request
public prop request: HttpRequest
功能:获取 Http 请求
prop responseBuilder
public prop responseBuilder: HttpResponseBuilder
功能:获取 Http 响应构建器
prop clientCertificate
public prop clientCertificate: ?Array<X509Certificate>
功能:获取 Http 客户端证书
func upgrade
public func upgrade(ctx: HttpContext): StreamingSocket
功能:在 handler 内获取 StreamingSocket,可用于支持协议升级和处理 CONNECT 请求。
- 调用该函数时,将首先根据 ctx.responseBuilder 发送响应,仅发送状态码和响应头。
- 调用该函数时,将把 ctx.request.body 置空,后续无法通过 body.read(...) 读数据,未读完的 body 数据将留存在返回的 StreamingSocket 中
注意:
-
对于 HTTP/1.1 或 HTTP/1.0
- 获取的 StreamingSocket 是底层连接,可直接读写连接上的数据
- 如果请求方法为 CONNECT,响应状态码必须是 2XX,否则报错,响应头中 content-length 和 transfer-encoding 头将被删除
- 如果请求方法不为 CONNECT,响应状态码必须是 101,如果用户未设置,将自动置为 101,如果用户设置了状态码且非 101,将报错
-
对于 HTTP/2
- 获取的 StreamingSocket 是一个 stream 上的读写流,后续读写数据将由 stream 封装成 data 帧收发
- 请求方法必须为 CONNECT,响应状态码必须为 2XX,否则将报错
参数:
- ctx:请求上下文
返回值:底层连接(对于 HTTP/2 是一个 stream),可用于后续读写
异常:
- HttpException:获取底层连接(对于 HTTP/2 是一个 stream)失败
interface HttpRequestHandler
public interface HttpRequestHandler {
func handle(ctx: HttpContext): Unit
}
Http request 处理器。 http server 端通过 handler 处理来自客户端的 http request;在 handler 中用户可以获取 http request 的详细信息,包括 header、body;在 handler 中,用户可以构造 http response,包括 header、body,并且可以直接发送 response 给客户端,也可交由 server 发送。 用户在构建 http server 时,需手动通过 server 的 HttpRequestDistributor 注册一个或多个 handler,当一个客户端 http request 被接收,distributor 按照request 中 url 的 path 分发给对应的 handler 处理。
func handle
func handle(ctx: HttpContext): Unit
功能:处理 Http 请求。
参数:
- ctx:Http 请求上下文
class FuncHandler
public class FuncHandler <: HttpRequestHandler {
public FuncHandler(let handler: (HttpContext) -> Unit)
}
HttpRequestHandler 接口包装类,把单个函数包装成 HttpRequestHandler。
FuncHandler
public FuncHandler(let handler: (HttpContext) -> Unit)
功能:FuncHandler 的构造函数。
参数:
- handler:是调用 handle 的处理函数,其入参为 HttpContext ,返回值类型为 Unit 的函数类型
func handle
public func handle(ctx: HttpContext): Unit
功能:处理 Http 请求。
参数:
- ctx:Http 请求上下文
class NotFoundHandler
public class NotFoundHandler <: HttpRequestHandler
便捷的 Http 请求处理器,404 Not Found
处理器。
func handle
public func handle(ctx: HttpContext): Unit
功能:处理 Http 请求,回复 404 响应。
参数:
- ctx:Http 请求上下文
func notFound
public func notFound(ctx: HttpContext): Unit
功能:便捷的 Http 请求处理函数,用于回复 404 响应。处理 Http 请求,回复 404 响应。
参数:
- ctx:Http 请求上下文
func handleError
public func handleError(ctx: HttpContext, code: UInt16): Unit
功能:便捷的 Http 请求处理函数,用于回复错误请求。处理 Http 请求,回复响应。
参数:
- ctx:Http 请求上下文
- code:Http 响应码
class OptionsHandler
public class OptionsHandler <: HttpRequestHandler
便捷的 Http 处理器,用于处理 OPTIONS 请求。 固定返回 "Allow: OPTIONS,GET,HEAD,POST,PUT,DELETE" 响应头
func handle
public func handle(ctx: HttpContext): Unit
功能:处理 Http OPTIONS 请求。
参数:
- ctx:Http 请求上下文
class RedirectHandler
public class RedirectHandler <: HttpRequestHandler {
public init(url: String, code: UInt16)
}
便捷的 Http 处理器,用于回复重定向响应。
init
public init(url: String, code: UInt16)
功能:RedirectHandler 的构造函数。
参数:
- url:重定向响应中 Location 头部的 url
- code:重定向响应的响应码
异常:
- HttpException:url 为空或响应码不是除 304 以外的 3XX 状态码时抛出异常
func handle
public func handle(ctx: HttpContext): Unit
功能:处理 Http 请求,回复重定向响应。
参数:
- ctx:Http 请求上下文
enum FileHandlerType
public enum FileHandlerType {
| DownLoad
| UpLoad
}
此枚举类用于设置 FileHandler
是上传还是下载模式。
DownLoad
DownLoad
功能:创建一个 FileHandler
类型的枚举实例,表示下载模式。
UpLoad
UpLoad
功能:创建一个 FileHandler
类型的枚举实例,表示上传模式。
class FileHandler
public class FileHandler <: HttpRequestHandler {
public init(path: String, handlerType!: FileHandlerType = DownLoad, bufferSize!: Int64 = 64 * 1024)
}
用于处理文件下载或者文件上传。
- 文件下载:
- 构造
FileHandler
时需要传入待下载文件的路径,目前一个FileHandler
只能处理一个文件的下载; - 下载文件只能使用 GET 请求,其他请求返回 400 状态码;
- 文件如果不存在,将返回 404 状态码;
- 构造
- 文件上传:
- 构造
FileHandler
时需要传入一个存在的目录路径,上传到服务端的文件将保存在这个目录中; - 上传文件时只能使用 POST 请求,其他请求返回 400 状态码;
- 上传数据的 http 报文必须是
multipart/form-data
格式的,Content-Type
头字段的值为multipart/form-data; boundary=----XXXXX
; - 上传文件的文件名存放在
form-data
数据报文中,报文数据格式为Content-Disposition: form-data; name="xxx"; filename="xxxx"
,文件名是filename
字段的值; - 目前 form-data 中必须包含 filename 字段;
- 如果请求报文不正确,将返回 400 状态码;
- 如果出现其他异常,例如文件处理异常,将返回 500 状态码;
- 构造
init
public init(path: String, handlerType!: FileHandlerType = DownLoad, bufferSize!: Int64 = 64 * 1024)
功能:FileHandler 的构造函数。
参数:
- path:FileHandler 构造时需要传入的文件或者目录路径字符串,上传模式中只能传入存在的目录路径;路径中存在../时,用户需要确认标准化后的绝对路径是期望传入的路径
- handlerType:构造 FileHandler 时指定当前 FileHandler 的工作模式,默认为 DownLoad 下载模式
- bufferSize:内部从网络读取或者写入的缓冲区大小,默认值为 64*1024(64k),若小于 4096,则使用 4096 作为缓冲区大小
异常:
- HttpException:当 path 不存在时,抛出异常
func handle
public func handle(ctx: HttpContext): Unit
功能:根据请求对响应数据进行处理。
参数:
- ctx:Http 请求上下文
class Client
public class Client
Client 类,用户可以通过 Client 实例发送 HTTP/1.1 或 HTTP/2 请求。
主要提供功能:发送 Http request、随时关闭等。
Client 文档中未明确说明支持版本的配置,在 HTTP/1.1 与 HTTP/2 都会生效。
prop httpProxy
public prop httpProxy: String
功能:获取客户端 http 代理,默认使用系统环境变量 http_proxy 的值,用字符串表示,格式为:"http://host:port",例如:"http://192.168.1.1:80"
prop httpsProxy
public prop httpsProxy: String
功能:获取客户端 https 代理,默认使用系统环境变量 https_proxy 的值,用字符串表示,格式为:"http://host:port",例如:"http://192.168.1.1:443"
prop connector
public prop connector: (String) -> StreamingSocket
功能:客户端调用此函数获取到服务器的连接
prop logger
public prop logger: Logger
功能:获取客户端日志记录器,设置 logger.level 将立即生效,记录器应该是线程安全的
prop cookieJar
public prop cookieJar: ?CookieJar
功能:用于存储客户端所有 Cookie,如果配置为 None,则不会启用 Cookie
prop poolSize
public prop poolSize: Int64
功能:HTTP/1.1 客户端使用连接池大小,表示对同一个主机(host:port)同时存在的连接数的最大值
prop autoRedirect
public prop autoRedirect: Bool
功能:获取客户端是否会自动进行重定向,304 状态码默认不重定向
func getTlsConfig
public func getTlsConfig(): ?TlsClientConfig
功能:获取客户端设定的 TLS 层配置。
返回值:
prop readTimeout
public prop readTimeout: Duration
功能:获取客户端设定的读取整个响应的超时时间,默认值为 15s
prop writeTimeout
public prop writeTimeout: Duration
功能:获取客户端设定的写请求的超时时间,默认值为 15s
prop headerTableSize
public prop headerTableSize: UInt32
功能:获取客户端 HTTP/2 Hpack 动态表初始值,默认值为 4096
prop enablePush
public prop enablePush: Bool
功能:获取客户端 HTTP/2 是否支持服务器推送,默认值为 true
prop maxConcurrentStreams
public prop maxConcurrentStreams: UInt32
功能:获取客户端 HTTP/2 初始最大并发流数量,默认值为 2^31 - 1
prop initialWindowSize
public prop initialWindowSize: UInt32
功能:获取客户端 HTTP/2 流控窗口初始值,默认值为 65535 ,取值范围为 0 至 2^31 - 1。
prop maxFrameSize
public prop maxFrameSize: UInt32
功能:获取客户端 HTTP/2 初始最大帧大小。默认值为 16384. 取值范围为 2^14 至 2^24 - 1
prop maxHeaderListSize
public prop maxHeaderListSize: UInt32
功能:获取客户端 HTTP/2 最大 header 大小。响应 headers 最大长度,为所有 header field 长度之和(所有 name 长度 + value 长度 + 32,包括自动添加的 h2 伪头),默认值为 UInt32.Max
func send
public func send(req: HttpRequest): HttpResponse
功能:通用请求函数,发送 HttpRequest 到 url 中的服务器,接收 HttpResponse注意:
- 对于 HTTP/1.1,如果请求中有 body 要发,那么需要保证 Content-Length 和 Transfer-Encoding: chunked必有且只有一个,以 chunked 形式发时,每段 chunk 最大为 8192 字节:如果用户发送的 body 为自己实现的 InputStream 类,则需要自己保证 Content-Length和 Transfer-Encoding: chunked 设置且只设置了一个;如果用户采用默认的 body 发送,Content-Length 和 Transfer-Encoding: chunked都缺失时,我们会为其补上 Content-Length header,值为 body.size
- 用户如果设置了 Content-Length,则需要保证其正确性:如果所发 body 的内容大于等于 Content-Length 的值,我们会发送长度为 Content-Length 值的数据;如果所发 body 的内容小于 Content-Length 的值,此时如果 body 是默认的 body,则会抛出 HttpException,如果 body是用户自己实现的 InputStream 类,其行为便无法保证(可能会造成服务器端的读 request 超时或者客户端的收 response 超时)
- 升级函数通过 WebSocket 的 upgradeFromClient 或 Client 的 upgrade 接口发出,调用 client 的其他函数发送 upgrade 请求会抛出异常
- 协议规定 TRACE 请求无法携带内容,故用户发送带有 body 的 TRACE 请求时会抛出异常
- HTTP/1.1 默认对同一个服务器的连接数不超过 10 个。response 的 body 需要用户调用
body.read(buf: Array<Byte>)
函数去读。body 被读完后,连接才能被客户端对象复用,否则请求相同的服务器也会新建连接。新建连接时如果连接数超出限制则会抛出 HttpException - body.read 函数将 body 读完之后返回 0,如果读的时候连接断开会抛出 ConnectionException
- HTTP/1.1 的升级请求如果收到 101 响应,则表示切换协议,此连接便不归 client 管理
- 下文的快捷请求函数的注意点相同一次请求重定向超出一定次数时抛此异常客户端解析报文出错时抛此异常一次性收到的 1xx 响应超过一定数量抛此异常对同一个服务器的连接超过一定数量抛此异常使用 send 函数升级协议抛此异常发送带有 body 的 TRACE 请求抛此异常。
参数:
- HttpRequest:发送的请求
返回值:服务端返回处理该请求的响应
异常:
- UrlSyntaxException:请求中 URL 错误时抛此异常
- SocketException:Socket 连接出现错误时抛此异常
- ConnectionException:从连接中读数据时对端已关闭连接抛此异常
- SocketTimeoutException:Socket 连接超时抛此异常
- TlsException:Tls 连接建立失败或通信异常抛此异常
- HttpException:当用户未使用 http 库提供的 API 升级 WebSocket 时抛此异常
- HttpTimeoutException:请求超时或读 HttpResponse.body 超时抛此异常
func get
public func get(url: String): HttpResponse
功能:请求方法为 GET 的便捷请求函数。
参数:
- url:请求的 url
返回值:服务端返回的响应
异常:
- UrlSyntaxException:当参数 url 不符合 URL 解析规范时,抛出异常
- IllegalArgumentException:当被编码的字符不符合 UTF-8 的字节序列规则时,抛出异常
- 其余同 func send
func head
public func head(url: String): HttpResponse
功能:请求方法为 HEAD 的便捷请求函数。
参数:
- url:请求的 url
返回值:服务端返回的响应
异常:
- UrlSyntaxException:当参数 url 不符合 URL 解析规范时,抛出异常
- IllegalArgumentException:当被编码的字符不符合 UTF-8 的字节序列规则时,抛出异常
- 其余同 func send
func put
public func put(url: String, body: InputStream): HttpResponse
功能:请求方法为 PUT 的便捷请求函数。
参数:
- url:请求的 url
- body:请求体
返回值:服务端返回的响应
异常:
- UrlSyntaxException:当参数 url 不符合 URL 解析规范时,抛出异常
- IllegalArgumentException:当被编码的字符不符合 UTF-8 的字节序列规则时,抛出异常
- 其余同 func send
func put
public func put(url: String, body: String): HttpResponse
功能:请求方法为 PUT 的便捷请求函数。
参数:
- url:请求的 url
- body:请求体
返回值:服务端返回的响应
异常:
- UrlSyntaxException:当参数 url 不符合 URL 解析规范时,抛出异常
- IllegalArgumentException:当被编码的字符不符合 UTF-8 的字节序列规则时,抛出异常
- 其余同 func send
func put
public func put(url: String, body: Array<UInt8>): HttpResponse
功能:请求方法为 PUT 的便捷请求函数。
参数:
- url:请求的 url
- body:请求体
返回值:服务端返回的响应
异常:
- UrlSyntaxException:当参数 url 不符合 URL 解析规范时,抛出异常
- IllegalArgumentException:当被编码的字符不符合 UTF-8 的字节序列规则时,抛出异常
- 其余同 func send
func post
public func post(url: String, body: InputStream): HttpResponse
功能:请求方法为 POST 的便捷请求函数。
参数:
- url:请求的 url
- body:请求体
返回值:服务端返回的响应
异常:
- UrlSyntaxException:当参数 url 不符合 URL 解析规范时,抛出异常
- IllegalArgumentException:当被编码的字符不符合 UTF-8 的字节序列规则时,抛出异常
- 其余同 func send
func post
public func post(url: String, body: String): HttpResponse
功能:请求方法为 POST 的便捷请求函数。
参数:
- url:请求的 url
- body:请求体
返回值:服务端返回的响应
异常:
- UrlSyntaxException:当参数 url 不符合 URL 解析规范时,抛出异常
- IllegalArgumentException:当被编码的字符不符合 UTF-8 的字节序列规则时,抛出异常
- 其余同 func send
func post
public func post(url: String, body: Array<UInt8>): HttpResponse
功能:请求方法为 POST 的便捷请求函数。
参数:
- url:请求的 url
- body:请求体
返回值:服务端返回的响应
异常:
- UrlSyntaxException:当参数 url 不符合 URL 解析规范时,抛出异常
- IllegalArgumentException:当被编码的字符不符合 UTF-8 的字节序列规则时,抛出异常
- 其余同 func send
func delete
public func delete(url: String): HttpResponse
功能:请求方法为 DELETE 的便捷请求函数。
参数:
- url:请求的 url
返回值:服务端返回的响应
异常:
- UrlSyntaxException:当参数 url 不符合 URL 解析规范时,抛出异常
- IllegalArgumentException:当被编码的字符不符合 UTF-8 的字节序列规则时,抛出异常
- 其余同 func send
func connect
public func connect(url: String, header!: HttpHeaders = HttpHeaders(), version!: Protocol = HTTP1_1): (HttpResponse, ?StreamingSocket)
功能:发送 CONNECT 请求与服务器建立隧道,返回建连成功后的连接,连接由用户负责关闭。服务器返回 2xx 表示建连成功,否则建连失败(不支持自动重定向,3xx 也视为失败)。
参数:
- url:请求的 url
- headers:请求头,默认为空请求头
- version:请求的协议,默认为 HTTP1_1
返回值:返回元组类型,其中 HttpResponse 实例表示服务器返回的响应体,Option<StreamingSocket>
实例表示请求成功时返回 headers 之后连接
异常:
- UrlSyntaxException:当参数 url 不符合 URL 解析规范时,抛出异常
- IllegalArgumentException:当被编码的字符不符合 UTF-8 的字节序列规则时,抛出异常
- 其余同 func send
func options
public func options(url: String): HttpResponse
功能:请求方法为 OPTIONS 的便捷请求函数。
参数:
- url:请求的 url
返回值:服务端返回的响应
异常:
- UrlSyntaxException:当参数 url 不符合 URL 解析规范时,抛出异常
- IllegalArgumentException:当被编码的字符不符合 UTF-8 的字节序列规则时,抛出异常
- 其余同 func send
func upgrade
public func upgrade(req: HttpRequest): (HttpResponse, ?StreamingSocket)
功能:发送请求并升级协议,用户设置请求头,返回升级后的连接(如果升级成功),连接由用户负责关闭。
- 服务器返回 101 表示升级成功,获取到了 StreamingSocket
- 必选请求头:
- Upgrade: protocol-name ["/" protocol-version]
- Connection: Upgrade (Connection: Upgrade 在有 Upgrade 时会自动补上)
- 不支持 HTTP/1.0、HTTP/2
- 不支持 HTTP/1.1 CONNECT 方法的 HttpRequest
参数:
- req:升级时发送的请求
返回值:返回一个元组,HttpResponse 实例表示服务器返回的响应,?StreamingSocket 实例表示获取的底层连接,升级失败时为 None
异常:
- HttpException:
- 请求报文或响应报文不符合协议
- 请求报文不含 Upgrade 头
- 发送 CONNECT 请求
- 发送带 body 的 TRACE 请求
- SocketException,ConnectionException:Socket 连接出现异常或被关闭
- SocketTimeoutException:Socket 连接超时
- TlsException: Tls 连接建立失败或通信异常
func close
public func close(): Unit
功能:关闭客户端建立的所有连接,调用后不能继续发送请求。
class ClientBuilder
public class ClientBuilder
ClientBuilder 类,用于 Client 实例的构建,Client 没有公开的构造函数,用户只能通过 ClientBuilder 得到 Client 实例。ClientBuilder 文档中未明确说明支持版本的配置,在 HTTP/1.1 与 HTTP/2 都会生效。
init
public init()
功能:创建新的 ClientBuilder 实例。
func httpProxy
public func httpProxy(addr: String): ClientBuilder
功能:设置客户端 http 代理,默认使用系统环境变量 http_proxy 的值。
参数:
- addr:格式为:"http://host:port",例如:"http://192.168.1.1:80"
返回值:当前 ClientBuilder 实例的引用
func httpsProxy
public func httpsProxy(addr: String): ClientBuilder
功能:设置客户端 https 代理,默认使用系统环境变量 https_proxy 的值。
参数:
- addr:格式为:"http://host:port",例如:"http://192.168.1.1:443"
返回值:当前 ClientBuilder 实例的引用
func noProxy
public func noProxy(): ClientBuilder
功能:调用此函数后,客户端不使用任何代理。
返回值:当前 ClientBuilder 实例的引用
func connector
public func connector(connector: (String)->StreamingSocket): ClientBuilder
功能:客户端调用此函数获取到服务器的连接。
参数:
- connector:入参为字符串,返回值类型为 Unit 的函数类型字符串返回
返回值:当前 ClientBuilder 实例的引用
func logger
public func logger(logger: Logger): ClientBuilder
功能:设定客户端的 logger,默认 logger 级别为 INFO,logger 内容将写入 Console.stdout。
参数:
- logger:需要是线程安全的,默认使用内置线程安全 logger
返回值:当前 ClientBuilder 实例的引用
func cookieJar
public func cookieJar(cookieJar: ?CookieJar): ClientBuilder
功能:用于存储客户端所有 Cookie。
参数:
- cookieJar:默认使用一个空的 CookieJar,如果配置为 None 则不会启用 Cookie
返回值:当前 ClientBuilder 实例的引用
func poolSize
public func poolSize(size: Int64): ClientBuilder
功能:HTTP/1.1 客户端使用连接池大小,表示对同一个主机(host:port)同时存在的连接数的最大值。
参数:
- size:默认 10,poolSize 需要大于 0
异常:
- HttpException:如果传参小于等于 0,则会抛出该异常
返回值:当前 ClientBuilder 实例的引用
func autoRedirect
public func autoRedirect(auto: Bool): ClientBuilder
功能:配置客户端是否会自动进行重定向,重定向会请求 Location 头的资源,协议规定,Location 只能包含一个 URI 引用Location = URI-reference详见 RFC 9110 10.2.2.。304 状态码默认不重定向。
参数:
- autoRedirect,默认 true
返回值:当前 ClientBuilder 实例的引用
func tlsConfig
public func tlsConfig(config: TlsClientConfig): ClientBuilder
功能:设置 TLS 层配置,默认不对其进行设置。
参数:
- config:设定支持 tls 客户端需要的配置信息
返回值:当前 ClientBuilder 实例的引用
func readTimeout
public func readTimeout(timeout: Duration): ClientBuilder
功能:设定客户端读取一个响应的最大时长。
参数:
- timeout:默认 15s,Duration.Max 代表不限制,如果传入负的 Duration 将被替换为 Duration.Zero
返回值:当前 ClientBuilder 实例的引用
func writeTimeout
public func writeTimeout(timeout: Duration): ClientBuilder
功能:设定客户端发送一个请求的最大时长。
参数:
- timeout,默认 15s,Duration.Max 代表不限制,如果传入负的 Duration 将被替换为 Duration.Zero
返回值:当前 ClientBuilder 实例的引用
func headerTableSize
public func headerTableSize(size: UInt32): ClientBuilder
功能:配置客户端 HTTP/2 Hpack 动态表初始值。
参数:
- size:默认值 4096
返回值:当前 ClientBuilder 实例的引用
func enablePush
public func enablePush(enable: Bool): ClientBuilder
功能:配置客户端 HTTP/2 是否支持服务器推送。
参数:
- enable:默认值 true
返回值:当前 ClientBuilder 实例的引用
func maxConcurrentStreams
public func maxConcurrentStreams(size: UInt32): ClientBuilder
功能:配置客户端 HTTP/2 初始最大并发流数量。
参数:
- size:默认值为 2^31 - 1
返回值:当前 ClientBuilder 实例的引用
func initialWindowSize
public func initialWindowSize(size: UInt32): ClientBuilder
功能:配置客户端 HTTP/2 流控窗口初始值。
参数:
- size:默认值 65535 , 取值范围为 0 至 2^31 - 1
返回值:当前 ClientBuilder 实例的引用
func maxFrameSize
public func maxFrameSize(size: UInt32): ClientBuilder
功能:配置客户端 HTTP/2 初始最大帧大小。
参数:
- size:默认值为 16384. 取值范围为 2^14 至 2^24 - 1
返回值:当前 ClientBuilder 实例的引用
func maxHeaderListSize
public func maxHeaderListSize(size: UInt32): ClientBuilder
功能:配置客户端接收的 HTTP/2 响应 headers 最大长度,为所有 header field 长度之和(所有 name 长度 + value 长度 + 32,包括自动添加的 h2 伪头),默认值为 UInt32.Max。
参数:
- size:客户端接收的 HTTP/2 响应 headers 最大长度
返回值:当前 ClientBuilder 实例的引用
func build
public func build(): Client
功能:构造 Client 实例。
返回值:用当前 ClientBuilder 实例构造数来的 Client 实例。
异常:
- IllegalArgumentException:配置项有非法参数时抛出此异常
class WebSocket
public class WebSocket
提供 WebSocket 服务的相关类,用户通过 upgradeFrom 函数以获取 WebSocket 连接。
提供 WebSocket 连接的读、写、关闭等函数。
调用 read()
即读取一个 WebSocketFrame,用户可通过 WebSocketFrame.frameType 来知晓帧的类型,通过 WebSocketFrame.fin 来知晓是否是分段帧。
调用 write(frameType: WebSocketFrameType, byteArray: Array<UInt8>)
,传入message的类型,和 message 的 byte 来发送 WebSocket 信息,如果写的是控制帧,则不会分段发送,如果写的是数据帧(Text、Binary),则会将 message 按底层 buffer 的大小分段(分成多个 fragment)发送。
详细说明见下文接口说明,接口行为以 RFC 6455 为准。
prop subProtocol
public prop subProtocol: String
功能:获取与对端协商到的 subProtocol,协商时,客户端提供一个按偏好排名的 subProtocols 列表,服务器从中选取一个或零个子协议
prop logger
public prop logger: Logger
功能:日志记录器
func read
public func read(): WebSocketFrame
功能:从连接中读取一个帧,如果连接上数据未就绪会阻塞,非线程安全(即对同一个 WebSocket 对象不支持多线程读)
说明:read 函数返回一个帧对象 WebSocketFrame ,用户可以调用 WebSocketFrame 的 frameType,fin 属性确定其帧类型和是否是分段帧调用 WebSocketFrame 的 payload 函数得到原始二进制数据数组:Array<UInt8>
- 分段帧的首帧为 fin == false,frameType == TextWebFrame 或 BinaryWebFrame中间帧 fin == false,frameType == ContinuationWebFrame尾帧 fin == true, frameType == ContinuationWebFrame
- 非分段帧为 fin == true, frameType != ContinuationWebFrame
注意:
- 数据帧(Text,Binary)可以分段,用户需要多次调用 read 将所有分段帧读完(以下称为接收到完整的 message),再将分段帧的 payload 按接收序拼接Text 帧的 payload 为 UTF-8 编码,用户在接收到完整的 message 后,调用 String.fromUtf8函数将拼接后的 payload 转成字符串Binary 帧的 payload 的意义由使用其的应用确定,用户在接收到完整的 message 后,将拼接后的 payload 传给上层应用
- 控制帧(Close,Ping,Pong)不可分段
- 控制帧本身不可分段,但其可以穿插在分段的数据帧之间。分段的数据帧之间不可出现其他数据帧,如果用户收到穿插的分段数据帧,则需要当作错误处理
- 客户端收到 masked 帧,服务器收到 unmasked 帧,断开底层连接并抛出异常
- rsv1、rsv2、rsv3 位被设置(暂不支持 extensions,因此 rsv 位必须为 0),断开底层连接并抛出异常
- 收到无法理解的帧类型(只支持 Continuation,Text,Binary,Close,Ping,Pong),断开底层连接并抛出异常
- 收到分段或 payload 长度大于 125 bytes 的控制帧(Close,Ping,Pong),断开底层连接并抛出异常
- 收到 payload 长度大于 20M 的帧,断开底层连接并抛出异常
- closeConn 关闭连接后继续调用读,抛出异常。
返回值:读到的 WebSocketFrame 对象
异常:
- SocketException:底层连接错误
- WebSocketException:收到不符合协议规定的帧,此时会给对端发送 Close 帧说明错误信息,并断开底层连接
- ConnectionException:从连接中读数据时对端已关闭连接抛此异常
func write
public func write(frameType: WebSocketFrameType, byteArray: Array<UInt8>, frameSize!: Int64 = FRAMESIZE)
功能:发送数据,非线程安全(即对同一个 WebSocket 对象不支持多线程写)
说明:write 函数将数据以 WebSocket 帧的形式发送给对端
- 如果发送数据帧(Text,Binary),传入的 byteArray 如果大于 frameSize (默认 4 * 1024 bytes),我们会将其分成小于等于 frameSize 的 payload 以分段帧的形式发送,否则不分段
- 如果发送控制帧(Close,Ping,Pong),传入的 byteArray 的大小需要小于等于 125 bytes,Close 帧的前两个字节为状态码,可用的状态码见 RFC 6455 7.4. Status Codes协议规定,Close 帧发送之后,禁止再发送数据帧,如果发送则会抛出异常
- 用户需要自己保证其传入的 byteArray 符合协议,如 Text 帧的 payload 需要是 UTF-8 编码,如果数据帧设置了 frameSize,那么需要大于 0,否则抛出异常
- 发送数据帧时,frameSize 小于等于 0,抛出异常
- 用户发送控制帧时,传入的数据大于 125 bytes,抛出异常
- 用户传入非 Text,Binary,Close,Ping,Pong 类型的帧类型,抛出异常
- 发送 Close 帧时传入非法的状态码,或 reason 数据超过 123 bytes,抛出异常
- 发送完 Close 帧后继续发送数据帧,抛出异常
- closeConn 关闭连接后调用写,抛出异常
参数:
- frameType:所需发送的帧的类型
- byteArray:所需发送的帧的 payload(二进制形式)
- frameSize:分段帧的大小,默认为 4 * 1024 bytes,frameSize 不会对控制帧生效(控制帧设置了无效)
异常:
- SocketException:底层连接错误
- WebSocketException:传入非法的帧类型,或者数据时抛出异常
func writeCloseFrame
public func writeCloseFrame(status!: ?UInt16 = None, reason!: String = ""): Unit
功能:发送 Close 帧
注意:协议规定,Close 帧发送之后,禁止再发送数据帧如果用户不设置 status,那么 reason 不会被发送(即有 reason 必有 status);控制帧的 payload 不超过 125 bytes,Close 帧的前两个 bytes 为 status,因此 reason 不能超过 123 bytes,closeConn 关闭连接后调用写,抛出异常。
参数:
- status:发送的 Close 帧的状态码,默认为 None,表示不发送状态码和 reason
- reason:关闭连接的说明,默认为空字符串,发送时会转成 UTF-8,不保证可读,debug 用,
异常:
- WebSocketException:传入非法的状态码,或 reason 数据超过 123 bytes
func writePingFrame
public func writePingFrame(byteArray: Array<UInt8>): Unit
功能:提供发送 Ping 帧的快捷函数,closeConn 关闭连接后调用写,抛出异常。
参数:
- byteArray:所需发送的帧的 payload(二进制形式)
异常:
- SocketException:底层连接错误
- WebSocketException:传入的数据大于 125 bytes,抛出异常
func writePongFrame
public func writePongFrame(byteArray: Array<UInt8>): Unit
功能:提供发送 Pong 帧的快捷函数,closeConn 关闭连接后调用写,抛出异常。
参数:
- byteArray:所需发送的帧的 payload(二进制形式)
异常:
- SocketException:底层连接错误
- WebSocketException:传入的数据大于 125 bytes,抛出异常
func closeConn
public func closeConn(): Unit
功能:提供关闭底层 WebSocket 连接的函数
说明:直接关闭底层连接。正常的关闭流程需要遵循协议规定的握手流程,即先发送 Close 帧给对端,并等待对端回应的 Close 帧。握手流程结束后方可关闭底层连接。
func upgradeFromServer
public static func upgradeFromServer(ctx: HttpContext, subProtocols!: ArrayList<String> = ArrayList<String>(),
origins!: ArrayList<String> = ArrayList<String>(),
userFunc!:(HttpRequest) -> HttpHeaders = {_: HttpRequest => HttpHeaders()}): WebSocket
功能:提供服务端升级到 WebSocket 协议的函数,通常在 handler 中使用
说明:服务端升级的流程为,收到客户端发来的升级请求,验证请求,如果验证通过,则回复 101 响应并返回 WebSocket 对象用于 WebSocket 通讯
- 用户通过 subProtocols,origins 参数来配置其支持的 subprotocol 和 origin 白名单,subProtocols如果不设置,则表示不支持子协议,origins 如果不设置,则表示接受所有 origin 的握手请求
- 用户通过 userFunc 来自定义处理升级请求的行为,如处理 cookie 等,传入的 userFunc 要求返回一个 HttpHeaders 对象,其会通过 101 响应回给客户端(升级失败的请求则不会)
- 暂不支持 WebSocket 的 extensions,因此如果握手过程中出现 extensions 协商则会抛 WebSocketException
- 只支持 HTTP1_1 和 HTTP2_0 向 WebSocket 升级
参数:
- ctx:Http 请求上下文,将传入给 handler 的直接传给 upgradeFromServer 即可
- subProtocols:用户配置的子协议列表,默认值为空,表示不支持。如果用户配置了,则会选取升级请求中最靠前的且出现在配置列表中的子协议作为升级后的 WebSocket 的子协议,用户可通过调用返回的 WebSocket 的 subProtocol 查看子协议
- origins:用户配置的同意握手的 origin 的白名单,如果不配置,则同意来自所有 origin 的握手,如果配置了,则只接受来自配置 origin 的握手
- userFunc:用户配置的自定义处理升级请求的函数,该函数返回一个 HttpHeaders,
返回值:
- WebSocket:升级得到的 WebSocket 实例
func upgradeFromClient
public static func upgradeFromClient(client: Client, url: URL,
version!: Protocol = HTTP1_1,
subProtocols!: ArrayList<String> = ArrayList<String>(),
headers!: HttpHeaders = HttpHeaders()): (WebSocket, HttpHeaders)
功能:提供客户端升级到 WebSocket 协议的函数
说明:客户端的升级流程为,传入 client 对象,url 对象,构建升级请求,请求服务器后验证其响应,如果握手成功,则返回 WebSocket 对象用于 WebSocket 通讯,并返回 101 响应头的 HttpHeaders 对象给用户,暂不支持 extensions如果子协议协商成功,用户可通过调用返回的 WebSocket 的 subProtocol 查看子协议。
参数:
- client:用于请求的 client 对象
- version:创建 socket 使用的 HTTP 版本,只支持 HTTP1_1 和 HTTP2_0 向 WebSocket 升级
- url:用于请求的 url 对象,WebSocket 升级时要注意 url 的 scheme 为 ws 或 wss
- subProtocols:用户配置的子协议列表,按偏好排名,默认为空。若用户配置了,则会随着升级请求发送给服务器。
- headers:需要随着升级请求一同发送的非升级必要头,如 cookie 等
返回值:升级成功,则返回 WebSocket 对象用于通讯和 101 响应的头
异常:
- SocketException:底层连接错误
- HttpException:握手时 HTTP 请求过程中出现错误
- WebSocketException:升级失败,升级响应验证不通过
class WebSocketFrame
public class WebSocketFrame
WebSocket 用于读的基本单元。
说明:WebSocketFrame 提供了三个属性,其中 fin 和 frameType 共同说明了帧是否分段和帧的类型。payload 为帧的载荷。 分段帧的首帧为 fin == false,frameType == TextWebFrame 或 BinaryWebFrame 中间帧 fin == false,frameType == ContinuationWebFrame 尾帧 fin == true, frameType == ContinuationWebFrame 非分段帧为 fin == true, frameType != ContinuationWebFrame 用户仅能通过 WebSocket 对象的 read 函数得到 WebSocketFrame。数据帧可分段,如果用户收到分段帧,则需要多次调用 read 函数直到收到完整的 message,并将所有分段的 payload 按接收顺序拼接。 注意,由于控制帧可以穿插在分段帧之间,用户在拼接分段帧的 payload 时需要单独处理控制帧。分段帧之间仅可穿插控制帧,如果用户在分段帧之间接收到其他数据帧,则需要当作错误处理。
prop fin
public prop fin: Bool
功能:获取 WebSocketFrame 的 fin 属性,fin 与 frameType 共同说明了帧是否分段和帧的类型
prop frameType
public prop frameType: WebSocketFrameType
功能:获取 WebSocketFrame 的帧类型,fin 与 frameType 共同说明了帧是否分段和帧的类型
prop payload
public prop payload: Array<UInt8>
功能:获取 WebSocketFrame 的帧载荷,如果是分段数据帧,用户需要在接收完完整的 message 后将所有分段的 payload 按接收序拼接
enum WebSocketFrameType
public enum WebSocketFrameType <: Equatable<WebSocketFrameType> & ToString {
| ContinuationWebFrame
| TextWebFrame
| BinaryWebFrame
| CloseWebFrame
| PingWebFrame
| PongWebFrame
| UnknownWebFrame
}
WebSocketFrame 的枚举类型。
ContinuationWebFrame
ContinuationWebFrame
功能:表示 continuation 帧类型,对应 Opcode 为 %x0 的情形。
TextWebFrame
TextWebFrame
功能:表示 text 帧类型,对应 Opcode 为 %x1 的情形。
BinaryWebFrame
BinaryWebFrame
功能:表示 binary 帧类型,对应 Opcode 为 %x2 的情形。
CloseWebFrame
CloseWebFrame
功能:表示 close 帧类型,对应 Opcode 为 %x8 的情形。
PingWebFrame
PingWebFrame
功能:表示 ping 帧类型,对应 Opcode 为 %x9 的情形。
PongWebFrame
PongWebFrame
功能:表示 pong 帧类型,对应 Opcode 为 %xA 的情形。
UnknownWebFrame
UnknownWebFrame
功能:表示 其余帧类型,暂不支持,对应 Opcode 为上述值之外的情形。
operator func ==
public override operator func == (that: WebSocketFrameType): Bool
功能:判等。
参数:
- WebSocketFrameType:被比较的对象
返回值:当前实例与 that
相等返回 true
,否则返回 false
operator func !=
public override operator func != (that: WebSocketFrameType): Bool
功能:判不等。
参数:
- WebSocketFrameType:被比较的对象
返回值:当前实例与 that
不等返回 true
,否则返回 false
func toString
public override func toString(): String
功能:获取 WebSocket 帧类型字符串。
返回值:WebSocket 帧类型字符串
enum Protocol
public enum Protocol <: Equatable<Protocol> & ToString {
| HTTP1_0
| HTTP1_1
| HTTP2_0
| UnknownProtocol(String)
}
HTTP 协议类型枚举。
HTTP1_0
HTTP1_0
功能:创建一个 HTTP1_0 类型的枚举实例,表示采用 http/1.0 协议版本。
HTTP1_1
HTTP1_1
功能:创建一个 HTTP1_1 类型的枚举实例,表示采用 http/1.1 协议版本。
HTTP2_0
HTTP2_0
功能:创建一个 HTTP2_0 类型的枚举实例,表示采用 http/2.0 协议版本。
UnknownProtocol
UnknownProtocol(String)
operator func ==
public override operator func == (that: Protocol): Bool
功能:判等。
参数:
- WebSocketFrameType:被比较的对象
返回值:当前实例与 that
相等返回 true
,否则返回 false
operator func !=
public override operator func != (that: Protocol): Bool
功能:判不等。
参数:
- WebSocketFrameType:被比较的对象
返回值:当前实例与 that
不等返回 true
,否则返回 false
func toString
public override func toString(): String
功能:获取 Http 协议版本对象字符串。
返回值:Http 协议版本对象字符串
class HttpHeaders
public class HttpHeaders <: Iterable<(String, Collection<String>)>
此类用于表示 Http 报文中的 header 和 trailer,定义了相关增、删、改、查操作。 header 和 trailer 为键值映射集,由若干 field-line 组成,每一个 field-line 包含一个键 (field -name) 和若干值 (field-value)。 field-name 由 token 字符组成,不区分大小写,在该类中将转为小写保存。 field-value 由 vchar,SP 和 HTAB 组成,vchar 表示可见的 US-ASCII 字符,不得包含前后空格,不得为空值。 详见 rfc 9110 https://www.rfc-editor.org/rfc/rfc9110.html#name-fields 示例: Example-Field: Foo, Bar key: Example-Field, value: Foo, Bar field-name = token token = 1tchar tchar = "!" / "#" / "$" / "%" / "&" / "'" / "" / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA ; any VCHAR, except delimiters
func add
public func add(name: String, value: String): Unit
功能:添加指定键值对 name: value如果 name 已经存在,将在其对应的值列表中添加 value,如果 name 不存在,则添加 name 字段及其值 value。
参数:
- name:HttpHeaders 的字段名称
- value:HttpHeaders 的字段值
异常:
- HttpException:如果传入的name/value包含不合法元素,将抛出此异常
func set
public func set(name: String, value: String): Unit
功能:设置指定键值对 name: value 如果 name 已经存在,传入的 value 将会覆盖之前的值。
参数:
- name:HttpHeaders 的字段名称
- value:HttpHeaders 的字段值
异常:
- HttpException:如果传入的 name/values 包含不合法元素,将抛出此异常
func get
public func get(name: String): Collection<String>
功能:获取指定 name 对应的 value 值。
参数:
- name:字段名称,不区分大小写
返回值:name 对应的 value 集合,如果指定 name 不存在,返回空集合
func getFirst
public func getFirst(name: String): ?String
功能:获取指定 name 对应的第一个 value 值。
参数:
- name:字段名称,不区分大小写
返回值:name 对应的第一个 value 值,如果指定 name 不存在,返回 None
func del
public func del(name: String): Unit
功能:删除指定 name 对应的键值对。
参数:
- name:删除的字段名称
func iterator
public func iterator(): Iterator<(String, Collection<String>)>
功能:获取迭代器,可用于遍历所有键值对。
返回值:该键值集的迭代器
func isEmpty
public func isEmpty(): Bool
功能:判断当前实例是否为空,即没有任何键值对。
返回值:如果当前实例为空,返回 true,否则返回 false
class HttpRequestBuilder
public class HttpRequestBuilder
HttpRequestBuilder 类用于构造 HttpRequest 实例。
init
public init()
功能:构造一个新 HttpRequestBuilder。
init
public init(request: HttpRequest)
功能: 通过 request 构造一个具有 request 属性的 HttpRequestBuilder。由于 body 成员是一个 InputStream,对原始的 request 的 body 的操作会影响到复制得到的 HttpRequest 的 body。HttpRequestBuilder 的 headers 和 trailers 是入参 request 的深拷贝。其余元素都是入参 request 的浅拷贝(因为是不可变对象,无需深拷贝)。
参数:
- request:传入的 HttpRequest 对象
func method
public func method(method: String): HttpRequestBuilder
功能:设置请求 method,默认请求 method 为 "GET"。
参数:
- method:请求方法,必须由 token 字符组成,如果传入空字符串,method 值将自动设置为 "GET"
返回值:当前 HttpRequestBuilder 实例的引用
异常:
- HttpException:参数 method 非法时抛出此异常
func url
public func url(rawUrl: String): HttpRequestBuilder
功能:设置请求 url,默认 url 为空的 URL 对象,即 URL.parse("")。
参数:
- 待解析成 url 对象的字符串,该字符串格式详见 url 包 parse 函数
返回值:当前 HttpRequestBuilder 实例的引用
异常:
- IllegalArgumentException - 当被编码的字符不符合 UTF8 的字节序列规则时,抛出异常
func url
public func url(url: URL): HttpRequestBuilder
功能:设置请求 url,默认 url 为空的 URL 对象,即 URL.parse("")。
返回值:当前 HttpRequestBuilder 实例的引用
func version
public func version(version: Protocol): HttpRequestBuilder
功能:设置请求的 http 协议版本,默认为 UnknownProtocol(""),客户端会根据 tls 配置自动选择协议。
返回值:当前 HttpRequestBuilder 实例的引用
func header
public func header(name: String, value: String): HttpRequestBuilder
功能:向请求 header 添加指定键值对 name: value,规则同 HttpHeaders 类的 add 函数。
返回值:当前 HttpRequestBuilder 实例的引用
异常:
- HttpException:如果传入的 name 或 value 包含不合法元素,将抛出此异常
func addHeaders
public func addHeaders(headers: HttpHeaders): HttpRequestBuilder
功能:向请求 header 添加参数 HttpHeaders 中的键值对。
返回值:当前 HttpRequestBuilder 实例的引用
func setHeaders
public func setHeaders(headers: HttpHeaders): HttpRequestBuilder
功能:设置请求 header,如果已经设置过,调用该函数将替换原 header。
返回值:当前 HttpRequestBuilder 实例的引用
func body
public func body(body: Array<UInt8>): HttpRequestBuilder
功能:设置请求 body,如果已经设置过,调用该函数将替换原 body调用该函数设置请求 body,则 body 将以内置的 InputStream 实现类表示,其大小已知默认请求 body 为空的 InputStream 对象,即其 read 函数总是返回 0body 大小是否已知将影响 HttpRequest 类属性 bodySize 的取值,及客户端发送请求的行为。
返回值:当前 HttpRequestBuilder 实例的引用
func body
public func body(body: String): HttpRequestBuilder
功能:设置请求 body,如果已经设置过,调用该函数将替换原 body调用该函数设置请求 body,则 body 将以内置的 InputStream 实现类表示,其大小已知。
返回值:当前 HttpRequestBuilder 实例的引用
func body
public func body(body: InputStream): HttpRequestBuilder
功能:设置请求 body,如果已经设置过,调用该函数将替换原 body调用该函数设置请求 body,则 body 以用户自定义的 InputStream 实现类表示,其大小未知。
返回值:当前 HttpRequestBuilder 实例的引用
func trailer
public func trailer(name: String, value: String): HttpRequestBuilder
功能:向请求 trailer 添加指定键值对 name: value,规则同 HttpHeaders 类的 add 函数。
返回值:当前 HttpRequestBuilder 实例的引用
异常:
- HttpException:如果传入的 name 或 value 包含不合法元素,将抛出此异常
func addTrailers
public func addTrailers(trailers: HttpHeaders): HttpRequestBuilder
功能:向请求 trailer 添加参数 HttpHeaders 中的键值对。
返回值:当前 HttpRequestBuilder 实例的引用
func setTrailers
public func setTrailers(trailers: HttpHeaders): HttpRequestBuilder
功能:设置请求 trailer,如果已经设置过,调用该函数将替换原 trailer。
返回值:当前 HttpRequestBuilder 实例的引用
func priority
public func priority(urg: Int64, inc: Bool): HttpRequestBuilder
功能:设置 priority 头的便捷函数,调用此函数后,将生成 priority 头,形如:"priority: urgency=x, i"如果通过设置请求头的函数设置了 priority 字段,调用此函数无效。如果多次调用此函数,以最后一次为准。
参数:
- urg:表示请求优先级,取值范围为 0~7,0 表示最高优先级
- inc:表示请求是否需要增量处理,为 true 表示希望服务器并发处理与之同 urg 同 inc 的请求,为 false 表示不希望服务器并发处理
返回值:当前 HttpRequestBuilder 实例的引用
func readTimeout
public func readTimeout(timeout: Duration): HttpRequestBuilder
功能:设置此请求的读超时时间。如果传入的 Duration 为负,则会自动转为 0。如果用户设置了此读超时时间,那么该请求的读超时以此为准;如果用户没有设置,那么该请求的读超时以 Client 为准。
参数:
- timeout:用户设置的此请求的读超时时间
返回值:当前 HttpRequestBuilder 实例的引用
func writeTimeout
public func writeTimeout(timeout: Duration): HttpRequestBuilder
功能:设置此请求的写超时时间。如果传入的 Duration 为负,则会自动转为 0。如果用户设置了此写超时时间,那么该请求的写超时以此为准;如果用户没有设置,那么该请求的写超时以 Client 为准。
参数:
- timeout:用户设置的此请求的写超时时间
返回值:当前 HttpRequestBuilder 实例的引用
func get
public func get(): HttpRequestBuilder
功能:构造 method 为 "GET" 的请求的便捷函数。
返回值:当前 HttpRequestBuilder 实例的引用
func head
public func head(): HttpRequestBuilder
功能:构造 method 为 "HEAD" 的请求的便捷函数。
返回值:当前 HttpRequestBuilder 实例的引用
func options
public func options(): HttpRequestBuilder
功能:构造 method 为 "OPTIONS" 的请求的便捷函数。
func trace
public func trace(): HttpRequestBuilder
功能:构造 method 为 "TRACE" 的请求的便捷函数。
func delete
public func delete(): HttpRequestBuilder
功能:构造 method 为 "DELETE" 的请求的便捷函数。
func post
public func post(): HttpRequestBuilder
功能:构造 method 为 "POST" 的请求的便捷函数。
func put
public func put(): HttpRequestBuilder
功能:构造 method 为 "PUT" 的请求的便捷函数。
func connect
public func connect(): HttpRequestBuilder
功能:构造 method 为 "CONNECT" 的请求的便捷函数。
func build
public func build(): HttpRequest
功能:根据 HttpRequestBuilder 实例生成一个 HttpRequest 实例。
返回值:根据当前 HttpRequestBuilder 实例构造出来的 HttpRequest 实例
class HttpRequest
public class HttpRequest <: ToString
此类为 Http 请求类。
客户端发送请求时,需要构造一个 HttpRequest
实例,再编码成字节报文发出。
服务端处理请求时,需要把收到的请求解析成 HttpRequest
实例,并传给 handler 处理函数。
prop method
public prop method: String
功能:获取 method,如 "GET", "POST",request 实例的 method 无法修改
prop url
public prop url: URL
功能:获取 url,表示客户端访问的 url
prop version
public prop version: Protocol
功能:获取 http 版本,如 HTTP1_1 和 HTTP2_0,request 实例的 version 无法修改
prop headers
public prop headers: HttpHeaders
功能:获取 headers,headers 详述见 HttpHeaders 类,获取后,可通过调用 HttpHeaders 实例成员函数,修改该请求的 headers
prop body
public prop body: InputStream
功能:获取 body 注意
- body 不支持并发读取
- 默认 InputStream 实现类的 read 函数不支持多次读取
prop trailers
public prop trailers: HttpHeaders
功能:获取 trailers,trailers 详述见 HttpHeaders 类,获取后,可通过调用 HttpHeaders 实例成员函数,修改该请求的 trailers
prop bodySize
public prop bodySize: Option<Int64>
功能:获取请求 body 长度
- 如果未设置 body,则 bodySize 为 Some(0)
- 如果 body 长度已知,即通过
Array<UInt8>
或 String 传入 body,或传入的 InputStream 有确定的 length (length >= 0),则 bodySize 为 Some(Int64) - 如果 body 长度未知,即通过用户自定义的 InputStream 实例传入 body 且 InputStream 实例没有确定的 length (length < 0),则 bodySize 为 None
prop form
public prop form: Form
功能:获取请求中的表单信息
- 如果请求方法为 POST,PUT,PATCH,且 content-type 包含 application/x-www-form-urlencoded,获取请求 body 部分,用 form 格式解析
- 如果请求方法不为 POST,PUT,PATCH,获取请求 url 中 query 部分 注意
- 如果用该接口读取了 body,body 已被消费完,后续将无法通过 body.read 读取 body
- 如果 form 不符合 Form 格式,抛 UrlSyntaxException 异常
prop remoteAddr
public prop remoteAddr: String
功能:用于服务端,获取对端地址,即客户端地址,格式为 ip: port,用户无法设置,自定义的 request 对象调用该属性返回 "",服务端 handler 中调用该属性返回客户端地址
prop close
public prop close: Bool
功能:表示该请求 header 是否包含 Connection: close
- 对于服务端,close 为 true 表示处理完该请求应该关闭连接
- 对于客户端,close 为 true 表示如果收到响应后服务端未关闭连接,客户端应主动关闭连接
prop readTimeout
public prop readTimeout: ?Duration
功能:表示该请求的请求级读超时时间,None 表示没有设置;Some(Duration) 表示设置了。
prop writeTimeout
public prop writeTimeout: ?Duration
功能:表示该请求的请求级写超时时间,None 表示没有设置;Some(Duration) 表示设置了。
func toString
public override func toString(): String
功能:把请求转换为字符串,包括 start line,headers,body size,trailers例如:GET /path HTTP/1.1\r\nhost: <www.example.com\r\n\r\nbody> size: 5\r\nbar: foo\r\n。
返回值:请求的字符串表示
class HttpResponseBuilder
public class HttpResponseBuilder
HttpResponseBuilder 类用于构造 HttpResponse 实例。
init
public init()
功能:构造一个新 HttpResponseBuilder。
func version
public func version(version: Protocol): HttpResponseBuilder
功能:设置 http 响应协议版本。
func status
public func status(status: UInt16): HttpResponseBuilder
功能:设置 http 响应状态码。
异常:
- HttpException:如果设置响应状态码不在 100~599 这个区间内,则抛出此异常
func header
public func header(name: String, value: String): HttpResponseBuilder
功能:向响应 header 添加指定键值对 name: value,规则同 HttpHeaders 类的 add 函数。
异常:
- HttpException:如果传入的 name 或 value 包含不合法元素,将抛出此异常
func addHeaders
public func addHeaders (headers: HttpHeaders): HttpResponseBuilder
功能:向响应 header 添加参数 HttpHeaders 中的键值对。
func setHeaders
public func setHeaders (headers: HttpHeaders): HttpResponseBuilder
功能:设置响应 header,如果已经设置过,调用该函数将替换原 header。
func body
public func body(body: Array<UInt8>): HttpResponseBuilder
功能:设置响应 body,如果已经设置过,调用该函数将替换原 body调用该函数设置请求 body,则 body 将以内置的 InputStream 实现类表示,其大小已知默认请求 body 为空的 InputStream 对象,即其 read 函数总是返回 0。
func body
public func body(body: String): HttpResponseBuilder
功能:设置响应 body,如果已经设置过,调用该函数将替换原 body调用该函数设置请求 body,则 body 将以内置的 InputStream 实现类表示,其大小已知。
func body
public func body(body: InputStream): HttpResponseBuilder
功能:设置响应 body,如果已经设置过,调用该函数将替换原 body调用该函数设置请求 body,则 body 以用户自定义的 InputStream 实现类表示,其大小未知。
func trailer
public func trailer(name: String, value: String): HttpResponseBuilder
功能:向响应 trailer 添加指定键值对 name: value,规则同 HttpHeaders 类的 add 函数。
异常:
- HttpException:如果传入的 name 或 value 包含不合法元素,将抛出此异常
func addTrailers
public func addTrailers (trailers: HttpHeaders): HttpResponseBuilder
功能:向响应 trailer 添加参数 HttpHeaders 中的键值对。
func setTrailers
public func setTrailers (trailers: HttpHeaders): HttpResponseBuilder
功能:设置响应 trailer,如果已经设置过,调用该函数将替换原 trailer。
func request
public func request(request: HttpRequest): HttpResponseBuilder
功能:设置响应对应的请求。
func build
public func build(): HttpResponse
功能:根据 HttpResponseBuilder 实例生成一个 HttpResponse 实例。
返回值:根据当前 HttpResponseBuilder 实例构造出来的 HttpResponse 实例
class HttpResponse
public class HttpResponse <: ToString
Http 响应类。
此类定义了 http 中响应 Response 的相关接口,客户端用该类读取服务端返回的响应。
prop version
public prop version: Protocol
功能:获取响应的协议版本,默认值为Http1_1
prop status
public prop status: UInt16
功能:获取响应的状态码,默认值为 200。状态码由 100~599 的三位数字组成,状态码所反映的具体信息可参考 rfc9110 https://httpwg.org/specs/rfc9110.html#status.codes。
prop headers
public prop headers: HttpHeaders
功能:获取 headers,headers 详述见 HttpHeaders 类,获取后,可通过调用 HttpHeaders 实例成员函数,修改该请求的 headers
prop body
public prop body: InputStream
功能:获取 body 注意
- body 不支持并发读取
- 默认 InputStream 实现类的 read 函数不支持多次读取
prop trailers
public prop trailers: HttpHeaders
功能:获取 trailers,trailers 详述见 HttpHeaders 类,获取后,可通过调用 HttpHeaders 实例成员函数,修改该请求的 trailers
prop bodySize
public prop bodySize: Option<Int64>
功能:获取响应 body 长度
- 如果未设置 body,则 bodySize 为 Some(0)
- 如果 body 长度已知,即通过
Array<UInt8>
或 String 传入 body,或传入的 InputStream 有确定的 length (length >= 0),则 bodySize 为 Some(Int64) - 如果 body 长度未知,即通过用户自定义的 InputStream 实例传入 body 且 InputStream 实例没有确定的 length (length < 0),则 bodySize 为 None
prop request
public prop request: Option<HttpRequest>
功能:获取该响应对应的请求,默认为 None
prop close
public prop close: Bool
功能:表示该响应 header 是否包含 Connection: close
- 对于服务端,close 为 true 表示处理完该请求应该关闭连接
- 对于客户端,close 为 true 表示如果收到响应后服务端未关闭连接,客户端应主动关闭连接
func getPush
public func getPush(): Option<ArrayList<HttpResponse>>
功能:获取服务器推送的响应,返回 None 代表未开启服务器推送功能,返回空 ArrayList 代表无服务器推送的响应。
func toString
public override func toString(): String
功能:把响应转换为字符串,包括 status-line,headers,body size, trailers例如:HTTP/1.1 200 OK\r\ncontent-length: 5\r\n\r\nbody size: 5\r\nbar: foo\r\n。
struct HttpStatusCode
public struct HttpStatusCode {
public static const STATUS_CONTINUE = 100
public static const STATUS_SWITCHING_PROTOCOLS = 101
public static const STATUS_PROCESSING = 102
public static const STATUS_EARLY_HINTS = 103
public static const STATUS_OK = 200
public static const STATUS_CREATED = 201
public static const STATUS_ACCEPTED = 202
public static const STATUS_NON_AUTHORITATIVE_INFO = 203
public static const STATUS_NO_CONTENT = 204
public static const STATUS_RESET_CONTENT = 205
public static const STATUS_PARTIAL_CONTENT = 206
public static const STATUS_MULTI_STATUS = 207
public static const STATUS_ALREADY_REPORTED = 208
public static const STATUS_IM_USED = 226
public static const STATUS_MULTIPLE_CHOICES = 300
public static const STATUS_MOVED_PERMANENTLY = 301
public static const STATUS_FOUND = 302
public static const STATUS_SEE_OTHER = 303
public static const STATUS_NOT_MODIFIED = 304
public static const STATUS_USE_PROXY = 305
public static const STATUS_TEMPORARY_REDIRECT = 307
public static const STATUS_PERMANENT_REDIRECT = 308
public static const STATUS_BAD_REQUEST = 400
public static const STATUS_UNAUTHORIZED = 401
public static const STATUS_PAYMENT_REQUIRED = 402
public static const STATUS_FORBIDDEN = 403
public static const STATUS_NOT_FOUND = 404
public static const STATUS_METHOD_NOT_ALLOWED = 405
public static const STATUS_NOT_ACCEPTABLE = 406
public static const STATUS_PROXY_AUTH_REQUIRED = 407
public static const STATUS_REQUEST_TIMEOUT = 408
public static const STATUS_CONFLICT = 409
public static const STATUS_GONE = 410
public static const STATUS_LENGTH_REQUIRED = 411
public static const STATUS_PRECONDITION_FAILED = 412
public static const STATUS_REQUEST_CONTENT_TOO_LARGE = 413
public static const STATUS_REQUEST_URI_TOO_LONG = 414
public static const STATUS_UNSUPPORTED_MEDIA_TYPE = 415
public static const STATUS_REQUESTED_RANGE_NOT_SATISFIABLE = 416
public static const STATUS_EXPECTATION_FAILED = 417
public static const STATUS_TEAPOT = 418
public static const STATUS_MISDIRECTED_REQUEST = 421
public static const STATUS_UNPROCESSABLE_ENTITY = 422
public static const STATUS_LOCKED = 423
public static const STATUS_FAILED_DEPENDENCY = 424
public static const STATUS_TOO_EARLY = 425
public static const STATUS_UPGRADE_REQUIRED = 426
public static const STATUS_PRECONDITION_REQUIRED = 428
public static const STATUS_TOO_MANY_REQUESTS = 429
public static const STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431
public static const STATUS_UNAVAILABLE_FOR_LEGAL_REASONS = 451
public static const STATUS_INTERNAL_SERVER_ERROR = 500
public static const STATUS_NOT_IMPLEMENTED = 501
public static const STATUS_BAD_GATEWAY = 502
public static const STATUS_SERVICE_UNAVAILABLE = 503
public static const STATUS_GATEWAY_TIMEOUT = 504
public static const STATUS_HTTP_VERSION_NOT_SUPPORTED = 505
public static const STATUS_VARIANT_ALSO_NEGOTIATES = 506
public static const STATUS_INSUFFICIENT_STORAGE = 507
public static const STATUS_LOOP_DETECTED = 508
public static const STATUS_NOT_EXTENDED = 510
public static const STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511
}
用以表示网页服务器超文本传输协议响应状态的 3 位数字代码。它由 RFC 9110 规范定义的,并得到 RFC2518、RFC 3229、RFC 4918、RFC 5842、RFC 7168 与 RFC 8297 等规范扩展。所有状态码的第一个数字代表了响应的五种状态之一。
- 状态代码的 1xx(信息)指示在完成请求的操作并发送最终响应之前通信连接状态或请求进度的临时响应;
- 状态代码的 2xx(成功)指示客户端的请求已成功接收、理解和接受;
- 状态代码的 3xx(重定向)指示用户代理需要采取进一步的操作才能完成请求;
- 状态代码的 4xx(客户端错误)指示客户端似乎出错;
- 状态代码的 5xx(服务器错误)指示服务器意识到它出错或无法执行请求的方法;
HttpStatusCode 类结构和主要函数如下:
STATUS_CONTINUE
public static const STATUS_CONTINUE = 100
HTTP 标准状态码 客户端继续发送请求时。这个临时响应是用来通知客户端它的部分请求已经被 服务器接收,且仍未被拒绝。客户端应当继续发送请求的剩余部分,或者如果请 求已经完成,忽略这个响应。服务器必须在请求完成后向客户端发送一个最终响应
STATUS_SWITCHING_PROTOCOLS
public static const STATUS_SWITCHING_PROTOCOLS = 101
服务器已经理解了客户端的请求,并将通过 Upgrade 消息头通知客户端采用不同的 协议来完成这个请求。在发送完这个响应最后的空行后,服务器将会切换到在 Upgrade 消息头中定义的那些协议
STATUS_PROCESSING
public static const STATUS_PROCESSING = 102
处理将被继续执行
STATUS_EARLY_HINTS
public static const STATUS_EARLY_HINTS = 103
提前预加载 (css、js) 文档
STATUS_OK
public static const STATUS_OK = 200
2 开头的状态码,代表请求已成功被服务器接收、理解、并接受,正常状态, 请求已成功,请求所希望的响应头或数据体将随此响应返回
STATUS_CREATED
public static const STATUS_CREATED = 201
请求已经被实现,而且有一个新的资源已经依据请求的需要而建立,且其 URI 已经 随 Location 头信息返回
STATUS_ACCEPTED
public static const STATUS_ACCEPTED = 202
服务器已接受请求,但尚未处理
STATUS_NON_AUTHORITATIVE_INFO
public static const STATUS_NON_AUTHORITATIVE_INFO = 203
服务器已成功处理了请求。返回的实体头部元信息不是在原始服务器上有效的 确定集合,而是来自本地或者第三方的拷贝
STATUS_NO_CONTENT
public static const STATUS_NO_CONTENT = 204
服务器成功处理,但未返回内容
STATUS_RESET_CONTENT
public static const STATUS_RESET_CONTENT = 205
服务器成功处理了请求,且没有返回任何内容,希望请求者重置文档视图
STATUS_PARTIAL_CONTENT
public static const STATUS_PARTIAL_CONTENT = 206
服务器已经成功处理了部分 GET 请求
STATUS_MULTI_STATUS
public static const STATUS_MULTI_STATUS = 207
DAV 绑定的成员已经在(多状态)响应之前的部分被列举,且未被再次包含
STATUS_ALREADY_REPORTED
public static const STATUS_ALREADY_REPORTED = 208
消息体将是一个 XML 消息
STATUS_IM_USED
public static const STATUS_IM_USED = 226
服务器已完成对资源的请求,并且响应是应用于当前实例的一个或多个实例操作的结果 的表示
STATUS_MULTIPLE_CHOICES
public static const STATUS_MULTIPLE_CHOICES = 300
被请求的资源有一系列可供选择的回馈信息,每个都有自己特定的地址和浏览器驱动的 商议信息。用户或浏览器能够自行选择一个首选的地址进行重定向
STATUS_MOVED_PERMANENTLY
public static const STATUS_MOVED_PERMANENTLY = 301
永久移动 请求的资源已被永久的移动到新 URI,返回信息会包括新的 URI,浏览器会自动定向到 新 URI。
STATUS_FOUND
public static const STATUS_FOUND = 302
临时移动 请求的资源已被临时的移动到新 URI,客户端应当继续向原有地址发送以后的请求
STATUS_SEE_OTHER
public static const STATUS_SEE_OTHER = 303
对应当前请求的响应可以在另一个 URL 上被找到,而且客户端应当采用 GET 的 方式访问那个资源
STATUS_NOT_MODIFIED
public static const STATUS_NOT_MODIFIED = 304
请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访 问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
STATUS_USE_PROXY
public static const STATUS_USE_PROXY = 305
使用代理,所请求的资源必须通过代理访问
STATUS_TEMPORARY_REDIRECT
public static const STATUS_TEMPORARY_REDIRECT = 307
临时重定向
STATUS_PERMANENT_REDIRECT
public static const STATUS_PERMANENT_REDIRECT = 308
请求和所有将来的请求应该使用另一个 URI
STATUS_BAD_REQUEST
public static const STATUS_BAD_REQUEST = 400
- 语义有误,当前请求无法被服务器理解
- 请求参数有误
STATUS_UNAUTHORIZED
public static const STATUS_UNAUTHORIZED = 401
当前请求需要用户验证
STATUS_PAYMENT_REQUIRED
public static const STATUS_PAYMENT_REQUIRED = 402
为了将来可能的需求而预留的状态码
STATUS_FORBIDDEN
public static const STATUS_FORBIDDEN = 403
服务器已经理解请求,但是拒绝执行
STATUS_NOT_FOUND
public static const STATUS_NOT_FOUND = 404
请求失败,请求所希望得到的资源未被在服务器上发现
STATUS_METHOD_NOT_ALLOWED
public static const STATUS_METHOD_NOT_ALLOWED = 405
请求行中指定的请求函数不能被用于请求响应的资源
STATUS_NOT_ACCEPTABLE
public static const STATUS_NOT_ACCEPTABLE = 406
请求的资源的内容特性无法满足请求头中的条件,因而无法生成响应实体
STATUS_PROXY_AUTH_REQUIRED
public static const STATUS_PROXY_AUTH_REQUIRED = 407
必须在代理服务器上进行身份验证
STATUS_REQUEST_TIMEOUT
public static const STATUS_REQUEST_TIMEOUT = 408
请求超时。客户端没有在服务器预备等待的时间内完成一个请求的发送
STATUS_CONFLICT
public static const STATUS_CONFLICT = 409
由于和被请求的资源的当前状态之间存在冲突,请求无法完成
STATUS_GONE
public static const STATUS_GONE = 410
被请求的资源在服务器上已经不再可用,而且没有任何已知的转发地址
STATUS_LENGTH_REQUIRED
public static const STATUS_LENGTH_REQUIRED = 411
服务器拒绝在没有定义 Content-Length 头的情况下接受请求
STATUS_PRECONDITION_FAILED
public static const STATUS_PRECONDITION_FAILED = 412
服务器在验证在请求的头字段中给出先决条件时,没能满足其中的一个或多个
STATUS_REQUEST_CONTENT_TOO_LARGE
public static const STATUS_REQUEST_CONTENT_TOO_LARGE = 413
请求提交的实体数据大小超过了服务器愿意或者能够处理的范围
STATUS_REQUEST_URI_TOO_LONG
public static const STATUS_REQUEST_URI_TOO_LONG = 414
请求的 URI 长度超过了服务器能够解释的长度
STATUS_UNSUPPORTED_MEDIA_TYPE
public static const STATUS_UNSUPPORTED_MEDIA_TYPE = 415
服务器无法处理请求附带的媒体格式,对于当前请求的函数和所请求的资源,请求中提 交的实体并不是服务器中所支持的格式
STATUS_REQUESTED_RANGE_NOT_SATISFIABLE
public static const STATUS_REQUESTED_RANGE_NOT_SATISFIABLE = 416
客户端请求的范围无效 请求中包含了 Range 请求头,并且 Range 中指定的任何数据范围都与当前资源的可 用范围不重合同时请求中又没有定义 If-Range 请求头
STATUS_EXPECTATION_FAILED
public static const STATUS_EXPECTATION_FAILED = 417
服务器无法满足 Expect 的请求头信息
STATUS_TEAPOT
public static const STATUS_TEAPOT = 418
服务端无法处理请求,一个愚弄客户端的状态码,被称为“我是茶壶”错误码,不应被认真对待
STATUS_MISDIRECTED_REQUEST
public static const STATUS_MISDIRECTED_REQUEST = 421
请求被指向到无法生成响应的服务器
STATUS_UNPROCESSABLE_ENTITY
public static const STATUS_UNPROCESSABLE_ENTITY = 422
请求格式正确,但是由于含有语义错误,无法响应
STATUS_LOCKED
public static const STATUS_LOCKED = 423
当前资源被锁定
STATUS_FAILED_DEPENDENCY
public static const STATUS_FAILED_DEPENDENCY = 424
由于之前的某个请求发生的错误,导致当前请求失败
STATUS_TOO_EARLY
public static const STATUS_TOO_EARLY = 425
服务器不愿意冒风险来处理该请求
STATUS_UPGRADE_REQUIRED
public static const STATUS_UPGRADE_REQUIRED = 426
客户端应当切换到 TLS/1.0
STATUS_PRECONDITION_REQUIRED
public static const STATUS_PRECONDITION_REQUIRED = 428
客户端发送 HTTP 请求时,必须要满足的一些预设条件
STATUS_TOO_MANY_REQUESTS
public static const STATUS_TOO_MANY_REQUESTS = 429
请求过多
STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE
public static const STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431
请求头字段太大
STATUS_UNAVAILABLE_FOR_LEGAL_REASONS
public static const STATUS_UNAVAILABLE_FOR_LEGAL_REASONS = 451
该请求因法律原因不可用
STATUS_INTERNAL_SERVER_ERROR
public static const STATUS_INTERNAL_SERVER_ERROR = 500
服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理
STATUS_NOT_IMPLEMENTED
public static const STATUS_NOT_IMPLEMENTED = 501
服务器不支持当前请求所需要的某个功能
STATUS_BAD_GATEWAY
public static const STATUS_BAD_GATEWAY = 502
作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应
STATUS_SERVICE_UNAVAILABLE
public static const STATUS_SERVICE_UNAVAILABLE = 503
临时的服务器维护或者过载
STATUS_GATEWAY_TIMEOUT
public static const STATUS_GATEWAY_TIMEOUT = 504
从上游服务器(URI 标识出的服务器,例如 HTTP、FTP、LDAP)或者辅助服务 器(例如 DNS)收到响应超时
STATUS_HTTP_VERSION_NOT_SUPPORTED
public static const STATUS_HTTP_VERSION_NOT_SUPPORTED = 505
服务器不支持,或者拒绝支持在请求中使用的 HTTP 版本
STATUS_VARIANT_ALSO_NEGOTIATES
public static const STATUS_VARIANT_ALSO_NEGOTIATES = 506
服务器存在内部配置错误
STATUS_INSUFFICIENT_STORAGE
public static const STATUS_INSUFFICIENT_STORAGE = 507
服务器无法存储完成请求所必须的内容
STATUS_LOOP_DETECTED
public static const STATUS_LOOP_DETECTED = 508
服务器在处理请求时检测到无限递归
STATUS_NOT_EXTENDED
public static const STATUS_NOT_EXTENDED = 510
获取资源所需要的策略并没有被满足
STATUS_NETWORK_AUTHENTICATION_REQUIRED
public static const STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511
要求网络认证
class HttpResponseWriter
public class HttpResponseWriter {
public HttpResponseWriter(let ctx: HttpContext)
}
HTTP response消息体 Writer,支持用户控制消息体的发送过程。
第一次调用 write 函数时,将立即发送 header 和通过参数传入的 body,此后每次调用 write,发送通过参数传入的 body。 对于 HTTP/1.1,如果设置了 transfer-encoding: chunked,用户每调用一次 write,将发送一个 chunk。 对于 HTTP/2,用户每调用一次 write,将把指定数据封装并发出。
HttpResponseWriter
public HttpResponseWriter(let ctx: HttpContext)
功能:构造一个 HttpResponseWriter 实例
func write
public func write(buf: Array<Byte>): Unit
功能:发送 buf 中数据到客户端。以下情况调用该函数将会抛出 HttpException 若服务端不进行捕捉将返回状态码为500的响应连接升级为 WebSocket ,或连接关闭response 协议版本为 HTTP/1.0若请求方法为 "HEAD" 或响应状态码为 "1XX\204\304"。
class HttpResponsePusher
public class HttpResponsePusher
HTTP/2 服务器推送。
如果服务器收到请求后,认为客户端后续还需要某些关联资源,可以将其提前推送到客户端。 服务端推送包括推送请求和推送响应。 启用服务端推送需要先调用 push 函数发送推送请求,并向服务器注册该请求对应的 handler,用以生成推送响应。 客户端可设置拒绝服务端推送。 不允许嵌套推送,即不允许在推送请求对应的 handler 中再次推送。这种情况下,服务端将不执行推送,并打印日志进行提示。
func getPusher
public static func getPusher(ctx: HttpContext): ?HttpResponsePusher
功能:获取 HttpResponsePusher 实例,如果客户端拒绝推送,将返回 None。
返回值:获得的 HttpResponsePusher
func push
public func push(path: String, method: String, header: HttpHeaders): Unit
功能:向客户端发送推送请求,path 为请求地址,method 为请求方法,header 为请求头。
struct TransportConfig
public struct TransportConfig
传输层配置,对应服务器调用serve()之后的建立连接传输层配置。
prop readTimeout
public mut prop readTimeout: Duration
功能:设定和读取传输层 socket 的读取时间,如果设置的时间小于 0 将置为 0,默认值为 Duration.Max。
prop writeTimeout
public mut prop writeTimeout: Duration
功能:设定和读取传输层 socket 的写入时间,如果设置的时间为负将置为 0,默认值为 Duration.Max。
prop writeBufferSize
public mut prop writeBufferSize: ?Int64
功能:设定和读取传输层 socket 的写缓冲区大小,默认值为 None ,若设置的值小于 0,将在服务器进行服务建立连接后抛出 IllegalArgumentException。
prop readBufferSize
public mut prop readBufferSize: ?Int64
功能:设定和读取传输层 socket 的读缓冲区大小,默认值为 None ,若设置的值小于 0,将在服务器进行服务建立连接后抛出 IllegalArgumentException。
prop keepAliveConfig
public mut prop keepAliveConfig: SocketKeepAliveConfig
功能:设定和读取传输层 socket 的消息保活配置,默认配置空闲时间为 45s,发送探测报文的时间间隔为 5s,在连接被认为无效之前发送的探测报文数 5 次,实际时间粒度可能因操作系统而异。
class Cookie
public class Cookie
HTTP 本身是无状态的,server 为了知道 client 的状态,提供个性化的服务,便可以通过 Cookie 来维护一个有状态的会话。
用户首次访问某站点时,server 通过 Set-Cookie header 将 name/value 对,以及 attribute-value 传给用户代理;用户代理随后的对该站点的请求中便可以将 name/value 包含进 Cookie header 中传给 server。
Cookie 类提供了构建 Cookie 对象,并将 Cookie 对象转成 Set-Cookie header 值的函数,提供了获取 Cookie 对象各属性值的函数。
Cookie 的各个属性的要求和作用见 RFC 6265。
prop cookieName
public prop cookieName: String
功能:获取 Cookie 对象的 cookie-name 值。
注意:cookie-name,cookie-value,expires-av 等名字采用 RFC 6265 中的名字,详情请见协议。
prop cookieValue
public prop cookieValue: String
功能:获取 Cookie 对象的 cookie-value 值。
prop expires
public prop expires: ?DateTime
功能:获取 Cookie 对象的 expires-av 值。
prop maxAge
public prop maxAge: ?Int64
功能:获取 Cookie 对象的 max-age-av 值。
prop domain
public prop domain: String
功能:获取 Cookie 对象的 domain-av 值。
prop path
public prop path: String
功能:获取 Cookie 对象的 path-av 值。
prop secure
public prop secure: Bool
功能:获取 Cookie 对象的 secure-av 值。
prop httpOnly
public prop httpOnly: Bool
功能:获取 Cookie 对象的 httpOnly-av 值。
prop others
public prop others: ArrayList<String>
功能:获取未被解析的属性。
func toSetCookieString
public func toSetCookieString(): String
功能:提供将 Cookie 转成字符串形式的函数,方便 server 设置 Set-Cookie header说明:服务器设置 Set-Cookie header 时,可以先创建一个 Cookie 对象,再对其调用该函数获得 Set-Cookie value,将 Set-Cookie value 设置为 Set-Cookie header 的值并发送给客户端注意:
- Cookie 各属性(包含 name,value)在对象创建时就被检查了,因此对 Cookie 对象调用 toSetCookieString 函数不会产生异常
- Cookie 必需的属性是 cookie-pair 即 cookie-name "=" cookie-value,cookie-value 可以为空字符串,toSetCookieString 函数只会将设置过的属性写入字符串,即只有 "cookie-name=" 是必有的,其余部分是否存在取决于是否设置。
返回值:字符串对象,用于设置 Set-Cookie header
init
public init(name: String, value: String, expires!: ?DateTime = None, maxAge!: ?Int64 = None,
domain!: String = "", path!: String = "", secure!: Bool = false, httpOnly!: Bool = false)
功能:提供 Cookie 对象的公开构造器说明:该构造器会检查传入的各项属性是否满足协议要求,如果不满足则会产生 IllegalArgumentException。 具体要求见 RFC 6265 4.1.1.
注意:Cookie 各属性中只有 cookie-name,cookie-value 是必需的,必须传入 name,value 参数,但 value 参数可以传入空字符串。
参数:
- name:cookie-name 属性
name = token
token = 1*tchar
tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
/ "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
/ DIGIT / ALPHA
- value:cookie-value 属性
value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )
cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
; US-ASCII characters excluding CTLs,
; whitespace DQUOTE, comma, semicolon,
; and backslash
- expires:设置 Cookie 的过期时间,默认为 None,时间必须在 1601 年之后
- maxAge:Cookie 的最大生命周期,默认为 None,如果 Cookie 既有 expires 属性,也有 maxAge,则表示该 Cookie 只维护到会话结束(维护到 Client 关闭之前,Client 关闭之后设置了过期的 Cookie 也不再维护)
max-age-av = "Max-Age=" non-zero-digit *DIGIT
non-zero-digit = %x31-39
; digits 1 through 9
DIGIT = %x30-39
; digits 0 through 9
- domain:默认为空字符串,表示该收到该 Cookie 的客户端只会发送该 Cookie 给原始服务器。如果设置了合法的 domain,则收到该 Cookie 的客户端只会发送该 Cookie 给所有该 domain 的子域(且满足其他属性条件要求才会发)
domain = <subdomain> | " "
<subdomain> ::= <label> | <subdomain> "." <label>
<label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
<ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
<let-dig-hyp> ::= <let-dig> | "-"
<let-dig> ::= <letter> | <digit>
<letter> ::= any one of the 52 alphabetic characters A through Z in upper case and a through z in lower case
<digit> ::= any one of the ten digits 0 through 9
RFC 1035 2.3.1.
而 RFC 1123 2.1. 放松了对 label 首字符必须是 letter 的限制
因此,对 domain 的要求为:
1、总长度小于等于 255,由若干个 label 组成
2、label 与 label 之间通过 "." 分隔,每个 label 长度小于等于 63
3、label 的开头和结尾必须是数字或者字母,label 的中间字符必须是数字、字母或者 "-"
- path:默认为空字符串,客户端会根据 url 计算出默认的 path 属性,见 RFC 6265 5.1.4. 收到该 Cookie 的客户端只会发送该 Cookie 给所有该 path 的子目录(且满足其他属性条件要求才会发)
path = <any CHAR except CTLs or ";">
CHAR = <any [USASCII] character>
CTLs = <controls>
- secure:默认为 false,如果设置为 true,该 Cookie 只会在安全协议请求中发送
- httpOnly:默认为 false,如果设置为 true,该 Cookie 只会在 HTTP 协议请求中发送
返回值:构造的 Cookie 对象
异常:
- IllegalArgumentException:传入的参数不符合协议要求
interface CookieJar
public interface CookieJar {
prop rejectPublicSuffixes: ArrayList<String>
prop isHttp: Bool
func storeCookies(url: URL, cookies: ArrayList<Cookie>): Unit
func getCookies(url: URL): ArrayList<Cookie>
func removeCookies(domain: String): Unit
func clear(): Unit
static func toCookieString(cookies: ArrayList<Cookie>): String
static func parseSetCookieHeader(response: HttpResponse): ArrayList<Cookie>
static func createDefaultCookieJar(rejectPublicSuffixes: ArrayList<String>, isHttp: Bool): CookieJar
}
CookieJar 是 Client 用来管理 Cookie 的工具,其有两个静态函数,toCookieString 用于将 ArrayList<Cookie>
转成字符串以便设置请求的 Cookie header;
parseSetCookieHeader 用于解析收到 response 中的 Set-Cookie header;如果 Client 配置了 CookieJar,那么 Cookie 的解析收发都是自动的;
用户可以实现自己的 CookieJar,实现自己的管理逻辑。
createDefaultCookieJar 函数返回一个默认的 CookieJar 实例。
CookieJar 的管理要求见 RFC 6265。
prop rejectPublicSuffixes
prop rejectPublicSuffixes: ArrayList<String>
功能:获取 public suffixes 配置,该配置是一个 domain 黑名单,会拒绝 domain 值为 public suffixes 的 Cookie (除非该 Cookie 来自于与 domain 相同的 host) public suffixes 见 https://publicsuffix.org/
prop isHttp
prop isHttp: Bool
功能:该 CookieJar 是否用于 HTTP 协议 isHttp 为 true 则只会存储来自于 HTTP 协议的 Cookie isHttp 为 false 则只会存储来自非 HTTP 协议的 Cookie,且不会存储发送设置了 httpOnly 的 Cookie
func storeCookies
func storeCookies(url: URL, cookies: ArrayList<Cookie>): Unit
功能:将 ArrayList<Cookie>
存进 CookieJar
说明:默认实现的 cookieJarImpl 中的 Cookie 是以 CookieEntry (包访问权限) 对象的形式存进 CookieJar 中的,具体要求见 RFC 6265 5.3.,如果往 CookieJar 中存 Cookie 时超过了上限(3000 条),那么至少清除 CookieJar 中 1000 条 Cookie 再往里存清除 CookieJar 中 Cookie 的优先级见 RFC 6265 5.3.12.
- 过期的 Cookie
- 相同 domain 中超过 50 条以上的部分
- 所有 Cookie具有相同优先级的 Cookie 则优先删除 last-access 属性更早的。
参数:
- url:产生该 Cookie 的 url
- cookies:需要存储的
ArrayList<Cookie>
func getCookies
func getCookies(url: URL): ArrayList<Cookie>
功能:从 CookieJar 中取出 ArrayList<Cookie>
说明:默认实现 cookieJarImpl 的取 ArrayList<Cookie>
函数的具体要求见 RFC 6265 5.4.,对取出的 ArrayList<Cookie>
调用 toCookieString 可以将取出的 ArrayList<Cookie>
转成 Cookie header 的 value 字符串。
参数:
- url:所要取出
ArrayList<Cookie>
的 url
返回值:CookieJar 中存储的对应此 url 的 ArrayList<Cookie>
func removeCookies
func removeCookies(domain: String): Unit
功能:从 CookieJar 中移除某个 domain 的 Cookie说明:默认实现 CookieJarImpl 的移除某个 domain 的 Cookie 只会移除特定 domain 的 Cookie,domain 的 subdomain 的 Cookie 并不会移除。
参数:
- domain:所要移除 Cookie 的域名
异常:
- IllegalArgumentException:如果传入的 domain 为空字符串或者非法,则抛出该异常,合法的 domain 规则见 Cookie 的参数文档
func clear
func clear(): Unit
功能:清除全部 Cookie说明:默认实现 CookieJarImpl 会清除 CookieJar 中的所有 Cookie。
func toCookieString
static func toCookieString(cookies: ArrayList<Cookie>): String
功能:静态函数,将 ArrayList<Cookie>
转成字符串,用于 Cookie header说明:该函数会将传入的 ArrayList<Cookie>
数组转成协议规定的 Cookie header 的字符串形式,见 RFC 6265 5.4.4.。
参数:
- cookies:所需转成 Cookie header 字符串的
ArrayList<Cookie>
返回值:用于 Cookie header 的字符串
func parseSetCookieHeader
static func parseSetCookieHeader(response: HttpResponse): ArrayList<Cookie>
功能:静态函数,解析 response 中的 Set-Cookie header说明:该函数解析 response 中的 Set-Cookie header,并返回解析出的 ArrayList<Cookie>
,解析 Set-Cookie header 的具体规则见 RFC 6265 5.2.。
参数:
- response:所需要解析的 response
返回值:从 response 中解析出的 ArrayList<Cookie>
数组
func createDefaultCookieJar
static func createDefaultCookieJar(rejectPublicSuffixes: ArrayList<String>, isHttp: Bool): CookieJar
功能:静态函数,构建默认的管理 Cookie 的 CookieJar 实例说明:默认的 CookieJar 的管理要求参考 RFC 6265 5.3.isHttp 为 false 则只会存储来自非 HTTP 协议的 Cookie,且不会存储发送设置了 httpOnly 的 Cookie。
参数:
- rejectPublicSuffixes:用户配置的 public suffixes,Cookie 管理为了安全会拒绝 domain 值为 public suffixes 的 cookie(除非该 Cookie 来自于与 domain 相同的 host),public suffixes 见 https://publicsuffix.org/
- isHttp:该 CookieJar 是否用于 HTTP 协议,isHttp 为 true 则只会存储来自于 HTTP 协议的 Cookie;
返回值:默认的 CookieJar 实例
class HttpException
public class HttpException <: Exception
Http 的异常类。
class HttpTimeoutException
public class HttpTimeoutException <: Exception
class HttpStatusException
public class HttpStatusException <: Exception
class WebSocketException
public class WebSocketException <: Exception
class ConnectionException
public class ConnectionException <: Exception {
public init(message: String)
}
init
public init(message: String)
参数:
- message:异常提示信息
异常:
- 类初始化函数
class CoroutinePoolRejectException
public class CoroutinePoolRejectException <: Exception {
public init(message: String)
}
init
public init(message: String)
参数:
- message:异常提示信息
异常:
- 异常类初始化函数
示例
server
Hello 仓颉
from net import http.ServerBuilder
main () {
// 1. 构建 Server 实例
let server = ServerBuilder()
.addr("127.0.0.1")
.port(8080)
.build()
// 2. 注册 HttpRequestHandler
server.distributor.register("/index", {httpContext =>
httpContext.responseBuilder.body("Hello 仓颉!")
})
// 3. 启动服务
server.serve()
}
通过 request distributor注册处理器
from net import http.{ServerBuilder, HttpRequestHandler, FuncHandler}
main () {
// 1. 构建 Server 实例
let server = ServerBuilder()
.addr("127.0.0.1")
.port(8080)
.build()
var a: HttpRequestHandler = FuncHandler({ httpContext =>
httpContext.responseBuilder.body("index")
})
var b: HttpRequestHandler = FuncHandler({ httpContext =>
httpContext.responseBuilder.body("id")
})
var c: HttpRequestHandler = FuncHandler({ httpContext =>
httpContext.responseBuilder.body("help")
})
server.distributor.register("/index", a)
server.distributor.register("/id", b)
server.distributor.register("/help", c)
// 2. 启动服务
server.serve()
}
自定义 request distributor与处理器
from net import http.*
from std import collection.HashMap
class NaiveDistributor <: HttpRequestDistributor {
let map = HashMap<String, HttpRequestHandler>()
public func register(path: String, handler: HttpRequestHandler): Unit {
map.put(path, handler)
}
public func distribute(path: String): HttpRequestHandler {
if (path == "/index") {
return PageHandler()
}
return NotFoundHandler()
}
}
// 返回一个简单的 HTML 页面
class PageHandler <: HttpRequestHandler {
public func handle(httpContext: HttpContext): Unit {
httpContext.responseBuilder.body(b"<html></html>")
}
}
class NotFoundHandler <: HttpRequestHandler {
public func handle(httpContext: HttpContext): Unit {
httpContext.responseBuilder
.status(404)
.body(b"404 Not Found")
}
}
main () {
// 1. 构建 Server 实例
let server = ServerBuilder()
.addr("127.0.0.1")
.port(8080)
.distributor(NaiveDistributor()) // 自定义分发器
.build()
// 2. 启动服务
server.serve()
}
自定义 server 网络配置
from std import fs.*
from net import tls.*
from crypto import x509.{X509Certificate, PrivateKey}
from net import http.*
//该程序需要用户配置存在且合法的文件路径才能执行
main () {
// 1. 自定义配置
// tcp 配置
var transportCfg = TransportConfig()
transportCfg.readBufferSize = 8192
// tls 配置 需要传入配套的证书与私钥文件路径
let pem0 = String.fromUtf8(File("/certPath", OpenOption.Open(true, false)).readToEnd())
let pem02 = String.fromUtf8(File("/keyPath", OpenOption.Open(true, false)).readToEnd())
var tlsConfig = TlsServerConfig(X509Certificate.decodeFromPem(pem0), PrivateKey.decodeFromPem(pem02))
tlsConfig.supportedAlpnProtocols = ["h2"]
// 2. 构建 Server 实例
let server = ServerBuilder()
.addr("127.0.0.1")
.port(8080)
.transportConfig(transportCfg)
.tlsConfig(tlsConfig)
.headerTableSize(10 * 1024)
.maxRequestHeaderSize(1024 * 1024)
.build()
// 3. 注册 HttpRequestHandler
server.distributor.register("/index", {httpContext =>
httpContext.responseBuilder.body("Hello 仓颉!")
})
// 4. 启动服务
server.serve()
}
response中的 chunked与trailer
from net import http.*
from std import io.*
from std import collection.HashMap
func checksum(chunk: Array<UInt8>): Int64 {
var sum = 0
for (i in chunk) {
if (i == b'\n') {
sum += 1
}
}
return sum / 2
}
main () {
// 1. 构建 Server 实例
let server = ServerBuilder()
.addr("127.0.0.1")
.port(8080)
.build()
// 2. 注册 HttpRequestHandler
server.distributor.register("/index", {httpContext =>
let responseBuilder = httpContext.responseBuilder
responseBuilder.header("transfer-encoding", "chunked") // 设置response头
responseBuilder.header("trailer", "checkSum")
let writer = HttpResponseWriter(httpContext)
var sum = 0
for (_ in 0..10) {
let chunk = Array<UInt8>(10, item: 0)
sum += checksum(chunk)
writer.write(chunk) // 立即发送
}
responseBuilder.trailer("checkSum", "${sum}") // handler结束后发送
})
// 3. 启动服务
server.serve()
}
处理重定向 request
from net import http.*
main () {
// 1. 构建 Server 实例
let server = ServerBuilder()
.addr("127.0.0.1")
.port(8080)
.build()
// 2. 注册 HttpRequestHandler
server.distributor.register("/redirecta",RedirectHandler("/movedsource", 308))
server.distributor.register("/redirectb",RedirectHandler("http://www.example.com", 308))
// 3. 启动服务
server.serve()
}
tls 证书热加载
from std import fs.*
from net import tls.*
from crypto import x509.{X509Certificate, PrivateKey}
from net import http.*
//该程序需要用户配置存在且合法的文件路径才能执行
main() {
// 1. tls 配置
let pem0 = String.fromUtf8(File("/certPath", OpenOption.Open(true, false)).readToEnd())
let pem02 = String.fromUtf8(File("/keyPath", OpenOption.Open(true, false)).readToEnd())
var tlsConfig = TlsServerConfig(X509Certificate.decodeFromPem(pem0), PrivateKey.decodeFromPem(pem02))
tlsConfig.supportedAlpnProtocols = ["http/1.1"]
let pem = String.fromUtf8(File("/rootCerPath", OpenOption.Open(true, false)).readToEnd())
tlsConfig.verifyMode = CustomCA(X509Certificate.decodeFromPem(pem))
// 2. 构建 Server 实例,并启动服务
let server = ServerBuilder()
.addr("127.0.0.1")
.port(8080)
.tlsConfig(tlsConfig)
.build()
spawn {
server.serve()
}
// 3. 更新 tls 证书和私钥,之后收到的 request将使用新的证书和私钥
server.updateCert("/newCerPath", "/newKeyPath")
// 4. 更新 CA, 双向认证时使用,之后收到的 request将使用新的CA
server.updateCA("/newRootCerPath")
}
client
Hello World
from net import http.*
main () {
// 1. 构建 client 实例
let client = ClientBuilder().build()
// 2. 发送 request
let rsp = client.get("http://example.com/hello")
// 3. 读取response
println(rsp)
// 4. 关闭连接
client.close()
}
自定义 client 网络配置
from std import socket.TcpSocket
from std import convert.Parsable
from std import fs.*
from net import tls.*
from crypto import x509.X509Certificate
from net import http.*
//该程序需要用户配置存在且合法的文件路径才能执行
main () {
// 1. 自定义配置
// tls 配置
var tlsConfig = TlsClientConfig()
let pem = String.fromUtf8(File("/rootCerPath", OpenOption.Open(true, false)).readToEnd())
tlsConfig.verifyMode = CustomCA(X509Certificate.decodeFromPem(pem))
tlsConfig.alpnProtocolsList = ["h2"]
// connector
let TcpSocketConnector = { authority: String =>
let words = authority.split(":")
let addr = words[0]
let port = UInt16.parse(words[1])
let socket = TcpSocket(addr, port)
socket.connect()
return socket
}
// 2. 构建 client 实例
let client = ClientBuilder()
.tlsConfig(tlsConfig)
.enablePush(false)
.connector(TcpSocketConnector)
.build()
// 3. 发送 request
let rsp = client.get("https://example.com/hello")
// 4. 读取response
let buf = Array<UInt8>(1024, item: 0)
let len = rsp.body.read(buf)
println(String.fromUtf8(buf.slice(0, len)))
// 5. 关闭连接
client.close()
}
request中的 chunked 与 trailer
from std import io.*
from std import fs.*
from net import http.*
func checksum(chunk: Array<UInt8>): Int64 {
var sum = 0
for (i in chunk) {
if (i == b'\n') {
sum += 1
}
}
return sum / 2
}
//该程序需要用户配置存在且合法的文件路径才能执行
main () {
// 1. 构建 client 实例
let client = ClientBuilder().build()
var requestBuilder = HttpRequestBuilder()
let file = File("./res.jpg", OpenOption.Open(true, false))
let sum = checksum(file.readToEnd())
let req = requestBuilder
.method("PUT")
.url("https://example.com/src/")
.header("Transfer-Encoding","chunked")
.header("Trailer","checksum")
.body(FileBody("./res.jpg"))
.trailer("checksum", sum.toString())
.build()
let rsp = client.send(req)
println(rsp)
client.close()
}
class FileBody <: InputStream {
var file: File
init(path: String) { file = File(path, OpenOption.Open(true, false))}
public func read(buf: Array<UInt8>): Int64 {
file.read(buf)
}
}
配置代理
from net import http.*
main () {
// 1. 构建 client 实例
let client = ClientBuilder()
.httpProxy("http://192.168.0.1:8080")
.build()
// 2. 发送 request,所有 request都会被发送至192.168.0.1地址的8080端口,而不是example.com
let rsp = client.get("http://example.com/hello")
// 3. 读取response
println(rsp)
// 4. 关闭连接
client.close()
}
cookie
Client
from net import http.*
from encoding import url.*
from std import socket.*
from std import time.*
from std import sync.*
main() {
// 1、启动socket服务器
let serverSocket = TcpServerSocket(bindAt: 0)
serverSocket.bind()
let fut = spawn {
serverPacketCapture(serverSocket)
}
sleep(Duration.millisecond * 10)
// 客户端一般从 response 中的 Set-Cookie header 中读取 cookie,并将其存入 cookieJar 中,
// 下次发起 request时,将其放在 request 的 Cookie header 中发送
// 2、启动客户端
let client = ClientBuilder().build()
let port = serverSocket.localAddress.port
var u = URL.parse("http://127.0.0.1:${port}/a/b/c")
var r = HttpRequestBuilder()
.url(u)
.build()
// 3、发送request
client.send(r)
sleep(Duration.second * 2)
r = HttpRequestBuilder()
.url(u)
.build()
// 4、发送新 request,从 CookieJar 中取出 cookie,并转成 Cookie header 中的值
// 此时 cookie 2=2 已经过期,因此只发送 1=1 cookie
client.send(r)
// 5、关闭客户端
client.close()
fut.get()
serverSocket.close()
}
func serverPacketCapture(serverSocket: TcpServerSocket) {
let buf = Array<UInt8>(500, item: 0)
let server = serverSocket.accept()
var i = server.read(buf)
println(String.fromUtf8(buf[..i]))
// GET /a/b/c HTTP/1.1
// host: 127.0.0.1:44649
// user-agent: CANGJIEUSERAGENT_1_1
// connection: keep-alive
// content-length: 0
//
// 过期时间为 4 秒的 cookie1
let cookie1 = Cookie("1", "1", maxAge: 4, domain: "127.0.0.1", path: "/a/b/")
let setCookie1 = cookie1.toSetCookieString()
// 过期时间为 2 秒的 cookie2
let cookie2 = Cookie("2", "2", maxAge: 2, path: "/a/")
let setCookie2 = cookie2.toSetCookieString()
// 服务器发送 Set-Cookie 头,客户端解析并将其存进 CookieJar 中
server.write("HTTP/1.1 204 ok\r\nSet-Cookie: ${setCookie1}\r\nSet-Cookie: ${setCookie2}\r\nConnection: close\r\n\r\n".toArray())
let server2 = serverSocket.accept()
i = server2.read(buf)
// 接收客户端的带 cookie 的请求
println(String.fromUtf8(buf[..i]))
// GET /a/b/c HTTP/1.1
// host: 127.0.0.1:34857
// cookie: 1=1
// user-agent: CANGJIEUSERAGENT_1_1
// connection: keep-alive
// content-length: 0
//
server2.write("HTTP/1.1 204 ok\r\nConnection: close\r\n\r\n".toArray())
server2.close()
}
Server
from net import http.*
main () {
// 服务器设置 cookie 时将 cookie 放在 Set-Cookie header 中发给客户端
// 1. 构建 Server 实例
let server = ServerBuilder()
.addr("127.0.0.1")
.port(8080)
.build()
// 2. 注册 HttpRequestHandler
server.distributor.register("/index", {httpContext =>
let cookie = Cookie("name", "value")
httpContext.responseBuilder.header("Set-Cookie", cookie.toSetCookieString()).body("Hello 仓颉!")
})
// 3. 启动服务
server.serve()
}
server push
仅用于 HTTP/2
client:
from std import fs.*
from std import collection.ArrayList
from net import tls.*
from crypto import x509.X509Certificate
from net import http.*
//该程序需要用户配置存在且合法的文件路径才能执行
// client:
main() {
// 1. tls 配置
var tlsConfig = TlsClientConfig()
let pem = String.fromUtf8(File("/rootCerPath", OpenOption.Open(true, false)).readToEnd())
tlsConfig.verifyMode = CustomCA(X509Certificate.decodeFromPem(pem))
tlsConfig.alpnProtocolsList = ["h2"]
// 2. 构建 Client 实例
let client = ClientBuilder()
.tlsConfig(tlsConfig)
.build()
// 3. 发送 request,收response
let response = client.get("https://example.com/index.html")
// 4. 收 pushResponse,此例中相当于 client.get("http://example.com/picture.png") 的response
let pushResponses: Option<ArrayList<HttpResponse>> = response.getPush()
client.close()
}
server:
from std import fs.*
from net import tls.*
from crypto import x509.{X509Certificate, PrivateKey}
from net import http.*
//该程序需要用户配置存在且合法的文件路径才能执行
main() {
// 1. tls 配置
let pem0 = String.fromUtf8(File("/certPath", OpenOption.Open(true, false)).readToEnd())
let pem02 = String.fromUtf8(File("/keyPath", OpenOption.Open(true, false)).readToEnd())
var tlsConfig = TlsServerConfig(X509Certificate.decodeFromPem(pem0), PrivateKey.decodeFromPem(pem02))
tlsConfig.supportedAlpnProtocols = ["h2"]
// 2. 构建 Server 实例
let server = ServerBuilder()
.addr("127.0.0.1")
.port(8080)
.tlsConfig(tlsConfig)
.build()
// 3. 注册原 request 的 handler
server.distributor.register("/index.html", {httpContext =>
let pusher = HttpResponsePusher.getPusher(httpContext)
match (pusher) {
case Some(pusher) =>
pusher.push("/picture.png", "GET", httpContext.request.headers)
case None =>
()
}
})
// 4. 注册 pushRequest 的 handler
server.distributor.register("/picture.png", {httpContext =>
httpContext.responseBuilder.body(b"picture.png")
})
// 4. 启动服务
server.serve()
}
webSocket
from net import http.*
from encoding import url.*
from std import time.*
from std import sync.*
from std import collection.*
from std import log.*
let server = ServerBuilder()
.addr("127.0.0.1")
.port(0)
.build()
// client:
main() {
// 1 启动服务器
spawn { startServer() }
sleep(Duration.millisecond * 200)
let client = ClientBuilder().build()
let u = URL.parse("ws://127.0.0.1:${server.port}/webSocket")
let subProtocol = ArrayList<String>(["foo1", "bar1"])
let headers = HttpHeaders()
headers.add("test", "echo")
// 2 完成 WebSocket 握手,获取 WebSocket 实例
let websocket: WebSocket
let respHeaders: HttpHeaders
(websocket, respHeaders) = WebSocket.upgradeFromClient(client, u, subProtocols: subProtocol, headers: headers)
client.close()
println("subProtocol: ${websocket.subProtocol}") // fool1
println(respHeaders.getFirst("rsp") ?? "") // echo
// 3 消息收发
// 发送 hello
websocket.write(TextWebFrame, b"hello")
// 收
let data = ArrayList<UInt8>()
var frame = websocket.read()
while(true) {
match(frame.frameType) {
case ContinuationWebFrame =>
data.appendAll(frame.payload)
if (frame.fin) {
break
}
case TextWebFrame | BinaryWebFrame =>
if (!data.isEmpty()) {
throw Exception("invalid frame")
}
data.appendAll(frame.payload)
if (frame.fin) {
break
}
case CloseWebFrame =>
websocket.write(CloseWebFrame, frame.payload)
break
case PingWebFrame =>
websocket.writePongFrame(frame.payload)
case _ => ()
}
frame = websocket.read()
}
println("data size: ${data.size}") // 4097
println("last item: ${String.fromUtf8(Array(data)[4096])}") // a
// 4 关闭 websocket,
// 收发 CloseFrame
websocket.writeCloseFrame(status: 1000)
let websocketFrame = websocket.read()
println("close frame type: ${websocketFrame.frameType}") // CloseWebFrame
println("close frame payload: ${websocketFrame.payload}") // 3, 232
// 关闭底层连接
websocket.closeConn()
server.close()
}
func startServer() {
// 1 注册 handler
server.distributor.register("/webSocket", handler1)
server.logger.level = OFF
server.serve()
}
// server:
func handler1(ctx: HttpContext): Unit {
// 2 完成 websocket 握手,获取 websocket 实例
let websocketServer = WebSocket.upgradeFromServer(ctx, subProtocols: ArrayList<String>(["foo", "bar", "foo1"]),
userFunc: {request: HttpRequest =>
let value = request.headers.getFirst("test") ?? ""
let headers = HttpHeaders()
headers.add("rsp", value)
headers
})
// 3 消息收发
// 收 hello
let data = ArrayList<UInt8>()
var frame = websocketServer.read()
while(true) {
match(frame.frameType) {
case ContinuationWebFrame =>
data.appendAll(frame.payload)
if (frame.fin) {
break
}
case TextWebFrame | BinaryWebFrame =>
if (!data.isEmpty()) {
throw Exception("invalid frame")
}
data.appendAll(frame.payload)
if (frame.fin) {
break
}
case CloseWebFrame =>
websocketServer.write(CloseWebFrame, frame.payload)
break
case PingWebFrame =>
websocketServer.writePongFrame(frame.payload)
case _ => ()
}
frame = websocketServer.read()
}
println("data: ${String.fromUtf8(Array(data))}") // hello
// 发 4097 个 a
websocketServer.write(TextWebFrame, Array<UInt8>(4097, item: 97))
// 4 关闭 websocket,
// 收发 CloseFrame
let websocketFrame = websocketServer.read()
println("close frame type: ${websocketFrame.frameType}") // CloseWebFrame
println("close frame payload: ${websocketFrame.payload}") // 3, 232
websocketServer.write(CloseWebFrame, websocketFrame.payload)
// 关闭底层连接
websocketServer.closeConn()
}
运行结果如下:
subProtocol: foo1
echo
data: hello
data size: 4097
last item: a
close frame type: CloseWebFrame
close frame payload: [3, 232]
close frame type: CloseWebFrame
close frame payload: [3, 232]
log
from std import log.*
from net import http.*
main () {
// 1. 构建 Server 实例
let server = ServerBuilder()
.addr("127.0.0.1")
.port(8080)
.build()
// 2. 注册 HttpRequestHandler
server.distributor.register("/index", {httpContext =>
httpContext.responseBuilder.body("Hello 仓颉!")
})
// 3. 开启日志
server.logger.level = DEBUG
// client端通过client.logger.level = DEBUG 开启
// 4. 启动服务
server.serve()
}
运行结果如下所示:
2024/01/25 17:23:54.344205 DEBUG Logger [Server#serve] bindAndListen(127.0.0.1, 8080)