透视HTTP协议
罗剑锋(Chrono)
奇虎360技术专家,Nginx/OpenResty开源项目贡献者
立即订阅
6077 人已学习
课程目录
已完结 44 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词|To Be a HTTP Hero
免费
破冰篇 (7讲)
01 | 时势与英雄:HTTP的前世今生
02 | HTTP是什么?HTTP又不是什么?
03 | HTTP世界全览(上):与HTTP相关的各种概念
04 | HTTP世界全览(下):与HTTP相关的各种协议
05 | 常说的“四层”和“七层”到底是什么?“五层”“六层”哪去了?
06 | 域名里有哪些门道?
07 | 自己动手,搭建HTTP实验环境
基础篇 (7讲)
08 | 键入网址再按下回车,后面究竟发生了什么?
09 | HTTP报文是什么样子的?
10 | 应该如何理解请求方法?
11 | 你能写出正确的网址吗?
12 | 响应状态码该怎么用?
13 | HTTP有哪些特点?
14 | HTTP有哪些优点?又有哪些缺点?
进阶篇 (8讲)
15 | 海纳百川:HTTP的实体数据
16 | 把大象装进冰箱:HTTP传输大文件的方法
17 | 排队也要讲效率:HTTP的连接管理
18 | 四通八达:HTTP的重定向和跳转
19 | 让我知道你是谁:HTTP的Cookie机制
20 | 生鲜速递:HTTP的缓存控制
21 | 良心中间商:HTTP的代理服务
22 | 冷链周转:HTTP的缓存代理
安全篇 (7讲)
23 | HTTPS是什么?SSL/TLS又是什么?
24 | 固若金汤的根本(上):对称加密与非对称加密
25 | 固若金汤的根本(下):数字签名与证书
26 | 信任始于握手:TLS1.2连接过程解析
27 | 更好更快的握手:TLS1.3特性解析
28 | 连接太慢该怎么办:HTTPS的优化
29 | 我应该迁移到HTTPS吗?
飞翔篇 (4讲)
30 | 时代之风(上):HTTP/2特性概览
31 | 时代之风(下):HTTP/2内核剖析
32 | 未来之路:HTTP/3展望
33 | 我应该迁移到HTTP/2吗?
探索篇 (5讲)
34 | Nginx:高性能的Web服务器
35 | OpenResty:更灵活的Web服务器
36 | WAF:保护我们的网络服务
37 | CDN:加速我们的网络服务
38 | WebSocket:沙盒里的TCP
总结篇 (2讲)
39 | HTTP性能优化面面观(上)
40 | HTTP性能优化面面观(下)
答疑篇 (2讲)
41 | Linux/Mac实验环境搭建与URI查询参数
42 | DHE/ECDHE算法的原理
结束语 (1讲)
结束语 | 做兴趣使然的Hero
透视HTTP协议
登录|注册

09 | HTTP报文是什么样子的?

Chrono 2019-06-17
在上一讲里,我们在本机的最小化环境了做了两个 HTTP 协议的实验,使用 Wireshark 抓包,弄清楚了 HTTP 协议基本工作流程,也就是“请求 - 应答”“一发一收”的模式。
可以看到,HTTP 的工作模式是非常简单的,由于 TCP/IP 协议负责底层的具体传输工作,HTTP 协议基本上不用在这方面操心太多。单从这一点上来看,所谓的“超文本传输协议”其实并不怎么管“传输”的事情,有点“名不副实”。
那么 HTTP 协议的核心部分是什么呢?
答案就是它传输的报文内容。
HTTP 协议在规范文档里详细定义了报文的格式,规定了组成部分,解析规则,还有处理策略,所以可以在 TCP/IP 层之上实现更灵活丰富的功能,例如连接控制,缓存管理、数据编码、内容协商等等。

报文结构

