HTTP协议

HTTP协议

HTTP(HyperText Transfer Protocol)是一个基于请求-应答模式的、无状态的应用层协议,是万维网数据通信的基础。

尽管TCP/IP协议是互联网上最流行的网络协议,但是,在HTTP协议中,并没有规定必须使用它或它支持的层。事实上,HTTP可以在任何互联网协议或其他网络上实现,HTTP假定其下层协议提供可靠的传输。因此,任何能够提供这种保证的协议都可以被其使用。因此也就是其在TCP/IP协议族使用TCP作为其传输层。

HTTP并不需要其底层的传输层协议是面向连接的,只需要它是可靠的或不丢失消息(至少返回错误)的。在互联网中,有两个最常用的传输层协议:TCP是可靠的,而UDP不是。因此,HTTP依赖于面向连接的TCP进行消息传递,但连接并不是必须的(一个连接是由传输层来控制的,这从根本上不属于HTTP的范围)。

HTTP是无状态的,即在同一个连接中,两次执行成功的请求之间是没有关系的。这就带来了一个问题,用户没有办法在同一个网站中进行连续的交互。而使用HTTP的头部扩展,HTTP Cookies 就可以解决这个问题。把Cookies添加到头部中,创建一个会话让每次请求都能共享相同的上下文信息,从而保持客户端和服务器之间的会话。

HTTP报文格式

HTTP Message Format

HTTP请求方式

请求方式 幂等性 描述
GET 幂等 向服务器发出指定资源的请求。使用GET方法应该只用在读取数据,而不应当被用于产生“副作用”的操作中,原因是GET请求可能会被网络蜘蛛等随意访问
HEAD 幂等 与GET方法一样,都是向服务器发出指定资源的请求。只不过服务器将不传回资源的本文部分。它的好处在于,使用这个方法可以在不必传输全部内容的情况下,就可以获取其中“关于该资源的信息”(元信息或称元数据)
POST 非幂等 向指定资源提交数据,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求本文中,这个请求可能会创建新的资源或修改现有资源,或二者皆有
DELETE 幂等 请求服务器删除Request-URI所标识的资源
PUT 幂等 向指定资源位置上传其最新内容
PATCH 非幂等 用于将局部修改应用到资源(如果对应的资源不存在,服务器可以创建一个新资源)

HTTP状态码

状态码 描述
1xx(消息) 请求已被服务器接收,需要继续处理(除非在某些试验条件下,否则服务器禁止向此类客户端发送1xx响应)
2xx(成功) 请求已成功被服务器接收、理解、并接受
3xx(重定向) 需要客户端后续操作才能完成请求
4xx(客户端错误) 客户端原因导致服务器无法处理请求
5xx(服务器错误) 服务器原因导致处理请求出错

HTTP长连接

HTTP长连接(Keep-Alive)是使用同一个TCP连接来发送和接收多个HTTP请求/应答,而不是为每一个新的请求/应答打开新的连接的方法。

HTTP persistent connection

在 HTTP/1.0 中,默认使用的是短连接。也就是说,浏览器和服务器每进行一次HTTP操作就建立一次连接,任务结束就中断连接。如果客户端浏览器访问的某个HTML或其他类型的 Web网页中包含有其他的Web资源(如JS、CSS、Image等),当浏览器每遇到这样一个Web资源,就会建立一个HTTP会话。

在 HTTP/1.0 中,没有官方的 Keep-Alive 的操作。通常是在现有协议上添加一个指数,如果浏览器支持 Keep-Alive,它会在请求头中添加 Connection: Keep-Alive 字段,然后当服务器收到请求作出响应时,它会在响应头中添加 Connection: Keep-Alive 字段,这样做,连接就不会中断,而是保持连接。当客户端发送另一个请求时,它会使用同一个连接。这一直持续到客户端或服务器端认为会话已经结束,其中一方中断连接。

在 HTTP/1.1 中,官方规定的 Keep-Alive 使用标准和 HTTP/1.0 版本中有些不同,所有的连接默认都是长连接,除非在请求头或响应头中添加 Connection: close 字段指明要关闭,这也就是为什么 Connection: Keep-Alive 字段再没有意义的原因。然而,Apache 2.0 httpd 的默认连接过期时间是仅仅15秒,对于 Apache 2.2 只有5秒。短的过期时间的优点是能够快速的传输多个web页组件,而不会绑定多个服务器进程或线程太长时间。

HTTP缓存

