HTTP

235 阅读18分钟

1.概念

1.1 请求和响应报文

客户端发送一个请求报文给服务器,服务器根据请求报文中的信息进行处理,并将处理结果放入响应报文中返回给客户端

请求报文结构

  • 第一行是包含了请求方法,URL,协议版本

  • 接下来的多行都是请求首部Header,每个首部都有首部名称,以及对应的值

  • 一个空行用来分隔首部和内容主体body

GET http://www.example.com/ HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cache-Control: max-age=0
Host: www.example.com
If-Modified-Since: Thu, 17 Oct 2019 07:18:26 GMT
If-None-Match: "3147526947+gzip"
Proxy-Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 xxx

param1=1&param2=2
  • 响应报文结构:
    • 第一行包含协议版本,状态码以及描述,最常见的是200 OK 表示请求成功了
    • 接下来多行也是首部内容
    • 一个空行分隔首部和内容主体
    • 最后是响应的内容主体
HTTP/1.1 200 OK
Age: 529651
Cache-Control: max-age=604800
Connection: keep-alive
Content-Encoding: gzip
Content-Length: 648
Content-Type: text/html; charset=UTF-8
Date: Mon, 02 Nov 2020 17:53:39 GMT
Etag: "3147526947+ident+gzip"
Expires: Mon, 09 Nov 2020 17:53:39 GMT
Keep-Alive: timeout=4
Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
Proxy-Connection: keep-alive
Server: ECS (sjc/16DF)
Vary: Accept-Encoding
X-Cache: HIT

<!doctype html>
<html>
<head>
    <title>Example Domain</title>
	// 省略... 
</body>
</html>

1.2 URL

HTTP使用URL(Uniform Resource Locator,统一资源定位符)来定位资源,它是URI(Uniform Resource Identifier,统一资源标识符)的子集,URL在URI的基础上增加了定位能力

  • 例如 urn:isbn: 0451450523 用来定义一个书籍名称,但是却没有表示怎么找到这本书

image.png

  • wikipedia: 统一资源标识符
  • wikipedia: URL
  • rfc2616: 3.2.2 http URL
  • What is the difference between a URI , a URL and a URN

2.HTTP方法

客户端发送的请求报文第一行为请求行,包含了方法字段

2.1 GET

获取资源

当前网络请求中,绝大部分使用的是GET方法

2.2 Head

获得报文首部

  • 和GET方法类似,但是不返回报文实体主体部分
  • 主要用于确认URL的有效性以及资源更新的日期时间等

2.3 POST

获取实体主体

POST主要用于传输数据,而GET主要用来获取资源

2.4 PUT

上传文件

由于自身不带验证机制,任何人都可以上传文件,因此存在安全性问题,一般不使用该方法

PUT /new.html HTTP/1.1
Host: example.com
Content-type: text/html
Content-length: 16

<p>New File</p>

2.5 PATCH

对资源进行部分修改

PUT也可以用于修改资源,但是只能完全替代原始资源,PATCH允许部分修改

PATCH /file.txt HTTP/1.1
Host: www.example.com
Content-Type: application/example
If-Match: "e0023aa4e"
Content-Length: 100

[description of changes]

2.6 DELETE

删除文件

与PUT功能相反,并且同样不带检验机制

DELETE /file.html HTTP/1.1

2.7 OPTIONS

查询支持的方法

查询指定URL能够支持的方法

会返回 Allow: GET POST HEAD OPTIONS这样的类容

2.8 CONNECT

要求在与代理服务器通信时建立隧道

使用SSL(Secure Sockets Layer , 安全套阶层)和TLS(Transport Layer Security , 传输层安全)协议把通信内容加密后经网络隧道传输

CONNECT www.example.com:443 HTTP/1.1

image.png

2.9 Trace

追踪路径

服务器会将通信路径返回给客户端

  • 发送请求时,在Max-Forwards首部字段中填入数值,每经过一个服务器就会减1,当数值为0时就停止传输

  • 通常不会使用Trance,并且它容易收到XST攻击(Cross - Site Tracing)

