HTTP 的手术室
早上 6 点,浏览器早高峰准时开场。GET、POST、请求头、响应体挤进 HTTP 手术室,TCP 还时不时“duang”地砸来一个包——今天又是忙碌的一天。
来客
时间定格在早上 6 点。伴随着“哒哒哒”的声响,浏览器地址栏出现了 https://breeze.vin。浏览器知道,又一个早高峰要来了。
“各部门就绪!” 浏览器老大一声令下,各部门都开始做起了准备工作。
“去 HTTP 服务部,记住你的请求方式是 GET,还有这些请求头也一起拿走。” 伴随着 Enter 的输入,浏览器老大对新生成的 URL 说道。
“浏览器老大让我来找你!” URL 飞奔到 HTTP 的办公室说道。
“好,请求方式?” 一看时间,早上 6 点,知道早高峰即将来临,HTTP 赶忙问道。
“GET。”
“请求头有吗?”
“有!” URL 拿出一个袋子说道。
“OK。” 说着,HTTP 拿起放在一旁的麻醉针,一针下去,URL 进入了休眠。没错,这间办公室就是手术室。
HTTP 拿起手术刀,手起刀落,取出 URL 的 <path>?<query> 部分,再把 GET、请求目标和版本号 HTTP/1.1 放进同一个袋子,用胶带封好,摆在一旁。
“请求行 OK,接下来是请求头。” HTTP 自言自语道,顺手拿起了 URL 带过来的袋子。
“让我检查一下。嗯,Accept、Cookie、User-Agent……然后加上 Host。” 一边检查,一边再次拿出手术刀,把 URL 的主机名部分切下,放入袋中。
“OK” 确认无误后,打包,放到了刚刚打包好的请求行下面。
“这个 GET 请求没有请求体,直接生成报文。” 按照流程,HTTP 将打包好的请求行与请求头一并丢进办公室旁的通道。
“接下来就交给 TCP 兄弟啦。” 对着通道喊道。
“duang ~” 通道一头发出物体碰撞的声音 “能不能事先通知下,我的脑袋禁不起你这么砸!” 通道另一侧来了句抱怨。
“下次一定注意,现在早高峰,急了点!”
回归
“duang ~ ” 头上被撞了个大包,HTTP 愤怒地喊道:“TCP 你干嘛!!”
“早高峰,早高峰 ~” 通道的另一头传来了 TCP 略带报复的声音。“这是刚刚请求的响应,快处理吧!”
“行!” HTTP 拿起手术刀,把刚刚的 URL 重新拼好,顺便将响应报文拆解开来。
伴随着一盆冷水泼下,URL 苏醒过来。
“这袋响应头信息,我已经按照键值对弄好了,这堆是响应体,我这处理不了,你就这样带走吧。” HTTP 对着刚刚苏醒的 URL 说道。
“能不能不是每次都泼水啊,我的发型!” URL 抱怨道。
“别啰嗦了,快去浏览器老大那!浏览器老大都快等不及了。”
“行吧!” 浏览器老大着急的样子 URL 可是知道的,带着东西,赶忙往浏览器那儿跑去。
开始忙碌
“我回来了,这是数据。” URL 跑得有点喘不过气来。
浏览器理都没理 URL,一头钻进了 URL 一旁的响应体中。
“HTML 解析器开始工作,CSS 你也别闲着,没看到这儿有样式吗?JavaScript 也赶紧动起来。” 浏览器老大指挥道。
“这有个 link 需要 CSS 资源!” HTML 解析器说道。“这还有个 script 使用的是外部脚本,需要 xxx.js。”
“好的,我马上生成 URL 去请求资源。” 浏览器老大边说,边生成了一系列的 URL,命令它们去 HTTP 服务部。
看着蜂拥而至的 URL,HTTP 攥了攥手中的工具,知道,早高峰来了。
不久,HTTP 服务部排起了长队。一批批的 URL 来,又有一批批的 URL 带着包裹出去。
终于,页面显示了出来,忙碌的早高峰过去了。HTTP 拿起一旁还冒着热气的咖啡,喝了一口。
POST URL
“JavaScript 让我来找你!” 一个 URL 跑了过来。与之前不同,这个 URL 除了带着一袋请求头,还带来一箱数据。
放下手中的咖啡,“请求方式?” HTTP 问道。
“POST。” URL 说道。
“看来用户开始提交数据了啊。” 说着,HTTP 拿起了放在一旁的麻醉针。
“请求行:POST /create HTTP/1.1 没毛病。”
“请求头。” 拿起 URL 带过来的请求头,检查一遍后,加入了 Host 并打包。
“这些数据我控制不了,直接塞到请求体中吧。” 把数据按请求行、请求头、请求体放好,打包后,扔进了一旁的通道里。HTTP 再次端起桌子上的咖啡喝了一口。
过了大约 5 秒,通道里蹦出一个报文。HTTP 知道这是刚刚那个 POST URL 的响应,便拿起工具拆解数据,让 URL 回到 JavaScript 那儿。
新人培训
忙碌的早高峰终于过去,悠闲的时间开始。浏览器老大却没让 HTTP 闲着,领来一个刚进服务部的实习生。
“趁现在不忙,给新人讲讲报文吧,省得下次早高峰手忙脚乱。” 浏览器老大说完就走了。
“行吧。” HTTP 放下咖啡,清了清嗓子,“我的工作,是根据 HTTP(超文本传输协议)生成报文,报文也就是刚刚被我扔进通道的东西。协议把报文分成了 3 部分,根据报文的不同,格式也有所区别,这也是这间手术室存在的意义。先看请求:”
| 内容 | 含义 |
|---|---|
| 请求行 | 请求方式 + 请求路径 + 协议版本。 |
| 请求头 | 代表浏览器信息以及当前请求的状态,比如 Host,Accept,User-Agent 等。 |
| 请求体 | 当前请求所携带的数据,提供给服务器上对应的处理程序使用。 |
“大概就长这样吧。”