缓存是一种保存资源副本并在下次请求时直接使用该副本的技术。当 Web 缓存发现请求的资源已经被存储,它会拦截请求,返回该资源的拷贝,而不会去源服务器重新下载。这样带来的好处有:缓解服务器端压力,提升性能(获取资源的耗时更短了)。对于网站来说,缓存是达到高性能的重要组成部分。缓存需要合理配置,因为并不是所有资源都是永久不变的,重要的是对一个资源的缓存应截止到其下一次发生改变(即不能缓存过期的资源)。
虽然 HTTP 缓存不是必须的,但重用缓存的资源通常是必要的。然而常见的 HTTP 缓存只能存储 GET 响应,对于其他类型的响应则无能为力(一般只有GET请求才会被缓存)。

在 HTTP/1.1 中定义的 Cache-Control 头用来区分对缓存机制的支持情况,请求头和响应头都支持这个属性。通过它提供的不同的值来定义缓存策略。

缓存控制

  1. 禁止进行缓存
    缓存中不得存储任何关于客户端请求和服务端响应的内容,每次由客户端发起的请求都会下载完整的响应内容。

    1
    2
    Cache-Control: no-store
    Cache-Control: no-cache, no-store, must-revalidate
  2. 强制确认缓存
    每次有请求发出时,缓存会将此请求发到服务器(该请求应该会带有与本地缓存相关的验证字段),服务器端会验证请求中所描述的缓存是否过期,若未过期(实际就是返回304),则缓存才使用本地缓存副本。

    1
    Cache-Control: no-cache
  3. 私有缓存和公共缓存
    public 指令表示该响应可以被任何中间人(比如中间代理、CDN等)缓存。若指定了 public,则一些通常不被中间人缓存的页面(因为默认是private),比如带有HTTP验证信息(帐号密码)的页面或某些特定影响状态码的页面,将会被其缓存。而 private 则表示该响应是专用于某单个用户的,中间人不能缓存此响应,该响应只能应用于浏览器私有缓存中。

    1
    2
    Cache-Control: private
    Cache-Control: public
  4. 缓存过期机制
    过期机制中,最重要的指令是 “max-age=“,表示资源能够被缓存(保持新鲜)的最大时间。相对Expires而言,max-age是距离请求发起的时间的秒数。针对应用中那些不会改变的文件,通常可以手动设置一定的时长以保证缓存有效,例如JS、CSS、图片等静态资源。

    1
    Cache-Control: max-age=31536000
  5. 缓存验证确认
    当使用了 “must-revalidate” 指令,那就意味着缓存在考虑使用一个陈旧的资源时,必须先验证它的状态,已过期的缓存将不被使用。

    1
    Cache-Control: must-revalidate
  6. Pragma 头
    Pragma 是 HTTP/1.0 标准中定义的一个header属性,请求中包含Pragma的效果跟在头信息中定义 Cache-Control: no-cache 相同,但是HTTP的响应头不支持这个属性,所以它不能拿来完全替代 HTTP/1.1 中定义的Cache-control头。通常定义Pragma以向后兼容基于 HTTP/1.0 的客户端。

新鲜度

理论上来讲,当一个资源被缓存存储后,该资源应该可以被永久存储在缓存中。由于缓存只有有限的空间用于存储资源副本,所以缓存会定期地将一些副本删除,这个过程叫做缓存驱逐。另一方面,当服务器上面的资源进行了更新,那么缓存中的对应资源也应该被更新,由于HTTP是C/S模式的协议,服务器更新一个资源时,不可能直接通知客户端及其缓存,所以双方必须为该资源约定一个过期时间,在该过期时间之前,该缓存副本就是新鲜的,当过了过期时间后,该缓存副本则变为陈旧的。驱逐算法用于将陈旧的缓存副本替换为新鲜的,注意,一个陈旧的缓存副本是不会直接被清除或忽略的,当客户端发起一个请求时,缓存检索到已有一个对应的陈旧缓存副本,则缓存会先将此请求附加一个 If-None-Match 头,然后发给目标服务器,以此来检查该资源副本是否是依然还是算新鲜的,若服务器返回了 304 Not Modified(该响应不会有带有实体信息),则表示此资源副本是新鲜的,这样一来,可以节省一些带宽。若服务器通过 If-None-Match 或 If-Modified-Since 判断后发现已过期,那么会带有该资源的实体内容返回。

HTTP Cache Staleness