3.HTTP状态码

服务器返回的响应报文中第一行为状态行,包含了状态码以及原因短语,用来告知客户端请求结果

3.1 1XX 信息

  • 100 Continue: 表明到目前为止都很正常,客户端可以继续发送请求或者忽略这个相应

3.2 2XX 成功

  • 200 OK

  • 204 No Content: 请求已经成功处理,但是返回的相应报文不包含主体部分。一般在只需要从客户端往服务端发送信息,而不需要返回数据时使用

  • 206 Partial Content: 表示客户端进行了范围请求,响应报文包含由Content - Range 指定范围的实体内容

3.3 3XX 重定向

  • 301 Moved Permanently: 永久重定向

  • 302 Found: 临时重定向

  • 303 See Other: 和302有着相同的功能,但是303明确要求客户端应该采用GET方法获取资源

  • 注: 虽然HTTP协议规定301和302状态下重定向时不允许把POST方法改成GET方法,但是大多数浏览器都会在301和302和303状态下的重定向把POST方法改成GET方法

  • 304 Not Modified: 如果请求报文首部包含一些条件,If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since,如果不满足条件,则服务器会返回 304 状态码。

  • 307 Temporary Redirect:临时重定向,与 302 的含义类似,但是 307 要求浏览器不会把重定向请求的 POST 方法改成 GET 方法。

3.4 4XX 客户端错误

  • 400 Bad Request:请求报文中存在语法错误。

  • 401 Unauthorized:该状态码表示发送的请求需要有认证信息(BASIC 认证、DIGEST 认证)。如果之前已进行过一次请求,则表示用户认证失败。

  • 403 Forbidden:请求被拒绝。

  • 404 Not Found

3.5 5XX 服务器错误

  • 500 Internal Server Error :服务器正在执行请求时发生错误。

  • 503 Service Unavailable :服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。

4.HTTP首部

有四种类型的首部字段: 通用首部字段,请求首部字段,响应首部字段和实体首部字段

各种首部字段及其含义如下

image.png

image.png

image.png

image.png

5.具体应用

5.1 连接管理

image.png

5.1.1 短链接与长连接

当浏览器访问一个包含多张图片的HTML页面时,除了请求访问的HTML页面资源,还会请求图片资源。如果每进行一次HTTP通信就要建立一次TCP连接,那么开销会很大

  • 长连接只需要建立一次TCP连接就能进行多次HTTP通信
    • 从HTTP/1.1 开始默认是长连接的,如果要断开连接,需要由客户端或者服务器端提出断开,使用Connection : close;
    • 在HTTP/1.1之前默认是短链接的,如果需要使用长连接,则使用Connection : keep-Alive

5.1.2 流水线

默认情况下,HTTP请求是按顺序发出的,下一个请求只有在当前请求收到相应之后才会被发出。由于受到网络延迟和带宽的限制,在下一个请求被发送到服务器之前,可能需要等待很长时间

  • 流水线时在同一条长连接上连续发出请求,而不同等待响应返回,这样可以减少延迟

5.2 Cookie

HTTP协议是无状态的,主要是为了让HTTP协议尽可能的简单,使得它能够处理大量事务。HTTP/1.1 引入Cookie来保存状态信息

Cookie是服务器发送到用户浏览器并保存在本地的一小块数据,他会在浏览器之后向同一服务器再次发送请求时被携带上,用于告知服务端两个请求是否来自同一浏览器。由于之后每次请求都会需要携带Cookie数据,因此会带来额外的性能开销(尤其是在移动环境下)

Cookie曾一度用于客户端数据的存储,因为当时并没有其它适合的存储办法而作为唯一的存储手段,但现在随着现代浏览器开始支持各种各样的存储方式,Cookie渐渐被淘汰。新的浏览器API已经允许开发者直接将数据存储到本地,如使用Web Storage API(本地存储和会话存储)或IndexedDB

