本章从客户端程序的角度来学习HTTP协议的使用方法。这些客户端程序需要获取或缓存文档,或者向服务器提交请求或数据。HTTP的最本质的概念形式:一种用于获取与发布文档的机制。HTTP1.1版本是目前最常用的版本。
1.Python客户端库
一个第三方库是Python程序员使用HTTP时的第一选择,可以替代urllib。这个库就是Kenneth Reitz写的Requests,它基于urllib3的连接池逻辑,urllib3目前由Andrey Petrov维护。在本章的学习中,会介绍urllib和Requests两个库针对各个HTTP特性各自展现出的优缺点。它们的接口极其相似,都提供了可供调用的方法,用于打开HTTP连接,发起请求,等待接收响应头,然后将包含响应头的响应对象发送给程序员。响应体会留在套接字的接收队列中,只有程序员请求时才会读取响应体。安装后运行下面程序:可以看到二者的使用接口很类似,但是已经看到了两处区别。Requests一开始就声明其支持gzip和deflate两种压缩格式的HTTP响应,而urllib则不支持。除此之外,Requests能够自己确定正确的解码方式,并将HTTP响应从原始字节转换为文本,而urllib库只会返回原始字节(注意:原始返回的是字节,网络传输中传输的就是字节),而用户需要自己解码。
2.端口、加密与封帧
80端口是用于纯文本HTTP会话的标准端口。而有些客户端则希望首先协商一个加密的TLS会话,一旦加密连接建立完成,就是用HTTP进行通信。这是超文本传输安全协议(HTTPS)的一个变形,此时的标准端口是443端口。在加密连接内部,只要像在普通的未加密套接字上一样,直接使用HTTP即可。 TLS的目的不只是保护数据在传输过程中不被窃听,它也会对客户端连接的服务器身份进行验证(此外,如果客户端也提供证书的话,TLS也允许服务器对客户端身份进行验证)。如果某个HTTPS客户端没有检查尝试连接的服务器所提供的证书是否与其主机名相匹配的话,绝对不要使用这个客户端。
在HTTP中,客户端首先会向服务器发送一个获取文档的请求(request),一旦发送完整个请求,客户端就会进行等待,直到从服务器接收到完整的响应(response)为止。响应中可能包含错误信息,也可能会提供客户端请求的文档信息。至少在今天最流行的HTTP/1.1协议版本中,不允许客户端在尚未收到上一个请求的响应前就再同一个套接字上开始发送第二个请求。
HTTP中有一种很重要的平衡——请求和响应采取了相同的格式化与封帧规则。下面的例子给出了一对请求和响应,在阅读后面的协议描述时可以进行参考。
请求和响应的标准名称都是HTTP消息(message),每个消息由以下三部分组成。
- 在请求消息中,第一行包含一个方法名和要请求的文档名;在响应消息中,第一行包含了返回码和描述信息。无论是在请求还是响应消息中,第一行都以回车和换行(CR_LF,ASCII码13和10结尾)。
- 第二部分包含零个或多个头信息,每个头信息由一个名称、一个冒号以及一个值组成。HTTP头的名称是区分大小写的,因此可以根据客户端或服务器的要求自由使用大写字母。每个头信息由一个CR-LF结尾。在列出了所有的头信息之后再跟上一个空行,无论第二部分是否包含头信息,都必须包含该空行。
- 第三部分是一个可选的消息体。消息体紧跟着头信息后面的空行。我们会简要介绍对各实体进行封帧的一些选项。