CSAPP proxy lab 解析 HTTP 报文时对 Content-Length 报头字段作用总结
在做 CSAPP proxy lab 时需要实现一个 proxy 来代理客户端和服务器之间的 HTTP 请求( 实验中仅关注了 GET 方法).在解析请求过程中,需要获得代理的 HTTP 请求和响应的 entity body 的数据长度进行处理.这里记录下关于 HTTP entity length 的内容.权威的 HTTP 请求信息可以直接参考对应的 RFC 文档,包括 RFC-1945 Hypertext Transfer Protocol -- HTTP/1.0 和 RFC-2616 Hypertext Transfer Protocol -- HTTP/1.1.
Entity body
HTTP 请求和响应中,其中的 Entity body 是可选的,即并不一定存在,同时 Entity body 可能为字符也可能为二进制数据,故而需要一定的手段对 Entity body 的数据长度进行标志,从而方便对 Entity body 数据的处理和传递.在 HTTP 请求和响应中,可包含有 Content-Length 来对 Entity body 的长度进行标志,注意这里的长度并不是指 HTTP请求和响应的总长度,而是报文中仅包含 "\r\n" 的空行后的数据的长度.
HTTP 1.0 中对 HTTP 请求和响应中 Entity body 是否存在有对应的描述.
在 HTTP 请求中 Entity body 是可选的,仅有当请求中需要在 Entity body 传递信息时,才会有对应的 Entity body, 同时包含 Entity body 的请求中必须要有 Content-Length 字段,来标志 Entity body 中数据的长度.也就是说只要 HTTP 请求中包含有 Entity body,那么在请求头中 Content-Length 一定存在.
在 HTTP 响应中,是否包含有 Entity body 与具体的情况有关.如对于客户端使用 HEAD 方法请求的响应一定不包含有Entity body,但是响应的响应头中可能包含有 Content-Length 字段.所有的响应状态为 1xx( informational ),200( no content )和304( not modified )的响应均不能包含有 Entity body.而其他所有的响应必须包含有 Entity body,或者给定一个指定 Content-Length 为 0 的头信息.注意,这里并没有指定包含有 Entity body 时一定会存在 Content-Length 头信息.
Content-Length
头信息中的 Content-Length 存放的是关于 Entity body 长度的信息.当是这两者并不总是同时存在的.根据上面的描述,对于 HTTP 请求,若 Entity body 存在,则 Content-Length 一定存在,并标志请求中 Entity body 的长度.而在 HTTP 响应中,当 Content-Length 不存在时,仍有可能存在 Entity body. 以及当 Content-Length 存在时,也可能没有 Entity body.
这里对 HTTP 响应的 Content-Length 进行以下解释.
1.若响应中 Content-Length 为 0,则响应中不包含有 Entity body.
2.若 Content-Length 不为 0,若该响应为对 HEAD 请求方法的响应,则 Entity body 不存在( HEAD 方法仅是获得与 GET 方法相同的响应头,而不含有对应的 Entity body,用于测试等操作, 此时 Content-Length 字段存放的是若对正常 GET 方法进行响应时 Entity body 的长度. 对 HEAD 方法的响应实际不存在 Entity body )
3.HTTP 响应中可能不包含有 Content-Length,此时一定有 Entity body 存在.根据 RFC-1945 7.2.2 Length, 当 Content-Length 不存在时, Entity body 的长度通过服务器关闭连接时确定.
这里 RFC 1945 对上面的第 3 点进行了解释. HTTP 请求中不能基于客户端关闭连接来确定请求报文中 Entity body 的长度,因为这样虽然确定了长度,但是服务端无法通过对应的连接再回复响应了,所以存在 Entity body 时必须通过 Content-Length 标志.而在服务器端,在服务器完成响应 Entity body 的数据传输后,可以关闭连接,此时客户端会收到 EOF 信息,从而能够确定响应发送完成,自然也就可以获得对应的 Entity body 的长度了.
关于 EOF 的补充,来自 CSAPP 第二版 p.631.
EOF 是由内核检测到的一种条件.应用程序在它接收到一个由 read 函数返回的零返回码时,它就会发现 EOF 条件.对于磁盘文件,当前文件位置超出文件长度时,会发生 EOF.对于因特网连接,当一个进程关闭连接它的那一端时,会发生 EOF.连接另一端的进程在试图读取流中的最后一个字节之后的字节时,会检测到 EOF.
以及还有一个关于 EOF 的小问题,在 c 的标准输入输出函数中,使用一个宏定义 EOF 来标志已经读取完了最后一个字节的内容,这个宏被定义为 -1.而在类 Unix 系统的系统 I/O 函数中,其读取完成时返回 0 作为 EOF 标志,所以在使用 read 函数时,不要用 read(xxx) == EOF 来判断是否读取完成.
这里补充一点 HTTP 相关的请求与响应的信息,仅供参考.
HTTP 请求( request )
以 HTTP 1.0 为例介绍一下简单的 HTTP 请求的格式. HTTP 1.0 支持简单请求( simple request )和完整请求( full request )两种,这里主要介绍完整请求.在 HTTP 1.0 中.完整请求格式如下所示.
Request Line //请求头,包括请求方法,请求资源的 uri 和 HTTP 版本 Header //请求的描述性信息,以 field : value 的格式给出 CRLF // "\r\n",注意该行前的所有行都需要以 "\r\n" 结尾 Entity-Body //请求体,可能为空
请求行( request line )
请求行包括请求方法,请求 URI 和 HTTP 版本这三个字段,使用空格隔开.一个典型的 HTTP 请求的请求行如下所示.其中 GET 为请求方法, "/pub/WWW/TheProject.html" 为请求的 URI,后面接一个 HTTP 版本.
GET /pub/WWW/TheProject.html HTTP/1.0
请求头( header )
请求头可以理解形如 field : value 的键值对序列,用于传递请求相关的信息,起到请求参数的作用,常见的请求如 Date, Content-Length, Content-Type 等,一个典型的请求头如下所示.注意value 与 ':' 之间存在空格.(实际上头(header)被分为通用头(general header),请求头(request header)和实体头(Entity header)三种,使用同样的表达形式对请求中不同的部分进行描述,这里未做具体区分)
Accept-Encoding: gzip, deflate //形如 field: value 的键值信息对
请求体( Entity body )
这里的请求体是为了表述方便,并不是正规翻译哈.可能存在也可能不存在,对请求体的处理需要获取具体的数据长度.在请求之前,请求行和请求头均使用 "\r\n" 作为结尾字符,且最后还存在一个空行包含 "\r\n" 作为与请求体的分割,而请求体中的内容可以为文本,也可以为二进制数据,故而需要具体的数据长度才能进行解析.同时请求体也并不是在每个请求中均存在.
HTTP 响应( response )
与客户端请求对应的是服务器段的响应. HTTP 响应的基本格式如下所示.
Status line //状态行,包括 HTTP 版本,状态码和状态码的描述性信息 Header //与请求头一致的 field: value 格式 CRLF //"\r\n"作为与响应体的分割 Entity body //可选的响应体
HTTP 响应与请求最大的区别在于开头的状态行( status line ),其由 HTTP 版本,状态码和状态码的描述性信息组成.典型的 HTTP 响应的状态行如下所示.常见的状态码如 404( not found ),502( bad gateway )等可以参考 .
HTTP/1.1 200 OK //状态行由 HTTP 版本,状态码和状态码的描述性信息组成
HTTP 响应的其他部分与 HTTP 请求的格式基本一致.