0x00 报文格式

一个普通的 HTTP 请求报文格式如下:

1
2
3
4
5
[请求方法] [URI] [HTTP版本]
Host: [主机地址]
// ... 其它请求头信息
// 一行空行
[请求实体]

简单例子如下:

1
2
GET / HTTP/1.1
Host: www.baidu.com

上述例子有两个空行,但并非无意义。第一行空行为分割请求头和请求实体的标志,第二行是因为请求实体内容为空(如果有内容就不是空行)。

下面来看响应报文格式:

1
2
3
4
5
[HTTP协议版本] [响应状态码] [状态码含义]
Content-Type: [响应内容类型]
// ... 其它响应头信息
// 一行空行
[响应实体]

简单例子如下:

1
2
3
4
5
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 34

<html><h1>Hello World!</h1></html>

0x01 重要的头信息

Content-Length

Content-Length 表示请求/响应实体内容的长度(单位是字节)。

当 Content-Length 的值与实际内容字节数不符会出现什么情况?

  1. 当内容的实际字节数大于 Content-Length 的值时:
    • 当服务端接收的请求实体长度超过 Content-Length ,服务端会直接关闭连接,导致本次请求失败。
    • 当服务端返回的响应实体长度超过 Content-Length,超出的部分会被截断,只保留 Content-Length 长度的内容。
  2. 当内容的实际字节数小于 Content-Length 的值时:
    • 当服务端接收的请求实体长度小于 Content-Length,因为接收到的长度不够服务端会继续等待后面的数据,直到超时。
    • 当服务端返回的响应实体长度小于 Content-Length,Chrome 浏览器会报 net::ERR_CONTENT_LENGTH_MISMATCH 的错误。

注意: 在多数浏览器中发起 AJAX 请求时是不能自定义 Content-Length 头的(出于安全原因),以 Chorme 为例会报 Refused to set unsafe header "Content-Length" 的错误。

关于 PHP 的应用技巧:

一般来说浏览器在接收到 Content-Length 单位个字节后就会视为本次请求完毕,所以利用 Content-Length 可以实现在请求完毕后继续执行一些任务最后才结束我们的 PHP 进程。示例如下:

1
2
3
4
5
<?php
header('Content-Length: 12');
echo 'i love http!';
sleep(6);
file_put_contents('./1.txt', '123');

访问上述代码,可以发现输出 i love http! 后请求就完成,但是 PHP 的进程仍在运行,直到 6 秒后写入文件 1.txt 进程结束。

这种方式适用于能够计算响应内容长度的情况,但如果响应长度不确定,比如我们要返回一个很大的响应,因为要节省内存所以采用分段读入,处理,输出的策略,那么这种情况就不能用这个方法了,因为无法提前计算出内容的长度。不过如果你使用了 PHP-FPM 可以使用 fastcgi_finish_request 函数实现这个功能。

Transfer-Encoding

Transfer-Encoding 表示传输的编码,一般情况下值都是 chunked,表示分块传输。使用 chunked 分块传输解决的就是 Content-Length 无法提前计算出来的情况,所以Transfer-EncodingContent-Length 是互斥的,如果同时出现,浏览器以 Transfer-Encoding 为准。 使用 chunked 后响应的示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
HTTP/1.1 200 OK 
Content-Type: text/plain
Transfer-Encoding: chunked

7\r\n
Mozilla\r\n
9\r\n
Developer\r\n
7\r\n
Network\r\n
0\r\n
\r\n

Content-Type

Content-Type 表示响应实体的 媒体类型

Accept

Accept 表示客户端能接受响应实体的 媒体类型

还有很多重要的头比如 cookie 相关的、跨域相关的、缓存相关的,篇幅所限暂不解释,请移步至 腾讯云社区 / HTTP 文档 / Headers

0x02 请求方法

方法 GET POST PUT DELETE PATCH OPTIONS HEAD
请求实体有内容
请求成功时响应实体有内容
安全
幂等
可缓存
允许出现在 HTML 表单

0x03 状态码

原谅我的懒惰,直接点击查看 状态码表格

列出一些常用的状态码:

  1. 200 201 202 204
  2. 301 302 304
  3. 400 401 403 404 405 422
  4. 502 503 504

常见问题

301 和 302 的区别?

301 重定向作为永久重定向,也意味着它是可以被缓存的,举个例子:用户第一次访问 A.com,服务器响应 301 并重定向至 B.com,重定向的结果会默认被浏览器缓存下来,当用户再次访问 A.com 时,浏览器并不会向 A.com 发起请求,而是直接跳转到 B.com。而 302 重定向则默认不会被浏览器缓存下来,意味着每次都会请求原服务器,再按服务器返回的响应执行重定向操作。当然通过设置缓存控制头可以改变默认的缓存策略。

301 和 302 导致 POST 请求变 GET(POST 请求参数丢失)?

当使用 POST 方法请求时如果服务端返回 301 或 302 状态码并指定重定向地址,那么大多数浏览器会采用 GET 方法请求重定向地址,这意味着之前 POST 请求所带的参数会丢失!如果你不希望请求的方法被改变,则服务端应该返回 307(对应 302)或 308(对应 301)。

详见:https://www.cnblogs.com/wuguanglin/p/redirect.html