你也许对 TCP/UDP 的报文格式有所了解,拿 TCP 报文来举例,它在实际要传输的数据之前附加了一个 20 字节的头部数据,存储 TCP 协议必须的额外信息,例如发送方的端口号、接收方的端口号、包序号、标志位等等。
有了这个附加的 TCP 头,数据包才能够正确传输,到了目的地后把头部去掉,就可以拿到真正的数据。
HTTP 协议也是与 TCP/UDP 类似,同样也需要在实际传输的数据前附加一些头数据,不过与 TCP/UDP 不同的是,它是一个“纯文本”的协议,所以头数据都是 ASCII 码的文本,可以很容易地用肉眼阅读,不用借助程序解析也能够看懂。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《透视HTTP协议》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(48)

  • 一步
    1:如果拼 HTTP 报文的时候,在头字段后多加了一个 CRLF,导致出现了一个空行,会发生什么?
    在header 下面第一个空行以后都会被当作body 体

    2:讲头字段时说“:”后的空格可以有多个,那为什么绝大多数情况下都只使用一个空格呢?
    头部多一个空格就会多一个传输的字节,去掉无用的信息,保证传输的头部字节数尽量小

    作者回复: 回答的很好。

    2019-06-17
    25
  • 壹笙☞漂泊
    答题:
    1、头字段后多了一个CRLF,会被当做body处理
    2、节省资源
    总结:
    HTTP协议的请求报文和相应报文的结构基本相同:
        1、起始行(start line):描述请求或响应的基本信息
        2、头部字段集合(header):使用key-value形式更详细的说明报文
        3、消息正文(entity):实际传输的数据,它不一定是纯文本,可以是图片、视频等二进制数据

    HTTP协议必须有header,可以没有body。而且header之后必须要有一个空行,也就是 “CRLF”,十六进制的“0D0A”

    请求行(请求报文里的起始行):
        描述了客户端想要如何操作服务器端的资源
    起始行由三部分构成:
        1、请求方法:标识对资源的操作:GET/POST/PUT
        2、请求目标:通常是一个URI,标记了请求方法要操作的资源
        3、版本号:标识报文使用的HTTP协议版本
    以上三部分,通常使用空格分隔,最后用CRLF换行

    状态行:(响应报文里的起始行):
        服务器响应的状态
    状态行也是由三部分构成:
        1、版本号:标识报文使用的HTTP协议版本
        2、状态码:三位数,用代码形式标识处理的结果,比如200是成功,500是服务器错误
        3、原因:作为数字状态码补充,是更详细的解释文字,帮助人理解原因
    以上三部分,通常也使用空格分隔,最后用CRLF换行

    头部字段:
    请求行或状态行再加上头部字段集合就构成了HTTP报文里完整的请求头或响应头。

    头部字段是key-value的形式,用“:”分隔,最后用CRLF换行标识字段结束

    头字段,不仅可以使用标准的Host等已有开头,也可以任意添加自定义头

    注意:
        1.字段名不区分大小写,例如“Host"也可以写成“host”,但首字母大写的可读性更好;
        2.字段名里不允许出现空格,可以使用连字符“一”,但不能使用下划线“”。例 如,“test-name”是合法的字段名,而“test name""test_ name' 是不正确的字段名;
        3.字段名后面必须紧接着“:”,不能有空格,而“:” 后的字段值前可以有多个空格;
        4.字段的顺序是没有意义的,可以任意排列不影响语义;
        5.字段原则上不能重复,除非这个字段本身的语义允许,例如Set-Cookie。

    常用头字段

    基本分为四类:
        1.通用字段:在请求头和响应头里都可以出现;
        2.请求字段:仅能出现在请求头里,进一步说明请求信息或者额外的附加条件;
        3.响应字段:仅能出现在响应头里,补充说明响应报文的信息;
        4.实体字段:它实际上属于通用字段,但专门描述body的额外信息。

    Host:请求字段,只能出现在请求头。是必须出现的字段
    User-Agent:是请求字段,只能出现在请求头里。
    Date:是通用字段,通常出现在响应头,标识HTTP报文创建的时间,客户端可以使用这个时间再搭配其他字段决定缓存策略
    Server字段是响应字段,只能出现在响应头里。告诉客户端当前正在提供Web服务的软件名称和版本号。
    Content-Length:标识报文里body的长度。

    作者回复: 总结的非常好,赞!

    2019-06-17
    13
  • 10
    我也遇到了前面2个同学提到的遗失对主机的连接的问题,但老师您的回答貌似没解决问题,我再确认一下,在浏览器可以顺利访问www.chrono.com的情况下:

    1. Win+R打开Telnet后,输入 “open www.chrono.com 80”,点击回车,然后界面显示“正在连接open www.chrono.com 80”
    2. 上一步漫长的等待后,界面显示“按任意键继续”
    3. 我按了空格,界面上新增一行“遗失对主机的连接”

    另外老师您说的“按Ctrl+]键,然后回车”我在上面1步和第3步都试过 每次都显示“无效指令”

    请问我是哪一步出的问题呢

    作者回复: 不需要等待,在显示“正在连接”的时候按按Ctrl+]键,然后回车。

    “正在连接”的意思是已经连上了。

    可以再搜索一下Windows上Telnet的用法。

    2019-06-19
    4
  • 业余草
    老师讲到了 Host,可以顺便讲一下 Host 攻击吗?

    作者回复: 网络攻击的范围太大,我不是专门做这个的,不能讲的很深入。

    简单来说,就是在host头里面加入精心设计的代码,诱骗服务器执行。

    2019-06-18
    4
  • 隰有荷
    为啥老师懂的这么多!唉,何时能学成你的一半水平,我也就满意了😂

    作者回复: 学业有先后,术业有专攻,一起努力。

    2019-06-21
    3
  • 火车日记
    老师有个点是不是漏讲了,头部字段content_type,和body的数据格式

    作者回复: 后面的进阶篇再讲。

    2019-06-19
    3
  • 一步
    文中说 http 的头部不能使用下划线,感觉是有问题的,就拿 nginx 来说吧,虽然nginx 默认是忽略下划线的头部的,但是可以设置 underscores_in_headers on; 来获取下划线的头部

    对于常用的 web application 服务器,下划线的头部好像是可以直接获取到的,不用配置什么

    老师你说的不能使用下划线是 RFC规范吗?

    作者回复: 是的,RFC有规定,但现实中也有部分不遵守。

    2019-06-17
    3
  • 苏超
    2. 讲头字段时说“:”后的空格可以有多个,那为什么绝大多数情况下都只使用一个空格呢?
    请问老师,空格可以一个都不加吧,telnet测试也可以正确返回,为什么还要使用一个空格

    作者回复: 按照rfc标准,空格可以是零个或多个,但一个空格已经成了约定俗成的习惯。见rfc7230 3.2.3。

    2019-06-18
    2
  • -W.LI-
    支持老师原声。赞一个

    作者回复: 理解万岁。

    2019-06-17
    2
  • Demon.Lee
    老师,不是通过ip+port+资源路径确定的吗,咋又通过header中的host来找呢,不懂。

    作者回复: host头字段是用来定位“虚拟主机”的。比如说一台物理服务器上托管了a.com、b.org、c.net三个网站,用ip+port就不知道该访问哪个网站。

    有了host, web server就可以知道选择对应网站的数据提供服务,再用uri去查找资源。

    2019-10-02
    1
    1
  • レイン小雨
    老师我请教您一个问题,就是最近做了个ReactNative的项目,里面的网络请求使用的是新一代的浏览器Fetch请求,导致我们在开发的过程中无法在Chrome的开发者工具中查看网络请求,因为它不是一个XHR,我就很迷惑Fetch是处在什么位置,它是特殊的http吗?

    作者回复: 可能我比较孤陋寡闻,没见过Fetch请求,这个应该是自定义的请求方法吧,标准的就8个。

    只要客户端和服务器双方约定好,用什么方法名都可以,只要能理解就行。

    2019-06-29
    1
  • 右耳朵猫咪
    请问token是不是放在请求头里?

    作者回复: 如果你说的是http认证,应该放在WWW-Authenticate字段里。

    2019-06-26
    1
  • 李彬
    老师,这个telnet每次编辑模式输入
    GET /09-1 HTTP/1.1 Host: www.chrono.com或GET / HTTP/1.1 Host: www.chrono.com,按回车都没有发起请求,按一次回车会提示“无效指令”,按两次回车就进入一个完全空白的页面,只能ctrl+z终止,会提示“遗失对主机的连接”,并且报"HTTP/1.1 400 Bad Request(text/html)",结果现在wireshark只能捕获" open www.chrono.com 80"这个连接动作的tcp三次握手,然后就自动断开连接了

    作者回复: 我在自己的环境又确认了一下:

    输入" open www.chrono.com 80",显示“正在连接”,这个时候按“ctrl+]”然后回车。

    进入编辑页面,鼠标右键粘贴请求头,再按回车,服务器就会返回响应报文。

    如果还是不能再现课程里的过程,可以去网上搜一下Windows上Telnet的用法,看有没有解决办法。

    2019-06-21
    1
    1
  • 名曰蓝兮
    在centos 7系统中,telnet连接主机后,按快捷键ctrl+]进入编辑模式,然后执行模拟请求会报错:?Invalid command,不进入编辑模式就可以正常进行实验了~记录一下

    作者回复: Linux上的Telnet和Windows的操作不一样,不用那么麻烦,可以直接粘贴数据发送。

    2019-06-19
    1
  • xing.org1^
    老师,请问为什么请求头太大会占用服务器资源呢?

    作者回复: 因为服务器必须分配内存存储请求头,然后才能处理,如果头很大,比如说16k,那么几万个请求就会占用非常多的内存。

    Nginx里限制头不超过4k,就是为了节约内存资源。

    2019-06-19
    1
  • Amark
    请问老师,抓包过程是http请求过程的什么位置拦截的数据包?

    作者回复: 抓包是在整个tcp协议栈,从最底层的mac,所以上面传输的所有数据都能够抓到。

    只要你点开始捕获,之后的http请求响应就都会被抓到,是整个过程。

    2019-06-18
    1
  • Alex
    老师您好,我初次接触openresty,我在open www.chrono.com这一步操作的时候一直连不上,命令行一直显示"正在连接www.chrono.com",然后过一会就提示"遗失对主机的连接"

    作者回复: 如果用浏览器连接没问题,那就是正确的。

    Telnet需要先按ctrl+],然后回车,进入编辑界面。

    另外,别忘了改hosts。

    有问题随时提。

    2019-06-17
    1
  • qzmone
    我也不是很理解这个host字段,比如一个网站的域名解析后的IP是负载均衡的IP,负载均衡后面对应的是web主机集群,那么这个host是什么,浏览器怎么知道虚拟主机的真实IP呢

    作者回复: host字段是给Web服务器(Apache,Nginx)用的。

    解析出ip后,请求到达Nginx,因为上面运行了很多的虚拟主机,比如a.com/b.com/c.com,那么应该进那个呢?

    这个时候就要用到host字段了。如果host=b.com,那么Nginx就会使用b.com的配置提供服务。

    你最后的问题其实是反了,浏览器解析域名得到ip地址,它不知道域名对应的是不是虚拟主机,也可能是真实主机。

    2019-06-17
    1
  • 高翔Gilbert
    为什么不找人读呢?听起来好吃力

    作者回复: sorry了,本人非播音科班,听着难受就看文字吧。

    2019-06-17
    1
  • keep it simple
    做了个实验,用fiddler抓手机浏览器的包,分别用默认设置、设置为电脑版发起,观察user-agent。现象如下
    默认设置,访问时返回了手机版页面:
    Mozilla/5.0 (Linux; Android 6.0.1; SM919 Build/MXB48T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36

    切换到电脑版,返回了电脑版页面:
    Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Safari/537.36
    也就是说服务器只看user-agent中的第一段?

    作者回复: user-agent这个字段非常混乱,浏览器都声称自己是XXX并且兼容YYY,服务器处理起来也很麻烦,具体的策略跟实现有关,不能一概而论。

    2019-11-30
收起评论
48
返回
顶部