对于含有特定头信息的请求,会去计算缓存寿命。比如 Cache-control: max-age=N 的头,相应的缓存的寿命就是N。通常情况下,对于不含这个属性的请求则会去查看是否包含Expires属性,通过比较Expires的值和头里面Date属性的值来判断是否缓存还有效。如果 max-age 和 expires 属性都没有,再找头里的 Last-Modified 信息。如果有,缓存的寿命就等于头里面Date的值减去Last-Modified的值除以10。

更多地利用缓存资源,可以提高网站的性能和相应速度。为了优化缓存,过期时间设置得尽量长是一种很好的策略,但一旦要更新就会很困难。特指网页上引入的一些JS、CSS文件,当它们变动时需要尽快更新线上资源。相信有人想到了办法:通过更新页面中引用的资源路径,让浏览器主动放弃缓存,加载新资源。我们可以在URL后面(通常是文件名后面)会加上版本号(摘要算法),加上版本号后的资源就被视作一个完全新的独立的资源了。但是这么做也存在一个弊端,所有引用这个资源的地方都需要更新链接。Web开发者们通常会采用自动化构建工具在实际工作中完成这些琐碎的工作。

缓存验证

用户点击刷新按钮时会开始缓存验证。如果缓存的响应头信息里含有 Cache-control: must-revalidate 的定义,在浏览的过程中也会触发缓存验证。另外,在浏览器偏好设置里设置 Advanced->Cache 为强制验证缓存也能达到相同的效果。

当缓存的文档过期后,需要进行缓存验证或者重新获取资源。只有在服务器返回强校验器或者弱校验器时才会进行验证。

ETag 响应头作为缓存的一种强校验器,是一个对用户代理(User Agent简称UA)不透明的值。对于像浏览器这样的HTTP UA,不知道ETag代表什么,不能预测它的值是多少。如果资源请求的响应头里含有ETag,客户端可以在后续的请求的头中带上 If-None-Match 头来验证缓存。

Last-Modified 响应头可以作为一种弱校验器。说它弱是因为它只能精确到一秒。如果响应头里含有这个信息,客户端可以在后续的请求中带上 If-Modified-Since 来验证缓存。

当向服务端发起缓存校验的请求时,服务端会返回 200 OK 表示返回正常的结果或者 304 Not Modified(不返回body)表示浏览器可以使用本地缓存文件。304的响应头也可以同时更新缓存文档的过期时间。

TLS安全协议

TLS(Transport Layer Security)及其前身SSL(Secure Sockets Layer)是一种安全协议,目的是为互联网通信提供安全及数据完整性保障。

TLS使用 X.509 认证,之后利用非对称加密演算来对通信方做身份认证,之后交换对称密钥作为会话密钥。这个会话密钥是用来将通信两方交换的数据做加密,保证两个应用间通信的保密性和可靠性,使客户与服务器应用之间的通信不被攻击者窃听。

TLS协议采用C/S架构模型,用于在两个应用程序间透过网络创建起安全的连线,防止在交换数据时受到窃听及篡改。

TLS协议的优势是与高层的应用层协议(如HTTP、FTP、Telnet等)无耦合。应用层协议能透明地运行在TLS协议之上,由TLS协议进行创建加密通道需要的协商和认证。应用层协议传送的数据在通过TLS协议时都会被加密,从而保证通信的私密性。

TLS协议是可选的,必须配置客户端和服务器才能使用。一旦客户端和服务器都同意使用TLS协议,他们通过使用一个握手过程协商出一个有状态的连接以传输数据。通过握手,客户端和服务器协商各种参数用于创建安全连接:

  1. 当客户端连接到支持TLS协议的服务器要求创建安全连接并列出了受支持的密码组合(加密密码算法和加密哈希函数),握手开始。
  2. 服务器从该列表中决定加密和散列函数,并通知客户端。
  3. 服务器发回其数字证书,此证书通常包含服务器的名称、受信任的证书颁发机构(CA)和服务器的公钥。
  4. 客户端确认其颁发的证书的有效性。
  5. 为了生成会话密钥用于安全连接,客户端使用服务器的公钥加密随机生成的密钥,并将其发送到服务器,只有服务器才能使用自己的私钥解密。
  6. 利用随机数,双方生成用于加密和解密的对称密钥。这就是TLS协议的握手,握手完毕后的连接是安全的,直到连接被关闭。如果上述任何一个步骤失败,TLS握手过程就会失败,并且断开所有的连接。

TLS Authentication

TLS利用密钥算法在互联网上提供端点身份认证与通讯保密,其基础是公钥基础设施(PKI)。不过在实现的典型例子中,只有网络服务者被可靠身份验证,而其客户端则不一定。这是因为公钥基础设施普遍商业运营,电子签名证书通常需要付费购买。协议的设计在某种程度上能够使C/S架构应用程序通讯本身预防窃听、干扰和消息伪造。

