HTTP详细总结
一、前言
URI
URI(统一资源标识符):我们使用HTTP协议,主要就用来访问互联网上的各种资源,而URI就用来标识这些资源在互联网上的位置,包括两种最常见的方式:URL和URN。
语法:{方案或协议} {主机+端口} {路径} {查询} {锚点}
例如:http://www.example.com:80/path/to/myfile.html?key1=value2&key2=value2#title
,
协议
协议:http://
告诉浏览器采用何种协议,常见的协议有:
协议 | 描述 | 作用 |
---|---|---|
data | Data URIs | |
file | 本地文件传输协议 | 访问本地计算机中的文件 |
ftp | 文件传输协议 | 主要用于远程文件传输 |
http/https | 超文本传输协议/安全的超文本传输协议 | |
mailto | 电子邮件协议 | 用于通知浏览器调用本地邮件客户端发送邮件 |
ssh | 安全外壳协议 | 提供安全加密的网络传输环境 |
tel | 电话协议 | 用于通知浏览器调用本地电话客户端发起通话请求 |
urn | 统一资源名称 | 统一资源名称 |
view-source | 资源源代码 | 后接资源url可以查看资源源代码 |
ws/wss | WebSocket连接协议 | websocket |
MIME类型 {#mime}
多用途Internet邮件扩展(MIME)类型 是一种标准化的方式来表示文档的性质和格式。在Windows上我们通常使用文件扩展名来区分使用何种软件来处理文档,而浏览器通常使用MIME类型(而不是文件扩展名)来确定如何处理文档,所以服务器应该在响应对象的头部设置正确的MIME类型。
-
语法:type/subtype
MIME的组成结构非常简单;由类型与子类型两个字符串中间用'/'分隔而组成。不允许空格存在。type 表示可以被分多个子类的独立类别。subtype 表示细分后的每个类型。
MIME对大小写不敏感,但是通常使用全小写形式。 -
独立类型:独立类型表明了对文件的分类,有如下几种:
类型 描述 典型实例 text 普通文本 text/plain;text/html;text/css image 图像,包括动图 image/gif;image/png;image/jpeg;image/bmp audio 音频 audio/midi;audio/mpeg;audio/ogg video 视频 video/webm;video/ogg application 某种二进制数据 application/octet-stream;application/xml;application/pdf 对于text类型,如果没有特定的子类型,则默认text/plain;类似的,application类型默认为application/octet-stream。
-
Multipart类型:复合文件一种类型。
multipart/form-data:用于表示表单POST时的数据。
-
multipart/byteranges:用于把部分的响应报文发送回浏览器。
使用状态码206 Partial Content来发送整个文件的子集,而HTTP对不能处理的复合文件使用特殊的方式:将信息直接传送给浏览器(这时可能会建立一个“另存为”窗口,但是却不知道如何去显示内联文件。)。当发送状态码206Partial Content 时,这个MIME类型用于指出这个文件由若干部分组成,每一个都有其请求范围。就像其他很多类型Content-Type使用分隔符来制定分界线。每一个不同的部分都有Content-Type这样的HTTP头来说明文件的实际类型,以及 Content-Range来说明其范围。
-
几种重要的MIME类型
- text/plain:纯文本
- text/css:css样式表文件
- text/html:html文件
- application/octet-stream:二进制流
- application/javascript:js文件
- application/json:json文件
- application/xml:xml文件
- application/pdf:pdf文件
- multipart/form-data:表单提交
如下为一个包含附件的提交:
以上请求需注意的是,Content-Type为multipart/form-data,且请求体里可以看到,除上传文件CustomMethod.dll外,还包括property1和property2两对值一并提交了。POST /site/file/upload HTTP/1.1 Host: myhost Connection: keep-alive Content-Length: 46492 Origin: http://myhost User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36 Content-Type: multipart/form-data; boundary=----WebKitFormBoundary5jBgoLfQFCAe9xpw Accept: */* Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: Company_ID=EINK; ------WebKitFormBoundary5jBgoLfQFCAe9xpw Content-Disposition: form-data; name="property1" CustomMethod.dll ------WebKitFormBoundary5jBgoLfQFCAe9xpw Content-Disposition: form-data; name="property2" 30 ------WebKitFormBoundary5jBgoLfQFCAe9xpw Content-Disposition: form-data; name="file"; filename="CustomMethod.dll" Content-Type: application/x-msdownload ------WebKitFormBoundary5jBgoLfQFCAe9xpw--
-
几种可能会用到的MIME类型
- image/jpeg:jpeg、jpg文件
- image/gif:gif文件
- image/x-ico:icon图标文件
- application/msword:Word文件
- application/vnd.ms-excel:excel文件
- application/x-rar-compressed:rar文件
-
设置正确MIME类型的重要性
再次重申,浏览器是以MIME作为处理文件的依据,如果一个link标签内的css文件被指定为text/plain,浏览器不会将其解析为css文件。
二、HTTP概览
HTTP协议
HTTP协议(超文本传输协议)是互联网上最广泛的一种网络协议。设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法。通过HTTP或者HTTPS协议请求的资源由统一资源标识符来标识。
HTTP请求过程
客户端(User-Agent)
一般为用户浏览器。
服务端(Web-Server)
一台或一组能提供Web服务的计算机。
代理(Proxies)
在浏览器和服务器之间有许多机器或设备转发了HTTP消息,这些设备分布于传输层、网络层或者物理层上,而有一些位于应用层,他们就是代理,代理对于客户端和服务器来说既可以是透明的,也可以是可见的。而代理既可能改变HTTP消息,也可能不改变,这就要看代理的具体作用了。各种代理有如下几种作用:
- 转发:单纯不做任何操作,比如大多数企业内网,通过一台代理服务器来访问外网。或将特定请求转发到其他地址。
- 缓存。
- 过滤:同样一些企业内网的代理会做一些反病毒扫描、家长控制等。
- 认证:完成一些身份认证的工作,或者对不同资源进行权限管理。
- 日志:比如在现有系统不做改变的情况下,使用nginx代理实现日志记录。
特点
- 可读性:HTTP协议被设计的很简单易读。
- 可扩展:扩展性强,只需客户端与服务器就新header达成一致即可轻易实现扩展。
- 无状态:HTTP是无状态的,在同一个连接中的两次连续的请求是没有关系的。尽管如此,仍然可以通过把cookie添加到header中获取上下文信息来解决此问题。
作用
- 缓存:使用cache相关的header等来控制缓存。服务端能告诉浏览器或代理哪些文档需要被缓存,缓存多久;同时浏览器也能告诉代理哪些文档请求需要忽略。
- 认证:使用Authenticate相似的头部即可实现对特定的页面进行保护,同时也可以通过cookie来实现相应功能。
- 代理:代理就是基于HTTP或socket层面。
- 会话:使用cookie来传递上下文信息。
- 开放同源策略:可以通过修改HTTP头部一些信息解除同源限制。
三、HTTP报文
HTTP会话包括客户端发送请求、建立连接、服务端返回响应,建立连接发生在传输层,通常为TCP连接,默认80端口。C-S模型不允许服务器在没有显示发送请求时向客户端发送数据,不过现在开发使用其他技术,如XMLHTTPRequest或Fetch API周期性地请求服务器,以达到类似效果。
客户端发送请求与服务端返回响应分别对应HTTP报文中得请求报文和响应报文。
HTTP请求报文
HTTP请求报文包括请求行、请求头、空行和请求体四部分组成,空行用来隔开请求头与请求体,如下图:
百度首页的请求头如下:
GET / HTTP/1.1
Host: www.baidu.com
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: BAIDUID=51DCFC83838328932381GS62F770DA7:FG=1;
请求行
请求行由请求的HTTP方法、路径、协议版本三部分组成。
请求方法有:GET、POST、PUT、DELETE、HEAD、CONNECT、OPTIONS、TRACE、PATCH。常用的有GET、POST、PUT、DELETE。
- GET:GET方法请求一个指定资源的表示形式. 使用GET的请求应该只被用于获取数据。
- HEAD:HEAD方法请求一个与GET请求的响应相同的响应,但没有响应体。
- POST:POST方法用于将实体提交到指定的资源,通常导致状态或服务器上的副作用的更改。
- PUT:PUT方法用请求有效载荷替换目标资源的所有当前表示。
- DELETE:DELETE方法删除指定的资源。
- CONNECT:CONNECT方法建立一个到由目标资源标识的服务器的隧道。
- OPTIONS:OPTIONS方法用于描述目标资源的通信选项。
- TRACE:TRACE方法沿着到目标资源的路径执行一个消息环回测试。
- PATCH:PATCH方法用于对资源应用部分修改。
请求头
请求头包含多组由:
分隔的键值对,用来告诉服务器一些与请求相关的信息。常用头信息后边有写。
请求体
请求体同请求一起发送到服务器端,一般用来更新数据,常用在POST请求,而GET、OPTIONS亲请求一般用来获取数据,不需要发送请求体。
请求体分为两种:
- Single-resource bodies:由单个文件组成,该类型body通过Content-Type和Content-Length来定义,如json数据,
Content-Type:application/json
,如下:POST /GAIA_FB/Account/Logon HTTP/1.1 Host: testsite.com Connection: keep-alive Content-Length: 144 Accept: application/json, text/javascript, */*; q=0.01 Origin: http://www.testsite.com X-Requested-With: XMLHttpRequest User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Content-Type: application/json Referer: http://testsite.com/Account/logon Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: CULTURE_CODE=zh-CN; loginfrom=aVgy0ql016TDtCp8QAsi0Q== { account: "rascalquan", cultureCode: "zh-CN", password: "112233", returnUrl: "" }
- Multiple-resource bodies:由多部分 body 组成,每一部分包含不同的信息位。通常是和HTML Forms连系在一起,如附件上传时,如下,
Content-Type:multipart/form-data
:POST /GAIA_FB/file/upload HTTP/1.1 Host: testsite.com Connection: keep-alive Content-Length: 32159 Origin: http://www.testsite.com User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryI93ysNPTg7vxds7O Accept: */* Referer: http://www.testsite.com/fileUpload Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: CULTURE_CODE=zh-CN; loginfrom=aVgy0ql016TDtCp8QAsi0Q==; ------WebKitFormBoundaryI93ysNPTg7vxds7O Content-Disposition: form-data; name="prop1" UsbkeyImport.xls ------WebKitFormBoundaryI93ysNPTg7vxds7O Content-Disposition: form-data; name="file"; filename="UsbkeyImport.xls" Content-Type: application/vnd.ms-excel ------WebKitFormBoundaryI93ysNPTg7vxds7O--
HTTP响应报文
HTTP响应报文包括状态行、响应头、空行和响应报文体四部分组成,空行用来隔开响应头与响应报文体,如下图:
状态行
状态行由协议版本、响应码、响应状态描述组成。
状态码
响应头
响应头与请求头类似,包含多组由:
分隔的键值对,用来描述服务器的一些相关的信息。
响应报文体
响应报文体即响应的主体,如HTML页面、JPEG图片、PDF文件等等,浏览器会根据响应头中的Content-Type设置的MIME选择相应的处理方式,是该呈现还是下载附件。
常用Header
各Header按照用途可以分为请求头(只用在请求时)、响应头(只用在响应时)、通用头(请求和响应时都可使用)三种。有些可在代码里修改,有些不可修改,由用户代理自行维护,如Date头。
- Accept:表示客户端可以接受或处理的MIME类型,服务端从多项类型中选择一项并以Content-Type回应。
基本语法:
。/ [;q=权重]
其中q代表权重,表示优先级,多个类型之间用,
隔开。
或/* */*
代表任意子类型或任意类型。
示例:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8 - Accept-Charset:表示客户端可以接受或处理的字符集类型,服务端从多项类型中选择一项并以Content-Type回应。
基本语法:
,字符集如utf-8或iso-8859-15等。[;q=权重]
其中q代表权重,表示优先级,多个类型之间用,
隔开。
不过,一般浏览器不会也不用提供,以各类型的默认值即可。 - Accept-Encoding:表示客户端可以接受或处理的压缩编码类型。
- Accept-Language:表示客户端可以接受的自然语言。
- Connection:表示当前事务完成后是否关闭连接。
用法:
Connection:Keep-Alive
//保持连接
Connection:Close
//关闭连接 - Content-Disposition:指示响应的内容作为内联形式内嵌到页面中还是附件形式下载到本地。
用法:
Content-Disposition:inline
//内联形式,默认
Content-Disposition:attachment;filename="filename1.jpg"
//附件形式,浏览器会弹出另存为的提示框,filename为下载时另存为的文件名
Content-Disposition:form-data;name="field1";filename="filename1.jpg"
//用于类型为multipate body时,多为表单提交,可看上文中MIME类型的Multipart类型中multipart/form-data
四、Cookie
概述
HTTP Cookie是服务器发送到浏览器并保存在浏览器端的一小块数据,它会在浏览器下次同一服务器时一并发送给服务器。Cookie使无状态的HTTP记录稳定的状态成为了可能。
Cookie作用
- 会话管理:记录用户的登录状态、访问状态等,记录登录状态是cookie最常用的功能,通常服务器会在用户登录成功后下发一个sessionid以免去每次登录。记录访问状态可以保持用户的浏览记录,如购物车等信息。
- 个性设置:记录用户一些自定义设置,如将主题、语言等的设置保存在cookie中。
- 行为追踪:跟踪并分析用户的行为,如Google投放的广告等。
使用
创建Cookie
服务器通过在响应头添加Set-Cookie:
来告知浏览器需要保存cookie,当浏览器看到该响应头后就会把其中的cookie键值对保存下来,并在后续的请求中自动添加cookie:
请求头。
- 会话期Cookie:像
这样的cookie就是会话期cookie,只在这次会话期间生效,当关闭浏览器后就会自动删除。= - 持久性Cookie:通过为cookie设置Expires(特定过期时间)或Max-Age(有效秒数)属性可以实现cookie的持久化保存。如
Set-Cookie: id=a3fWa; Expires=Thu Nov 22 2018 16:25:27 GMT;
设置cookie id为特定的失效时间2018/11/22 16:25:27。如Set-Cookie: id=a3fWa; Max-Age=1000;
设置cookie id在1000秒后失效。
访问Cookie
- HTTP访问:从Cookie请求头直接访问。
- Javascript访问:对于非HttpOnly标记的cookie可以通过document.cookie访问或修改cookie。
Cookie安全
可以为cookie添加Secure或HttpOnly标记。
添加了Secure标记的cookie只有在https加密时才会发送给服务器以保证cookie不会被截取,如Set-Cookie: id=a3fWa;Secure
。尽管如此,还是不建议将敏感信息,如密码,放在cookie中
添加了HttpOnly标记的cookie只在发送HTTP消息时才能被发现,而不能被js抓取到,避免了XSS(跨域脚本攻击)的危害。
Cookie作用域
Domain 和 Path 标识定义了Cookie的作用域:即Cookie应该发送给哪些URL。
Domain标识了哪些主机可以接收cookie,如果不指定,则默认只有当前主机(不包含子域名)可以接收该cookie;如果指定了Domain,则一般包括子域名也可接收。
Path标识了哪些路径(及其子路径)可以接收该cookie。如Set-Cookie: id=a3fWa;path=\docs
。
五、Cache
Cache概述
常用的缓存有数据库缓存、服务器端缓存、浏览器端缓存等,Cache即浏览器端缓存,当浏览器请求一个web资源后,在浏览器端保存一份该资源的副本,当下次再次请求相同资源时,如果满足特定条件则直接返回该资源的副本,而无需再次发起请求。
合理利用缓存有助于:
- 缓解服务器压力
- 减少网络带宽
- 加快响应速度
Cache三大策略
Cache缓存的工作原理包括三大策略:缓存存储策略、缓存过期策略、缓存验证策略。三大策略分别处理缓存的是否存储、何时过期、验证缓存是否有效。
缓存存储策略
与缓存存储策略相关的有Cache-Control的几个header和Pragma
- Cache-Control:Private:缓存,只能被单个用户缓存,不能作为共享缓存(即代理服务器不能缓存它),可以缓存响应内容。
- Cache-Control:Public:缓存,可以被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存。
- Cache-Control:no-cache:缓存,但强制将请求提交给原始服务器进行验证。
- Cache-Control:only-if-cached:表明客户端只接受已缓存的响应,并且不要向原始服务器检查是否有更新的拷贝。
- Cache-Control:max-age:缓存,设置缓存存储的最大周期,超过这个时间缓存被认为过期(单位秒)。
- Cache-Control:no-store:不缓存。
- Pragma:no-cache:同Cache--Control:no-cache,强制缓存验证,上古产物,不建议使用了。
无法被缓存的情况:
- 响应头中包含Cache-Control:no-store头的请求无法被缓存
- 响应头中包含Cache-Control:max-age=0的请求无法被缓存
- 响应头中不包含Last-Modified/Etag,也不包含Cache-Control/Expires的请求无法被缓存
- POST请求无法被缓存
缓存过期策略
与缓存过期策略相关的HTTP头:
- Cache-Control:max-age:缓存存储的最大周期,优先级高于Expires。
- Expires:即在此时候之后,缓存过期。
缓存过期并不意味着缓存被删除或失效,即使缓存过期了还是有可能使用的。后边的缓存验证策略中会讲到。
缓存验证策略
当缓存的文档过期后,需要进行缓存验证或者重新获取资源。只有在服务器返回强校验器或者弱校验器时才会进行验证。
弱校验器:Last-Modified
响应头可以作为一种弱校验器。说它弱是因为它只能精确到一秒。如果响应头里含有这个信息,客户端可以在后续的请求中带上 If-Modified-Since:Last-Modified-value
或If-Unmodified-Since:Last-Modified-value
来验证缓存。
- If-Modified-Since:是否在指定时间之后做了修改,将上次响应头中的Last-Modified的值发给服务器验证,如果服务器资源在此之后有做修改,则返回新的资源,状态码200,否则返回一个不带消息主体的304响应,以告诉浏览器使用缓存即可。通常只用在GET、HEAD请求中。
- If-Unmodified-Since:是否在指定时间之后未做修改,类似if-Modified-Since,不过通常只用于POST请求。如并发修改时,未防止覆盖修改,如果请求的资源在指定时间之后发生了修改,则返回412(前提条件失效)错误,如果未修改,则做出修改,并返回200。
强校验器:ETag可以唯一标识一份资源,可以通过特定计算获得(如md5),如果响应头里含有这个信息,客户端可以在后续的请求中带上 If-Match:ETag-value
或If-None-Match:ETag-value
来验证缓存。尽管ETag比Last-Modified准确很多,但是对ETag的计算会消耗一定的服务器资源或客户端资源。
- If-None-Match:配合ETag使用,当且仅当服务器上没有任何资源的 ETag 属性值与这个首部中列出的相匹配的时候,服务器端会才返回所请求的资源(GET、HEAD)或对服务器资源做修改(POST、PUT),响应码为200 ,否则返回304响应码(GET、HEAD)或412错误(POST、PUT)。
- If-Match:类似If-None-Match,验证通过则返回200,否则返回412。If-Match与if-None-Match优先级高于if-Modified-Since与if-Unmodified-Since。
如图:
访问方式
用户访问方式可以总结为三种:
- 直接访问:点击超链接、地址栏输入URL的方式,会进行普通的缓存验证策略。
- 刷新页面:点击刷新按钮、F5、右键重新加载,会强制验证缓存,在请求头中加上
If-Modified-Since
或If-None-Match
,如果满足验证则返回资源,否则返回304来从缓存获取资源。 - Ctrl+F5:不采取缓存,强制服务器重新发送完整请求,这时请求头即没有
If-Modified-Since
也没有If-None-Match
。
缓存实践
综上对各种HTTP缓存控制头部的对比以及用户可能出现的浏览器刷新行为的讨论,当我们在一个项目上做http缓存的应用时,我们实际上还是会把上述提及的大多数首部字段均使用上。
- Expires / Cache-Control
Expires用时刻来标识失效时间,不免收到时间同步的影响,而Cache-Control使用时间间隔很好的解决了这个问题。 但是 Cache-Control 是 HTTP1.1 才有的,不适用于 HTTP1.0,而 Expires 既适用于 HTTP1.0,也适用于 HTTP1.1,所以说在大多数情况下同时发送这两个头会是一个更好的选择,当客户端两种头都能解析的时候,会优先使用 Cache-Control。 - Last-Modified / ETag
二者都是通过某个标识值来请求资源, 如果服务器端的资源没有变化,则自动返回 HTTP 304 (Not Changed)状态码,内容为空,这样就节省了传输数据量。而当资源发生比那话后,返回和第一次请求时类似。从而保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源。
其中Last-Modified使用文件最后修改作为文件标识值,它无法处理文件一秒内多次修改的情况,而且只要文件修改了哪怕文件实质内容没有修改,也会重新返回资源内容;ETag作为“被请求变量的实体值”,其完全可以解决Last-Modified头部的问题,但是其计算过程需要耗费服务器资源。 - from-cache / 304
Expires和Cache-Control都有一个问题就是服务端作为的修改,如果还在缓存时效里,那么客户端是不会去请求服务端资源的(非刷新),这就存在一个资源版本不符的问题,而强制刷新一定会发起HTTP请求并返回资源内容,无论该内容在这段时间内是否修改过;而Last-Modified和Etag每次请求资源都会发起请求,哪怕是很久都不会有修改的资源,都至少有一次请求响应的消耗。
对于所有可缓存资源,指定一个Expires或Cache-Control max-age以及一个Last-Modified或ETag至关重要。同时使用前者和后者可以很好的相互适应。
前者不需要每次都发起一次请求来校验资源时效性,后者保证当资源未出现修改的时候不需要重新发送该资源。而在用户的不同刷新页面行为中,二者的结合也能很好的利用HTTP缓存控制特性,无论是在地址栏输入URI然后输入回车进行访问,还是点击刷新按钮,浏览器都能充分利用缓存内容,避免进行不必要的请求与数据传输。 - 避免304
对于一些css或js等静态文件,不会频繁更新,但每次更新后又需保证客户端能获取到最新,而不是从缓存中获取。可以在url后添加版本号(?md5
或?version
)的方式模拟服务端ETag的原理。而每次更新资源后只需更新其版本号即可,同时为其设置一个较长的Expire或max-age。