5.2.1 用途:

  • 会话状态管理(如用户登录状态、购物车、游戏分数或其他需要记录的信息)
  • 个性化设置(如用户自定义、主体等)
  • 浏览器行为(如跟踪分析用户行为等)

5.2.2 创建过程:

  • 服务器发送的响应报文包含Set-Cookie首部字段,客户端得到响应报文后把Cookie内容保存到浏览器中
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry

[page content]
  • 客户端之后对同一个服务器发送请求时,会从浏览器中提取出Cookie信息并通过Cookie请求首部字段发送给服务器
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry

5.2.3 分类:

  • 会话期Cookie: 浏览器关闭之后它会被自动删除,也就是说他仅仅在会话期内有效
  • 持久性Cookie: **指定过期时间(Expires)或有效期(max-age)**之后就成为了持久性的Cookie
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;

5.2.4 作用域

Domain标识制定了哪些主机可以接受Cookie。如果不指定,默认当前文档的主句(不包含子域名)。如果制定了Domain,则一般包含子域名

  • 例如: 如果设置Domain = Mozilla.org,则Cookie也包含在子域名中(如develop.mozilla.org)

Path标识制定了主机下的哪些路径可以接受Cookie(该URL路径必须存在于请求URL中)。以字符%x2F("/")作为路径分隔符,子路径也会被匹配

  • 例如: Path = /docs , 则以下地址都会匹配:
    • /docs
    • /docs/Web/
    • /docs/Web/HTTP

5.2.5 JavaScript

浏览器通过document.cookie 属性可以创建新的Cookie,也可以通过该属性访问非HTTPOnly标记的Cookie

document.cookie = "yummy_cookie=choco";
document.cookie = "tasty_cookie=strawberry";
console.log(document.cookie);

5.2.6 HttpOnly

标记为HttpOnly的Cookie不能被JavaScript脚本调用。跨站脚本攻击XSS常常使用JavaScript的document.cookie API窃取用户的Cookie信息,英雌使用HttpOnly标记可以在一定程度上避免XSS攻击

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly

5.2.7 Secure

标记为Secure的Cookie只能通过被HTTPS协议加密过的请求发送过服务端。即便设置了Secure标记,敏感信息也不应该通过Cookie传输,因为Cookie有其固有的不安全性,Secure标记也无法提供的安全保障。

5.2.8 Session

除了可以将用户信息通过Cookie存储在用户浏览器中,也可以利用Session存储在服务器端,存储在服务器端的信息更加安全

Session可以存储在服务器上的文件、数据库或者内存中。也可以将Session存储在Redis这种内存型数据库中,效率会更高

使用Session维护用户登录状态的过程如下:

  • 用户进行登陆时,用户提交包含用户名和密码的表单,放入HTTP请求报文中;
  • 服务器验证该用户名和密码,如果正确则把用户信息存储到Redis中,他在Redis中的Key称之为Session ID;
  • 服务器返回的响应报文的Set-Cookie首部字段包含了这个Session ID,客户端收到响应报文之后将该Cookie值存入到浏览器中
  • 客户端之后对同一个服务器进行请求时会包含该Cookie值,服务器收到之后提取出Session ID,从Redis中取出用户信息,继续之前的业务操作

应该注意Session ID的安全性问题,不能让它被恶意攻击者轻易获取,那么就不能产生一个容易被猜到的Session ID值。此外,还需要经常重新生成Session ID。在对安全性要求极高的场景下,如: 转账等操作,除了使用Session管理用户状态之外,还需要对用户进行重新验证,比如重新输入密码,或者使用短信验证码等方式

5.2.9 浏览器禁用Cookie

此时无法使用Cookie来保存用户信息,只能使用Session。除此之外,不能再将Session ID存放到Cookie中,而是使用URL重写技术,将Session ID 作为URL的参数进行传递