“再看响应:”
| 内容 | 含义 |
|---|---|
| 状态行 | 协议版本 + 状态码 + 原因短语。 |
| 响应头 | 代表服务器信息以及当前响应的状态,比如 Date,ETag,Server 等。 |
| 响应体 | 当前响应所携带的数据,提供给浏览器上的处理程序使用。 |
“大概就长这样吧。”

“接着是请求方式。不同的请求方式代表了对资源不同的处理,虽然这个是 URL 直接给我的,但所代表的具体意义还是得知道的。”
| 请求方式 | 含义 |
|---|---|
GET |
返回请求路径所对应的资源。 |
HEAD |
不需要返回资源,仅返回响应行和响应头即可。 |
POST |
向目标资源提交数据,具体语义由服务器定义,常用于创建资源或触发操作。 |
PUT |
用请求体中的表示创建或整体替换目标资源。 |
PATCH |
对目标资源进行部分修改。 |
DELETE |
删除请求路径所对应的资源。 |
OPTIONS |
查询目标资源支持的通信选项,常用于跨域预检。 |
TRACE |
直接返回请求,主要用于测试。 |
“最后是状态码。不同的响应状态码,对应了不同的资源状态。”
| 状态码 | 含义 |
|---|---|
1** |
服务器收到请求,需要请求者继续执行操作。 |
2** |
请求成功。 |
3** |
资源移动或是未发生变更,需要浏览器进行下一步操作,请求移动后的地址,或直接使用缓存资源。 |
4** |
请求错误,包括请求语法错误,或是资源不存在。 |
5** |
服务器错误,资源存在,但服务器处理时发生错误。 |
“OK,HTTP 大致内容也就这些,只是一个协议嘛,不是太复杂。”
“还没介绍响应头、请求头啊!” 实习生翻着笔记提醒道。
“等用到的时候再说也来得及。” HTTP 想也没想就回了一句。
话音刚落,手术室的灯光忽然闪了一下,一块半透明的面板在墙上展开,上面密密麻麻全是今天经手的请求——屏幕外的主人打开了开发者工具。
“被看得一清二楚啊。” HTTP 打了个寒颤,裹紧白大褂,“行了,今天就到这儿,下一个早高峰见。”