一个写文档的开发者,其实就是个 Docker
正文
上一期我们熟悉了应用场景和测试,这一期我们实现receive
函数。
先重温一下 API:
1 | 复制代码class HTTP2Protocol: |
我们的整体设计思路是 Event Driven + Mutable State.
Event Driven:gethy
内部自定义一些事件(Event),HTTP2Protocol
的 Public API 只会返回这些 Event 而已。
Mutable State:HTTP2Protocol
内部会管理两个缓冲(Buffer),一个inbound_buffer
储存接收的数据,一个outbound_buffer
储存需要发送的数据。这两个 Buffer 都是私有的,用户不应该使用。根据不同的事件,HTTP2Protocol
会向 Buffer 添加数据或者清除数据。
HTTP2Protocol 类
现在,我们来看更具体的函数签名:
1 | 复制代码# http2protocol.py |
current_events
:顾名思义,用来存放目前已知的事件。
request_buffer
:存放没有接收完整的 Request Stream。
response_buffer
:存放没有完全发送的 Response Stream。
Stream 类
当然,我们还需要一个Stream
来表示一个数据流。
1 | 复制代码class Stream: |
流程图
在实现之前,我们先来看看流程图。
如图所示,我们的工作流程是纯线性的,所以也使其逻辑简明,容易实现。
receive
1 | 复制代码def receive(self, data: bytes): |
这里就将receive
函数写好了,接下来实现_handle_event
和_parse_request_buffer
。
_handle_event
Handle events 的部分由几个重要的函数组成。
1 | 复制代码def _handle_event(self, event: h2.events.Event): |
首先_handle_event
要判断是哪种 h2 事件。我们用if/else
来将事件导流到相应的函数去。本期只关心 Request(Headers&Data),其余事件简单地打印出来。
注:这里的 h2 事件其实和 HTTP/2 的 frame 有直接的关系。一个 Request 事件其实就是一个 Request Frame。一个 Data 事件其实就是一个 Data Frame。
参考文档:
_request_headers_received
1 | 复制代码def _request_headers_received(self, event: RequestReceived): |
这个 event 里有stream_id
&headers
,将其拿到并构造一个Stream
实例。如果数据流结束,则调用_stream_ended
。这里stream_ended == True
的意思就是这个 Request 只有 Headers。通常的GET
或者POST url param encoded
就属于这个类型。很多框架甚至不允许GET
带有 Request Body/Data。
_data_received
1 | 复制代码def _data_received(self, event: DataReceived): |
Request 也可以带有 Data,所以就会触发这个事件。这里request_buffer[event.stream_id]
是一定不能触发KeyError
的,因为只有可能先接收 Headers,再接收 Data。如果有 KeyError,那么八阿哥一定潜伏于某处。这里stream_ended == True
就说明 Request 完整接收了。
_stream_ended
1 | 复制代码def _stream_ended(self, event: StreamEnded): |
当接收完一个 Request 数据流后,将Stream
实例的状态做一些调整。
_parse_request_buffer
这样,我们就将所有数据都处理好了。现在的任务就是将缓冲扫描一遍,看有没有指的返回的东西。
1 | 复制代码def _parse_request_buffer(self): |
这里的逻辑也简单明了,检查有没有完整的 Request,有的话就构造一个完整的RequestEvent
,然后将其放到self.current_events
中。最后从缓冲中删除相应的Stream
。
RequestEvent 类
RequestEvent
定义如下:
1 | 复制代码# events.py |
纯粹为了代码可读性而定义的。
仔细的同学可能会看到两点:
- 在
_stream_ended
中就可以完成这个函数中的所有操作,没有必要再 loop 一遍浪费时间。 - 如果非要再 loop 一遍,可以写成函数式的,return
current_events
,而不是更改对象的值。
完全正确,这里我为了大家看得简单明了,所以选择了更简洁,但是效率稍微慢一点的实现。
结语
到这里你就实现了一个完全正确可用的 HTTP/2 服务器端的接收功能。下一期就要实现发送了。
视频对文章进行补充,感兴趣就去看看吧!代码在 GitHub,喜欢给个🌟呗!
代码
视频
油腻的管道(你留言我就上传)
文章
下期(还没写)
本文转载自: 掘金