HttpFeatures

Feng Gao
8 min readAug 8, 2021

1 Overviews

我们都知道 Asp.NET Core 应用程序就是对 HttpContext 对象的处理,但是我们可以为 ASP.NET Core 选择不同的服务器,所以不同的服务器与 HttpContext 之间的适配就成了主要的问题。因此 Asp.NET Core 提供了 IFeature 这样的通用的接口,任何服务器只要实现了这样的接口,就可以直接构造出 HttpContext 这样的对象。

接下来我们看一看都有哪些 Features

2 Features

2.1 IHttpConnectionFeature

它代表了一个 HTTP 连接下层的 TCP/IP (Socket) 来连接,我们都知道一个 TCP 连接是四元组 (源IP,源端口,目标IP,目标端口) ,同时还有一个 id 用来表示当前的连接

2.2 IHttpRequestFeature

表示一个 Http Request 请求. protocol 表明是 HTTP 的版本; Schema 说明是 http 还是 https ; Method 表明是 GET , POST 等请求方法;

假设我们有这样一个请求地址

https://localhost:80/api/rollout/456?detail=true
  • PathBase: /api
  • Path: /api/rollout/456
  • QueryString: ?detail=true
  • RawTarget: /api/rollout/456?detail=true

Header 字典记录了 HTTP 请求的 Header 的内容,假设我们的 header 是这样的

HeaderA: value1, value2
HeaderA: value3

那么 Header 中记录的形式是这样的 Headers["HeaderA"]={"value1", "value2", "value3"}

2.3 IRequestCookiesFeature

代表了请求中的 Cookies , 它返回了一个 IRequestCookieCollection 对象。 请求中的 Cookies 是放在 Header 中,格式如下

Cookie: SID=31d4d96e407aad42; lang=en-US

那么 IRequestCookieCollection 保存的数据就是

SID: 31d4d96e407aad42
lang: en-US

2.4 IFormFeature

2.5 IFormFeature

HTML 页面中,可以通过下面的方式发送 form 数据

<form action="https://example.com" method="POST">
</form>

发送的请求时候, Header 中的 Content-Type 包含下面三种选项

  • application/x-www-form-urlencoded : 它说明了 form 中的 key-value 对按照 &= 的方式组合在一起放在 body 中,比如说
POST /test HTTP/1.1
Host: example
Content-Type: application/x-www-form-urlencoded
Content-Length: 27

field1=value1&field2=value2
  • Multipart/form-data 如果 form 中包含了大的二进制文件,需要拆开开,并且使用 boundary 指定的字符进行拆分。在每一个部分中,用 Content-Disposition 之后的内容表示各自的 key-value
POST /test HTTP/1.1
Host: foo.example
Content-Type: multipart/form-data;boundary="boundary"

--boundary
Content-Disposition: form-data; name="field1"

value1
--boundary
Content-Disposition: form-data; name="field2"; filename="example.txt"

value2
--boundary--

有了这些基础知识,我们来看看 IFormFeature 究竟做了什么事情

  • HasFormContentType 说明是否包含了 Form 的请求
  • Form, ReadForm, ReadFormAsync 都是读取出 IFormCollection

IFormCollection 中,

前 5 项是关于是application/x-www-form-urlencoded form 中包含的记录,而 Files 则记录了 Multipart/form-data form 中的数据。 IFormFileCollection 则是 IFormFile 的集合,可以通过 name 索引读取,需要注意的是多个块可以包含相同的 name .

2.6 IRequestCookiesFeature

这是请求发过来的 cookie , 一般来讲请求的 cookie 是这样的

GET /sample_page.html HTTP/2.0
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry

Cookie 的 header 项中,通过 ;= 将多个 key-value 组合在一起。 IRequestCookiesFeature 就保存着这些 key-value 对。

2.7 IHttpWebSocketFeature

首先什么是 WebSocket , 我们都知道 HTTP 请求是单向的,也就是说只有客户端发送请求之后,服务端将数据发送给客户端;在长时间运行的服务中,如果客户端想要得到最新的服务端数据,需要采取定期 poll 服务端。那么 WebSocket 出现就解决了这个问题,它允许客户端和服务端双向发送数据,这样一旦服务发现了新的数据,就可以向客户端直接发送,从而避免了不停的 poll 的策略。

WebSocket 的连接的建立是基于 Http 请求

GET ws://example.com:8181/ HTTP/1.1
Host: localhost:8181
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: q4xkcO32u266gldTuKaSOw==
  • ws://example.com:8181 执行了 Websocket 的协议,而且是开始于 HTTP/1.1 协议
  • Connection:Upgrade 确定当前的连接是升级了
  • Upgrade:websocket 表明升级到 Websocket 协议
  • Sec-Websocket-Version 指定 WebSocket 的版本
  • Sec-WebSockety-key 随机生成的 16 个字节 base64 编码后的值

那么服务端返回的数据就是

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: fA9dggdnMPU79lJgAE3W4TRnyDM=
  • HTTP/1.1 101 转换协议
  • Upgrade 修改协议为 WebSocket
  • Sec-WebSocket-Accept 将客户端发过来的 Sec-WebSocket-Key 和 `258EAFA5-E914–47DA-95CA-C5AB0DC85B11` 拼接后的 base64 值。

基于上述 WebSocket 的基本认知, IHttpWebSocketFeature 主要工作首先是否判断可以转换成 WebSocket ,如果是则调用 AcceptAsync 转换成一个 WebSocket 对象。

2.8 IHttpResponseFeature

  • StatusCode HTTP 请求的 status code
  • ReasonPhrase StatusCode 中的文本解释,比如 OK , Not Found 等等,注意这个不是个 Optional 的
  • Headers 响应的 header
  • Body 请求 BodyStream
  • OnStarting OnCompletedResponse 开始,完成的时候,注册相关的操作。

2.9 IHttpResponseBodyFeature

  • Stream 用来写 body 的 Stream
  • Writer 用来写 body 的 Writer
  • StartAsync 调用 OnStarting 方法,并且让 Headers 只读
  • SendFileAsync 发送请求的文件
  • CompleteAsync Flush 剩下的 header data 等等。

2.10 IResponseCookiesFeature

IRequestCookiesFeature 相呼应, IResponseCookies ,一个典型的 Response 的 cookie 是这样的

HTTP/2.0 200 OK
Content-Type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry

但是不同于 Request 中直接的 Cookie , response 中的 cookie 可以由额外的配置选项,比如

  • Expires 设置 Cookie 过期时间
  • Secure 设置 cookie只能在 HTTPS 协议中使用
  • ...

这些都通过 CookieOptions 来配置

--

--

Feng Gao

A software developer in Microsoft at Suzhou. Most articles spoken language is Chinese. I will try with English when I’m ready