5.2.10 Cookie与Session选择

  • Cookie 只能存储ASCII码字符串,而Session可以存储任何类型的数据,因此在考虑数据复杂性时首选Session;

  • Cookie存储在浏览器中,容易被恶意查看。如果非要将一些隐私数据存放在Cookie中,可以将Cookie值进行加密,然后在服务器进行解密;

  • 对于大型网站,如果用户所有的信息都存储在Session中,那么开销是非常大的,因此不建议将所有的用户信息都存储到Session中

5.3.1缓存

5.3.1 优点

  • 缓解服务器压力;
  • 减低客户端获取资源的延迟: 缓存通常位于内存中,读取缓存的速度非常快。并且缓存服务器在地理位置上也有可能比源服务器来的近,比如浏览器缓存

5.3.2 实现方法

  • 让代理服务进行缓存;
  • 让客户端浏览器进行缓存

5.3.3 Cache-Control

  • HTTP/1.1 通过Cache-Control首部字段来控制缓存

5.3.3.1 禁止进行缓存

no-store指令规定不能对请求或响应的任何一部分进行缓存

Cache-Control: no-store

5.3.3.2 强制确认缓存

no-cache指令规定缓存服务器需要先向源服务器验证缓存资源的有效性,只有当缓存资源有效时才能使用该缓存对客户端的请求进行响应

Cache-Control: no-cache

5.3.3.3 私有缓存和公共缓存

private指令规定了将资源作为私有缓存,只能被单独用户使用,一般存储在用户浏览器中

Cache-Control: private

public指令规定了将资源作为公共缓存,可以被多个用户使用,一般存储在代理服务器中

Cache-Control: public

5.3.3.4 缓存过期机制

max-age指令出现在请求报文,并且缓存资源的缓存时间小于该指令指定的时间,那么就能接受该缓存 max-age指令出现在响应报文,标识缓存资源在缓存服务器中保存时间

Cache-Control: max-age=31536000

Expires首部字段也可以用于告知缓存服务器该资源什么时候会过期

Expires: Wed, 04 Jul 2012 08:26:05 GMT
  • 在HTTP/1.1中,会优先处理max-age指令;
  • 在HTTP/1.0中,max-age指令会被忽略掉

5.3.4 缓存验证

需要先了解ETag首部字段的含义,它是资源的唯一标识。URL不能唯一表示资源

  • 例如: google.com 有中文和英文两个资源只有ETag才能对这两个资源进行唯一标识
ETag: "82e22293907ce725faf67773957acd12"

可以将缓存资源的ETag值放入If-None-Match首部,服务器收到该请求后,判断缓存资源的ETag值和资源的最新ETag值是否一致,如果一致则表示缓存资源有效,返回304 Not Modified

If-None-Match: "82e22293907ce725faf67773957acd12"

Last-Modified首部字段也可以用于缓存验证,它包含在源服务器发送的响应报文中,指示源服务器对资源的最后修改时间。但是它是一种弱校验器,因为只能精确到1S,所以它通常作为ETag的备用方案。如果响应首部字段里含有这个信息,客户端可以在后续的请求上带上If-Modified-Since来验证缓存。服务器只在所请求的资源在给定的日期时间之后对内容进行过修改的情况下才会将该资源返回,状态码为200。如果请求的资源从那时起未经修改,那么返回一个不带有实体主体的304 Not Modified响应报文

Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT

If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT

5.4 内容协商

通过内容协商返回最适合的内容,例如根据浏览器的默认语言选择返回中文页面还是英文页面

5.4.1 类型

5.4.1.1 服务端驱动型

客户端设置特定的HTTP首部字段,例如Accept、Accept-Charset、Accept-Encoding和Accept-Language,服务器根据这些字段返回特定的资源

它存在以下问题:

  • 服务器很难知道客户端浏览器的全部信息;
  • 客户端提供的信息相当冗长(HTTP/2协议的首部压缩机制缓解了这个问题),并且存在隐私风险(HTTP指纹识别技术);
  • 给定的资源需要返回不同的展现形式,共享缓存的效率会降低,而服务端的实现会越来越复杂

