仍然记得研究生毕业时,也就是15年底(12月初)去一家公司面试,第三轮时那面试官扔我一本《HTTP权威指南 》,说"给你1小时,然后你再跟我讲讲什么是HTTP"。
时光飞逝,18年5月,工作正好两年,换工作。我一个两年工作经验的后端Java开发再一次被问到:讲述一下HTTP。
以上即是菜逼我写这篇文章的缘由。记录一下自己对HTTP的理解。
HTTP涉及到的东西太多,想讲清楚不容易。
超文本传输协议,用来传输诸如 HTML 的超媒体文档等数据的,是基于 TCP/IP 的应用层协议。被设计用于Web浏览器和Web服务器之间的通信,是一种 client-server 协议,通过交换各自的消息(与数据流正好相反)来进行交互,客户端如浏览器发出的消息叫做 requests,服务端回应的消息就叫做 responses。HTTP是无状态协议,意味着服务器不会在两个请求之间保留任何数据(状态)。
HTTP 使用面向连接的TCP作为传输层协议。HTTP 本身无连接。
HTTP 请求由三部分组成:请求行Request line、请求头Request header、请求正文body;header和body之间有个空行。
请求行以一个方法符号开头,以空格分开,后面跟着请求 URI 和协议的版本,格式如下:
Method Request-URI HTTP-Version CRLF
Method表示请求方法;Request-URI是一个统一资源标识符;HTTP-Version表示请求的HTTP协议版本;
CRLF表示回车和换行(除了作为结尾的CRLF外,不允许出现单独的CR或LF字符)。
HTTP响应也是由三个部分组成:响应行Response line、响应报头Response header、响应正文body;header和body之间也有个空行。
Response 消息中的第一行叫做状态行,由HTTP协议版本号, 状态码, 状态消息 三部分组成。状态行格式如下:
HTTP-Version Status-Code Reason-Phrase CRLF
Reason-Phrase表示状态代码的文本描述。
其主要特点:
TCP、HTTP、UDP,都是通信协议。TCP/IP是个协议组,可分为四个层次:网络接口层、网络层、传输层和应用层。
网络层有IP协议、ICMP协议、ARP协议、RARP协议和BOOTP协议。
传输层中有TCP协议与UDP协议。
应用层有FTP、HTTP、TELNET、SMTP、DNS等协议。
HTTP本身就是一个协议,是从Web服务器传输超文本到本地浏览器的传送协议。
TCP 被称为“面向连接”的传输层协议。TCP 比 UDP 更可靠。HTTP是基于 TCP/IP 的,有很多常见的应用层协议是以 TCP 为基础的,如FTP、SMTP、POP、IMAP等。通常而言,HTTP基于TCP / IP层,但可以在任何可靠的传输层上使用;也就是说,一个不会静默丢失消息的协议,如UDP。
Socket:
这是为了实现以上的通信过程而建立成来的通信管道,其真实的代表是客户端和服务器端的一个通信进程,双方进程通过socket进行通信,而通信的规则采用指定的协议。socket只是一种连接模式,不是协议,TCP、UDP,简单的说(虽然不准确)是两个最基本的协议,很多其它协议都是基于这两个协议如,http就是基于TCP的,用socket可以创建TCP连接,也可以创建UDP连接,用socket可以创建任何协议的连接,因为其它协议都是基于此的。
HTTP 对 TCP 连接的使用,分为两种方式:短连接和长连接(持久连接,Keep-Alive或Persistent Connection)
浏览器发起一个TCP连接请求,获得HTML源码,解析到HTML里面有很多外部超媒体资源(图片,CSS,JS),如果是短连接模式,则一个HTML请求结束之后,就关闭连接;对于外部资源再开启一个连接请求,获得相应,如此多次连接多次相应。而长连接模式下,解析出来需要的所有外部资源,使用一个TCP连接(获得资源需要一定时间,故而需要保持连接 keep-alive),一次性把所有需要加载的资源获取到。
在 HTTP 1.0 版本,默认使用的是短连接(Web 诞生初期,网页相对简单,“短连接”的问题不大)。
1995年底开始制定 HTTP 1.1 草案时,网页已经开始变得复杂(网页内的图片、脚本越来越多了)。建立 TCP 连接是有“时间成本”和“CPU 成本。所以,在 HTTP 1.1 中,默认采用的是“Keep-Alive”的方式。
面试问题:TCP和HTTP的关系?TCP和UDP的区别?为什么HTTP不是基于UDP?HTTP基于TCP的使用方式(表述待完善)?
URL一般的组成成分是<协议>://<主机>:<端口号>/<路径>
,更具体的:
schema://host[:port#]/path/.../[?query-string][#anchor]
scheme:协议;host:HTTP服务器的IP地址或者域名;port:HTTP服务器的默认端口是80(可省略),使用别的端口,必须指明;path:访问资源的路径;query-string:发送给http服务器的数据;anchor:锚。
绝对URL(absolute URL)显示文件的完整路径,这意味着绝对URL本身所在的位置与被引用的实际文件的位置无关。
相对URL(relative URL)以包含URL本身的文件夹的位置为参考点,描述目标文件夹的位置。
如果路径省略URL就指到因特网上的某个主页。
虽然HTTP的请求方式有很多种(包括扩展的),在实际应用中常用的也就是get和post,其他请求方式也都可以通过这两种方式间接实现。
必须需要掌握的是GET和POST,其次是PUT和DELETE。
面试经常被问的是PUT和POST的区别?GET和POST区别?
HTTP消息由客户端到服务器的请求和服务器到客户端的响应组成。请求消息和响应消息都是由开始行(对于请求消息,开始行就是请求行,对于响应消息,开始行就是状态行),消息报头(可选),空行(只有CRLF的行),消息正文(可选)组成。每一个报头域都是由名字+“:”+空格+值 组成,消息报头域的名字是大小写无关的。
HTTP一共有四种类型的首部字段:
通用首部字段:请求报文和响应报文两方都会使用的首部。
请求首部字段:从客户端向服务器发送请求报文时使用的首部。
响应首部字段:从服务器向客户端返回响应报文时使用的首部。
实体首部字段:针对请求报文和响应报文的实体部分使用的首部。
通用首部字段
在普通报头中,有少数报头域用于所有的请求和响应消息,但并不用于被传输的实体,只用于传输的消息。
首部字段名 | 说明 | 示例 |
---|---|---|
Cache-Control | 控制缓存的行为 | Cache-Control:Public可以被任何缓存所缓存;Cache-Control:Private内容只缓存到私有缓存中;Cache-Control:no-cache所有内容都不会被缓存 |
Connection | 逐跳首部、连接的管理 | Connection:keep-alive当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接; Connection:close 代表一个Request完成后,客户端和服务器之间用于传输HTTP数据的TCP连接会关闭,当客户端再次发送Request,需要重新建立TCP连接。 |
Date | 创建报文的日期时间 | |
Pragma | 防止页面被缓存,在HTTP/1.1版本中,和Cache-Control:no-cache作用一模一样,只有一个用法,例如:Pragma:no-cache | |
Trailer | 报文末端的首部一览 | |
Transfer-Encoding | 指定报文主体的传输编码方式 | |
Upgrade | 升级为其他协议 | |
Via | 代理服务器的相关信息 | |
Warning | 错误通知 |
请求报头
请求报头允许客户端向服务器端传递请求的附加信息以及客户端自身的信息。
首部字段名 | 说明 | 示例 |
---|---|---|
Accept | 用于指定用户代理(客户端)可处理的媒体类型,如果服务器无法返回text/html类型的数据,服务器应该返回一个406错误(non acceptable)。Accept:*/* 代表浏览器可以处理所有类型。 |
Accept:text/html, image/gif |
Accept-Charset | 用于指定客户端接受的字符集,如果在请求消息中没有设置这个域,缺省是任何字符集都可以接受。 | Accept-Charset:iso-8859-1,gb2312. |
Accept-Encoding | 接受的内容编码,通常指定压缩方法,是否支持压缩,支持什么压缩方法;如果请求消息中没有设置这个域服务器假定客户端对各种内容编码都可以接受。 | |
Accept-Language | 接受的语言(自然语言),如果请求消息中没有设置这个报头域,服务器假定客户端对各种语言都可以接受。 | |
Authorization Web | 认证信息 | |
Expect | 期待服务器的特定行为 | |
From | 用户的电子邮箱地址 | |
Host | 请求资源所在服务器 | |
if-Match | 比较实体标记(ETag) | |
if-Modified-Since | 比较资源的更新时间 | |
if-None-Match | 比较实体标记(与if-Match相反) | |
if-Range | 资源未更新时发送实体Byte的范围请求 | |
if-Unmodified-Since | 比较资源的更新时间(与if-Modified-Since相反) | |
Max-Forwards | 最大传输逐跳数 | |
Proxy-Authorization | 代理服务器要求客户端的认证信息 | |
Range | 实体的字节范围请求 | |
Referer | 提供Request的上下文信息的服务器,告诉服务器是从哪个链接过来的。 | |
TE | 传输编码的优先级 | |
User-Agent | 告诉HTTP服务器,客户端使用的操作系统和浏览器的名称和版本。 |
响应首部字段
首部字段名 | 说明 |
---|---|
Accept-Ranges | 是否接受字节范围请求 |
Age | 推算资源创建经过时间 |
ETag | 资源的匹配信息 |
Location | 令客户端重定向至指定的URI |
Proxy-Authenticate | 代理服务器对客户端的认证信息 |
Reter-After | 对再次发起请求的时机要求 |
Server | 指明HTTP服务器的软件信息。 |
Vary | 代理服务器缓存的管理信息 |
www-Authenticate | 服务器对客户端的认证信息 |
实体首部字段
首部字段名 | 说明 |
---|---|
Allow | 资源可支持的HTTP方法 |
Content-Encoding | 实体主体的适用的编码方式 |
Content-Language | 实体主体的自然语言 |
Content-Length | 实体主体的大小,即发送给HTTP服务器数据的长度,单位:字节 |
Content-Location | 替代对应资源的URI |
Content-MD5 | 实体主体的报文摘要 |
Content-Range | 实体主体的位置范围 |
Content-Type | 实体主体的媒体类型,WEB服务器告诉浏览器自己响应的对象的类型和字符集。 |
Expires | 实体主体过期的日期时间,浏览器会在指定过期时间内使用本地缓存 |
Last-Modified | 资源的最后修改日期时间 |
Date | 生成消息的具体时间和日期 |
1xx(临时响应)
表示临时响应并需要请求者继续执行操作的状态代码,这些状态代码表示临时的响应。客户端在收到常规响应之前,应准备接收一个或多个 1xx 响应。
100 (继续) 请求者应当继续提出请求。 服务器返回此代码表示已收到请求的第一部分,正在等待其余部分。
101 (切换协议) 请求者已要求服务器切换协议,服务器已确认并准备切换。
2xx (成功)
201 (已创建) 请求成功并且服务器创建了新的资源。
202 (已接受) 服务器已接受请求,但尚未处理。
203 (非授权信息) 服务器已成功处理了请求,但返回的信息可能来自另一来源。
204 (无内容) 服务器成功处理了请求,但没有返回任何内容。
205 (重置内容) 服务器成功处理了请求,但没有返回任何内容。
206 (部分内容) 服务器成功处理了部分 GET 请求。
3xx (重定向)
客户端浏览器必须采取更多操作来实现请求。例如,浏览器可能不得不请求服务器上的不同的页面,或通过代理服务器重复该请求。表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向。
300 (多种选择) 针对请求,服务器可执行多种操作。 服务器可根据请求者 (user agent) 选择一项操作,或提供操作列表供请求者选择。
301 (永久移动) 请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。
302 (临时移动) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
303 (查看其他位置) 请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。
304 (未修改) 自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。
305 (使用代理) 请求者只能使用代理访问请求的网页。 如果服务器返回此响应,还表示请求者应使用代理。
307 (临时重定向) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
4xx(客户端错误)
这些状态代码表示请求可能出错,妨碍了服务器的处理。
400 (错误请求Bad Request) 客户端请求有语法错误,不能被服务器所理解。
401 (Unauthorized,未授权) 请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应。
402 要求付费。
403 (Forbidden,禁止) 服务器拒绝请求。
404 (未找到) 服务器找不到请求的网页。
405 (方法禁用) 禁用请求中指定的方法。
406 (不接受) 无法使用请求的内容特性响应请求的网页。
407 (需要代理授权) 此状态代码与 401(未授权)类似,但指定请求者应当授权使用代理。
408 (请求超时) 服务器等候请求时发生超时。
409 (冲突) 服务器在完成请求时发生冲突。 服务器必须在响应中包含有关冲突的信息。
410 (过期的/已删除) 如果请求的资源已永久删除,服务器就会返回此响应。
411 (需要有效长度) 服务器不接受不含有效内容长度标头字段的请求。
412 (未满足前提条件) 服务器未满足请求者在请求中设置的其中一个前提条件。
413 (请求实体过大) 服务器无法处理请求,因为请求实体过大,超出服务器的处理能力。
414 (请求的 URI 过长) 请求的 URI(通常为网址)过长,服务器无法处理。
415 (不支持的媒体类型) 请求的格式不受请求页面的支持。
416 (请求范围不符合要求) 如果页面无法提供请求的范围,则服务器会返回此状态代码。
417 (未满足期望值) 服务器未满足”期望”请求标头字段的要求。
另外,还有更详细的错误码,开发不常见:
401 - 访问被拒绝。IIS 定义许多不同的 401 错误,它们指明更为具体的错误原因。这些具体的错误代码在浏览器中显示,但不在 IIS 日志中显示:
401.1 - 未授权:登录失败
401.2 - 未授权:服务器配置问题导致登录失败
401.3 - 由于 ACL 对资源的限制而未获得授权、ACL 禁止访问资源。
401.4 - 未授权:授权被筛选器拒绝。
401.5 - 未授权:ISAPI/CGI 应用程序授权失败。
401.7 – 访问被 Web 服务器上的 URL 授权策略拒绝。这个错误代码为 IIS 6.0 所专用。
403 - 禁止访问:IIS 定义许多不同的 403 错误,它们指明更为具体的错误原因:
403.1 - 执行访问被禁止。
403.2 - 读访问被禁止。
403.3 - 写访问被禁止。
403.4 - 要求 SSL。
403.5 - 要求 SSL 128。
403.6 - IP 地址被拒绝。
403.7 - 要求客户端证书。
403.8 - 站点访问被拒绝。
403.9 - 用户数过多。
403.10 - 配置无效。
403.11 - 密码更改。
403.12 - 拒绝访问映射表。
403.13 - 客户端证书被吊销。
403.14 - 拒绝目录列表。
403.15 - 超出客户端访问许可/客户访问许可过多。
403.16 - 客户端证书不受信任或无效。
403.17 - 客户端证书已过期或尚未生效。
403.18 - 在当前的应用程序池中不能执行所请求的 URL。这个错误代码为 IIS 6.0 所专用。
403.19 - 不能为这个应用程序池中的客户端执行 CGI。这个错误代码为 IIS 6.0 所专用。
403.20 - Passport 登录失败。这个错误代码为 IIS 6.0 所专用。
404 - 未找到。 404.0 -(无) – 没有找到文件或目录。
404.1 - 无法在所请求的端口上访问 Web 站点。
404.2 - Web 服务扩展锁定策略阻止本请求。
404.3 - MIME 映射策略阻止本请求。
405 - 用来访问本页面的 HTTP 谓词不被允许(方法不被允许)
406 - 客户端浏览器不接受所请求页面的 MIME 类型。
407 - 要求进行代理身份验证。
410 - 永远不可用
412 - 前提、先决条件失败。
413 – 请求实体太大。
414 - 请求 URI 太长。
415 – 不支持的媒体类型。
416 – 所请求的范围无法满足。
417 – 执行失败。
423 – 锁定的错误。
5xx(服务器错误)
这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错。
500 (Internal Server Error,服务器内部错误) 服务器遇到错误,无法完成请求。
501 (尚未实施) 服务器不具备完成请求的功能。 例如,服务器无法识别请求方法时可能会返回此代码。
502 (错误网关) 服务器作为网关或代理,从上游服务器收到无效响应。
503 (服务不可用) 服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态。
504 (网关超时) 服务器作为网关或代理,但是没有及时从上游服务器收到请求。
505 (HTTP 版本不受支持) 服务器不支持请求中所用的 HTTP 协议版本。
RFC 6585 已经发布,该文档描述了 4 个新的 HTTP 状态码。
HTTP 协议还在变化?是的,HTTP 协议一直在演变,新的状态码对于开发 REST 服务或者说是基于 HTTP 的服务非常有用,下面我们为你详细介绍这四个新的状态码以及是否应该使用。
500-11 服务器关闭
500.12 - 应用程序正忙于在 Web 服务器上重新启动。
500.13 - Web 服务器太忙。
500-14 - 应用程序无效
500.15 - 不允许直接请求 Global.asa。
500.16 – UNC 授权凭据不正确。这个错误代码为 IIS 6.0 所专用。
500.18 – URL 授权存储不能打开。这个错误代码为 IIS 6.0 所专用。
500.100 - 内部 ASP 错误。
501 - 页眉值指定未实现的配置。
502 - Web 服务器用作网关或代理服务器时收到了无效响应。
502.1 - CGI 应用程序超时。
502.2 - CGI 应用程序出错。application.
503 - 服务不可用。这个错误代码为 IIS 6.0 所专用。
428 Precondition Required (要求先决条件)
先决条件是客户端发送 HTTP 请求时,如果想要请求能成功必须满足一些预设的条件。
一个好的例子就是 If-None-Match 头,经常在 GET 请求中使用,如果指定了 If-None-Match ,那么客户端只在响应中的 ETag 改变后才会重新接收回应。
先决条件的另外一个例子就是 If-Match 头,这个一般用在 PUT 请求上用于指示只更新没被改变的资源,这在多个客户端使用 HTTP 服务时用来防止彼此间不会覆盖相同内容。
当服务器端使用 428 Precondition Required 状态码时,表示客户端必须发送上述的请求头才能执行请求,这个方法为服务器提供一种有效的方法来阻止 ‘lost update’ 问题。
429 Too Many Requests (太多请求)
当你需要限制客户端请求某个服务数量时,也就是请求速度限制。
例如 ‘509 Bandwidth Limit Exceeded’. Twitter 使用 420 (这不是HTTP定义的状态码)
如果你希望限制客户端对服务的请求数,可使用 429 状态码,同时包含一个 Retry-After 响应头用于告诉客户端多长时间后可以再次请求服务。
431 Request Header Fields Too Large (请求头字段太大)
某些情况下,客户端发送 HTTP 请求头会变得很大,那么服务器可发送 431 Request Header Fields Too Large 来指明该问题。
HTTP 502 - 网关错误
用户试图通过 HTTP 或文件传输协议 (FTP) 访问一台正在运行 Internet 信息服务 (IIS) 的服务器上的内容时,IIS 返回一个表示该请求的状态的数字代码。该状态代码记录在 IIS 日志中,同时也可能在 Web 浏览器或 FTP 客户端显示。状态代码可以指明具体请求是否已成功,还可以揭示请求失败的确切原因。
511 Network Authentication Required (要求网络认证)
如果你在开发一个 HTTP 服务器,你不一定需要处理该状态码,但如果你在编写 HTTP 客户端,那这个状态码就非常重要。
如果你频繁使用笔记本和智能手机,你可能会注意到大量的公用 WIFI 服务要求你必须接受一些协议或者必须登录后才能使用。
这是通过拦截HTTP流量,当用户试图访问网络返回一个重定向和登录,这很讨厌,但是实际情况就是这样的。
使用这些“拦截”客户端,会有一些讨厌的副作用。在 RFC 中有提到这两个的例子:
如果你在登录WIFI前访问某个网站,网络设备将会拦截首个请求,这些设备往往也有自己的网站图标 ‘favicon.ico’。登录后您会发现,有一段时间内你访问的网站图标一直是WIFI登录网站的图标。
如果客户端使用HTTP请求来查找文档(可能是JSON),网络将会响应一个登录页,这样你的客户端就会解析错误并导致客户端运行异常,在现实中这种问题非常常见。
因此 511 状态码的提出就是为了解决这个问题。
如果你正在编写 HTTP 的客户端,你最好还是检查 511 状态码以确认是否需要认证后才能访问。
面试题扩展:Nginx中的499状态码?
HTTP/1.0,HTTP/1.1 以及 HTTP 2.0,区别是什么?
请求一个万维网文档需要的时间是2RTT+文档传输时间。因为要和服务器建立TCP连接需要3次握手,在第三次握手的时候捎带发送请求相关的数据,然后HTTP服务器响应报文总共是四次交互,也就是2RTT时间。再加上一些其他的开销,万维网服务器要服务大量的客户,所以每次浏览都需要建立连接,HTTP/1.0中这种非持续连接(短链接)服务器负担很重。HTTP/1.1使用持续连接(长链接),服务器在发送响应后仍然保持这条连接。
持续链接还分为流水线方式和非流水线方式。后者规定客户发送浏览请求得到响应后才能发送下一个。流水线方式客户不用等到响应就可以发送下一个请求,服务器收到请求后就可以连续响应,不用等待,节省时间。
HTTP 1.1的持续连接,需要增加新的请求头来帮助实现。
例如,Connection请求头的值为Keep-Alive时,客户端通知服务器返回本次请求结果后保持连接;Connection请求头的值为close时,客户端通知服务器返回本次请求结果后关闭连接。
HTTP 1.1还提供与身份认证、状态管理和Cache缓存等机制相关的请求头和响应头。
包括现在HTTP 2.0.
在nginx中有个配置文件mime.types,主要是标示Content-Type的文件格式。
application/x-www-form-urlencoded是常用的表单发包方式,普通的表单提交,或者js发包,默认都是通过这种方式。
multipart/form-data用在发送文件的POST包,boundary用于分割数据。当文件太长,HTTP无法在一个包之内发送完毕,就需要分割数据,分割成一个一个chunk发送给服务端,–用于区分数据快。
HTTP通信中并不存在所谓的json,而是将string转成json,即application/json可以理解为text/plain,普通字符串。
Http指纹识别的原理大致上也是相同的:记录不同服务器对Http协议执行中的微小差别进行识别。Http指纹识别比TCP/IP堆栈指纹识别复杂许多,理由是定制Http服务器的配置文件、增加插件或组件使得更改Http的响应信息变的很容易,这样使得识别变的困难;然而定制TCP/IP堆栈的行为需要对核心层进行修改,所以就容易识别。
要让服务器返回不同的Banner信息的设置是很简单的,象Apache这样的开放源代码的Http服务器,用户可以在源代码里修改Banner信息,然后重起Http服务就生效;对于没有公开源代码的Http服务器比如微软的IIS或者是Netscape,可以在存放Banner信息的Dll文件中修改,当然这样的修改的效果还是不错的。另外一种模糊Banner信息的方法是使用插件。
常用测试请求:
1:HEAD/Http/1.0发送基本的Http请求
2:DELETE/Http/1.0发送那些不被允许的请求,比如Delete请求
3:GET/Http/3.0发送一个非法版本的Http协议请求
4:GET/JUNK/1.0发送一个不正确规格的Http协议请求
Http指纹识别工具Httprint,通过运用统计学原理,组合模糊的逻辑学技术,能很有效的确定Http服务器的类型。它可以被用来收集和分析不同Http服务器产生的签名。
使用POST方法时,可以设置Content Length来定义需要传送的数据长度,例如ContentLenth:999999999,在传送完成前,内存不会释放,攻击者可以利用这个缺陷,连续向WEB服务器发送垃圾数据直至WEB服务器内存耗尽。这种攻击方法基本不会留下痕迹。
参考:http://www.cnpaf.net/Class/HTTP/0532918532667330.html