$ ls ~yifei/notes/

curio asks 源码解析

Posted on:

Last modified:

asks 是 Python 的异步框架 curio 中的一个 http 库。它基于 h11 这个库来做 http 协议的解析, 然后提供了在 curio 下的 IO 操作。下面按照功能逐个介绍其中的每个部分。

杂项

auth.py

该文件中主要包含了 http auth 相关函数,支持了 Basic Auth 的 Digest Auth。值得注意的是, digest auth 作为一种既很复杂又不安全的认证方式,已经没有人用了。需要使用 http auth 的话, 现在推荐的方式使用 https + basic auth。

base_funcs.py

提供了一些快捷方式函数,比如 curio.get。

cookie_utils.py

该文件主要包含了 CookieTracker, 对外的方法主要有两个 get_additional_cookies 用于获取 域名对应的 cookie,_store_cookies 用于添加 cookie。

parse_cookies 函数主要用于解析 set-cookie 头部,并把解析到的 cookie 附加到 response 对象上。

errors.py

asks 中抛出的异常的类

http_utils.py

处理编码和压缩的两个函数。

请求与响应

request_object.py

该文件中主要是定义了 RequestProcessor 类。RequestProcessor 用于生成一个 HTTP 请求。

make_request 方法。hconnection 定义和使用的地方相距太远了。cookie 的生成应该使用 join。 之后调用 _request_io 发送请求

_request_io 调用 首先掉用 _send, 然后调用 _catch_response

_catch_response 调用 recv_event

_recv_event 不断调用 _async_lib.recv(self.sock, 10000) 从而不断产生数据,直到读完为止

sessions.py

session 类

request 调用 grab_connection 获取一个 socket,然后把这个 socket 交给 Request 对象 grab_connection 调用 checkout_connection 获得一个 socket 或者,调用 make_connection 产生 一个新的 socket,注意其中有一个奇怪的 await sleep(0),可能意思是把循环交回给 event loop

make_connection 调用 _connect 方法,并把 host 和 port 作为属性写到 socket 上

session 中有两个 SocketQ 的类,conn_pool, checked_out_sockets 分别用来保存已连接未使用的 socket 和正在使用中的 socket

response_objects.py

Response 表示了一个响应。如果在发起请求的时候选择了 stream=True, response.body 会是一个 StreamBody 实例,StreamBody 用于流式处理响应。

Cookie 类表示了一个 Cookie,不知道这里为什么没有用标准库的 cookie。

Connection Pool

如果使用代理的话

req_structs.py

SocketQ 是一个 socket 的连接池。使用一个 deque 作为存储,实际上相当于又模拟了一个字典 {netloc => socket}(思考:为什么不使用 OrderedDict 呢?)index 返回指定 hostloc 对应的 index。pull 弹出指定 index 的 socket。__contains__ 遍历看是否包含对应的 socket。需要 注意的是这个类不是线程安全的,不过对于 curio 来说,线程安全似乎无关紧要,毕竟只有一个线程。

CaseIncesitiveDict 是一个对大小写不敏感的词典,直接从 requests 里面拿过来的。

curio 的网络通信

首先,需要引入 curio.socket 而不是使用内置的 socket

TCP 通信,使用 sock.bind/listen/accept 等建立服务器,使用 recv 和 sendall 发送接收消息。 UDP 通信,使用 recvfrom 和 sendto 函数通信

作为客户端使用 curio.open_connection 打开到服务器的链接,其中 ssl=True 打开的是 HTTPS 连接诶

对于其他要使用 ssl 的情况,应该使用 curio.ssl 而不是标准库的 ssl

curio.network.

ssl.wrap_socket 不支持 server_hostname sslcontext.wrap_socket 支持

不要把 proxy 传递给 request 对象

添加 http 代理支持

asks 把繁重的 http 解析工作都用 h11 这个库巧妙的解决了,所以 asks 本身是非常轻量的一个库。 很遗憾的是,在 asks 中尚未支持代理,下面我们尝试为 asks 增加 http 代理的支持 :P

在 http 代理模式中,每个请求都需要添加上 Proxy-Authorization 的 Header。而在 https 请求中 ,只有 Connect 的时候需要添加上 Proxy-Authorization 的 Header。

代理的 socket 池和真正的 socket 池要分开,这样设计还简单一点。

© 2016-2022 Yifei Kong. Powered by ynotes

All contents are under the CC-BY-NC-SA license, if not otherwise specified.

Opinions expressed here are solely my own and do not express the views or opinions of my employer.