5.4.1.2 代理驱动型

服务器返回300 Multiple Choices 或者 406 Not Acceptable,客户端从中选出最合适的那个资源

5.4.2 Vary

Vary: Accept-Language

在使用内容协商的情况下,只有当缓存服务器中的缓存满足内容协商条件时,才能使用该缓存,否则应该向源服务器请求该资源

  • 例如: 一个客户端发送了一个包含Accept-Language首部字段的请求之后,源服务器返回的响应包含Vary: Accept-Language内容,缓存服务器对这个响应进行缓存之后,在客户端下一次访问同一个URL资源,并且Accept-Language与缓存中的对应的值相同才能返回该缓存

5.5 内容编码

内容编码将实体主体进行压缩,从而减少传输量

常用的内容编码有: gzip、compress、deflate、identity

浏览器发送Accept-Encoding首部,其中包含它所支持的压缩算法, 以及各自的优先级。服务器则从中选择一种,使用该算法对想用的消息主体进行压缩,并且发送Content-Encoding首部来告知浏览器它选择了哪一种算法。由于改内容协商过程是基于编码类型来选择资源的展现形式的,响应报文的Vary首部字段至少要包含Content-Encoding

5.6 范围请求

如果网络出现中断,服务器只发送了一部分数据,范围请求可以使得客户端只请求服务器未发送的那部分数据,从而避免服务器重新发送所有数据

5.6.1 Range

在请求报文中添加Range首部字段指定请求的范围

GET /z4d4kWk.jpg HTTP/1.1
Host: i.imgur.com
Range: bytes=0-1023

请求成功的话服务器返回相应包含206 Partial Content 状态码

HTTP/1.1 206 Partial Content
Content-Range: bytes 0-1023/146515
Content-Length: 1024
...
(binary content)

5.6.2 Accept-Ranges

响应首部字段Accept-Ranges 用于告知客户端是否能够处理范围请求,可以处理使用bytes,否则使用none

Accept-Ranges: bytes

5.6.3 响应状态码

  • 在请求成功的情况下,服务器会返回206 Partial Content状态码

  • 在请求的范围越界的情况下,服务器会返回416 Requested Range Not Satisfiable 状态码

  • 在不支持范围请求的情况下,服务器返回200 OK 状态码

5.7 分块传输编码

Chunked Transfer Encoding,可以把数据分割成多块,让浏览器逐步显示页面

5.8 多部分对象集合

一份报告主体内可含有多种类型的实体同时发送,**每个部分之间用boundary字段定义的分隔符进行分隔,每个部分都可以有首部字段 **

  • 例如: 上传多个表单时可以使用如下方式
Content-Type: multipart/form-data; boundary=AaB03x

--AaB03x
Content-Disposition: form-data; name="submit-name"

Larry
--AaB03x
Content-Disposition: form-data; name="files"; filename="file1.txt"
Content-Type: text/plain

... contents of file1.txt ...
--AaB03x--

5.9 虚拟主机

HTTP/1.1 使用虚拟主机技术,使得一台服务器拥有多个域名,并且逻辑上可以看成多个服务器

5.10 通信数据转发

5.10.1 代理

代理服务器接受客户端的请求,并且转发给其他服务器

使用代理的主要目的是:

  • 缓存;
  • 负载均衡;
  • 网络访问控制;
  • 访问日志记录

代理服务器分为正向代理和反向代理两种:

  • 用户察觉得到正向代理存在

image.png

  • 而反向代理一般位于内部网络中,用户察觉不到

5.10.2 网关

与代理服务器不同的是,网关服务器会将HTTP转化为其他协议进行通信,从而请求其他非HTTP服务器的服务

5.10.3 隧道

使用SSL等加密手段,在客户端和服务器之间建立一条安全的通信线路