TLS包含三个基本阶段:

  1. 对等协商支持的密钥算法
  2. 基于非对称密钥的信息传输加密和身份认证、基于PKI证书的身份认证
  3. 基于对称密钥的数据传输保密

在第一阶段,客户端与服务器协商所用密码算法。当前广泛实现的算法选择如下:

  • 公钥私钥非对称密钥保密系统:RSA、Diffie-Hellman、DSA
  • 对称密钥保密系统:RC2、RC4、IDEA、DES、Triple DES、AES以及Camellia
  • 单向散列函数:MD5、SHA1以及SHA256

会话密钥

会话密钥(Session key)是一次性用于会话中加密用的对称式密钥,所有成员使用同一把密钥来加密明文、解密密文,在此次连线结束该密钥即无效,如需重新通信则需要再重新进行一次密钥的产生及交换等步骤。

在密码学中,只要消息一送离己方,就假设会遭到拦截。如果在交换密钥的过程中未经处理的会议密钥遭到拦截,则此加密形同虚设。所以在对称式加密中,如何交换密钥是一个重要的课题。

非对称加密虽然相较安全且不用考虑如何交换密钥,但需要较多的资源来进行运算,对于许多需求来说太慢了;所有私密密钥(secret key)算法都要求密钥是安全分发的,通过使用非对称加密算法来加密另一个更快速的对称式加密算法的秘密密钥,可以显著提高整体性能。

跟所有密钥一样,必须使用特殊的方法产生会话密钥,使其不能被攻击者预测,通常会使用随机的方式。在任何的加密系统中,没有正确选择会话密钥(或任何密钥)会是一个重大的设计缺陷。

HTTPS

HTTPS 协议可以理解为 HTTP+SSL/TLS,即 HTTP 下加入 TLS 层,HTTPS 的安全基础是 TLS,客户端和服务端的信息传输都会通过 TLS 进行加密,所以传输的数据都是加密后的数据。

HTTP的不安全性 HTTPS解决方案
窃听风险(攻击者可以获知消息内容) 消息加密
篡改风险(攻击者可以篡改消息内容) 消息摘要
冒充风险(攻击者可以冒充其他人参与通信) CA身份认证

Protocol HTTPS

RESTful

REST(Representational State Transfer)是一种万维网软件架构风格,目的是便于不同软件/程序在网络(如互联网)中互相传递信息。需要注意的是,REST是设计风格而不是标准。REST通常基于使用HTTP、URI、XML和HTML这些现有的广泛流行的协议和标准。

要理解RESTful架构,最好的方法就是去理解 Representational State Transfer 这个词组到底是什么意思,它的每一个词代表了什么涵义。

  • 资源(Resources):REST的名称 表现层状态转化 中,省略了主语,表现层其实指的是资源的表现层,而资源是由统一资源定位符(URI)来指定。
  • 表现层(Representation):资源的表现形式可以是JSON、XML或者HTML,当然也可以是任何其他的格式。
  • 状态转换(State Transfer):状态转换是建立在表现层之上的,客户端通过操作资源的表现形式来操作资源,对资源的操作包括获取、创建、修改和删除资源,这些操作正好对应HTTP协议提供的GET、POST、PUT和DELETE方法。
状态码 请求方式 响应内容 描述
200 GET/PUT 资源 操作成功
201 POST 资源 资源创建成功
202 POST/PUT/DELETE/PATCH N/A 请求已被接受
204 PUT/DELETE/PATCH N/A 操作执行成功但没有返回数据
301 GET Link 资源已被移除
303 GET Link 重定向
304 GET N/A 资源没有被修改
400 GET/POST/PUT/DELETE/PATCH 错误提示 请求参数有误
401 GET/POST/PUT/DELETE/PATCH 错误提示 未认证
403 GET/POST/PUT/DELETE/PATCH 错误提示 未授权
404 GET/POST/PUT/DELETE/PATCH 错误提示 资源不存在
405 GET/POST/PUT/DELETE/PATCH 错误提示 请求方法不允许
409 GET/POST/PUT/DELETE/PATCH 错误提示 资源冲突
415 GET/POST/PUT/DELETE/PATCH 错误提示 不支持的数据(媒体)类型
429 GET/POST/PUT/DELETE/PATCH 错误提示 请求过多被限制
500 GET/POST/PUT/DELETE/PATCH 错误提示 服务器内部错误

参考链接