网络协议HTTP

125 阅读19分钟

HTTP

HTTP 是应用层的一种可扩展的协议,通过TCP或者是TLS加密的 TCP 连接来发送,理论上任何可靠的传输协议都可以使用。不仅可传输超文本文档,还可传输图片、视频或者向服务器发送如 HTML 表单这样的信息。

HTTP 是一个 client-server 协议:请求request通过一个实体被发出,实体即用户代理(常指浏览器)每一个发送到服务器的请求,都会被服务器处理并返回一个消息,即response。在这个请求与响应之间,还有许多被称为 proxies 的实体,他们的作用与表现各不相同,比如有些是网关,还有些是caches等。

image.png

代理(Proxies)

在浏览器和服务器之间,有许多计算机和其他设备转发了 HTTP 消息。由于 Web 栈层次结构的原因,它们大多都出现在传输层、网络层和物理层上,对于 HTTP 应用层而言就是透明的,虽然它们可能会对应用层性能有重要影响。还有一部分是表现在应用层上的,被称为代理(Proxies) 。代理(Proxies)既可以表现得透明,又可以不透明(“改变请求”会通过它们)。代理主要有如下几种作用:

  • 缓存(可以是公开的也可以是私有的,像浏览器的缓存)
  • 过滤(像反病毒扫描,家长控制...)
  • 负载均衡(让多个服务器服务不同的请求)
  • 认证(对不同资源进行权限管理)
  • 日志记录(允许存储历史信息)

HTTP特点:

  • HTTP是可扩展的,HTTP/1.0开始支持HTTP header被扩展,只要客户端和服务端就新的HTTP头达成语义一致即可
  • HTTP是有会话,无状态的,同一个连接中,两个请求之间是没有关联的,故HTTP加入了cookies解决了这个问题,使得本质上无状态的HTTP,通过cookies可以创建有状态的会话。
  • HTTP是无连接的,每次连接只处理一个请求,当然HTTP/1.1为了提高HTTP的请求效率,加入了持久连接的,可通过头部的Connection属性控制TCP的连接状态。

HTTP报文: 用于http协议交互的信息叫做http报文。报文本身是由多行数据构成的字符串文字。http报文通常由报文首部和报文主体两部分组成,两者之间由最初出现的空行划分

image.png

HTTP有两种报文:请求报文和响应报文

HTTP 请求和响应具有相似的结构,由以下部分组成︰

  1. 一行起始行用于描述要执行的请求,或者是对应的状态,成功或失败。这个起始行总是单行的。
  2. 一个可选的 HTTP 头集合指明请求或描述消息正文。
  3. 一个空行指示所有关于请求的元数据已经发送完毕。
  4. 一个可选的包含请求相关数据的正文body (比如 HTML 表单内容), 或者响应相关的文档。 正文的大小有起始行的 HTTP 头来指定。

起始行和 HTTP 消息中的 HTTP 头统称为请求头,而其有效负载被称为消息正文。

image.png (1)报文首部:客户端或服务器端发送的请求或响应报文的内容及属性。

(2)空行(回车+换行):用来区分报文首部和报文主体。

(3)报文主体:应该要送的数据

HTTP请求

(1)请求行: 包括了客户端的请求方法,URI、HTTP版本。

(2)请求首部字段: 包括了请求的附加信息如客户端的信息,响应的优先级。

(3)通用首部字段:请求报文和响应报文都会使用的报文。

(4)实体首部字段:补充了与实体有关的资源信息,如实体更新的时间。

(5)报文主体:一般来说请求报文在使用GET/HEAD/DELETE/OPTIONS方法的时候没有报文主体,在使用post方法的时候才有

image.png

图1:请求头

HTTP响应

(1)响应报文状态行:包括HTTP版本、状态码。

(2)响应报文首部字段:服务器端向客户端发送响应报文使用的首部包括相应的附加内容,要求客户端补充的消息等。

(3)通用首部字段:请求报文和响应报文都会使用的报文。

(4)实体首部字段:补充了与实体有关的资源信息,如实体更新的时间。

(5)报文主体:对于响应报文来说报文体一般是传给客户端的数据。不是所有的响应都有 body:具有状态码 (如 201或 204) 的响应,通常不会有 body。 image.png

图2:响应头

HTTP方法

http的请求方法有get,post,head,options,put,delete,connect,trace,patch

GET方法

请求指定的资源。使用 GET 的请求应该只用于获取数据,请求报文没有主体,但成功的响应报文有主体。

POST方法

发送数据给服务器。请求主体的类型由 Content-Type 首部指定 一个 POST 请求通常是通过 HTML 表单发送,并返回服务器的修改结果。在这种情况下,content type 是通过在  <form>元素中设置正确的 enctype 属性,或是在 <input>和 <button> 元素中设置 formenctype 属性来选择的:

  • application/ x-www-form-urlencoded: 数据被编码成以 '&' 分隔的键 - 值对,同时以 '=' 分隔键和值。非字母或数字的字符会被 percent-encoding: 这也就是为什么这种类型不支持二进制数据 (二进制数据应使用 multipart/form-data 代替)。
  • multipart/form-data
  • text/plain

当 POST 请求是通过除 HTML 表单之外的方式发送时,例如使用 XMLHttpRequest, 那么请求主体可以是任何类型。

使用默认的 application/x-www-form-urlencoded 作为 content type 的简单表单:

POST / HTTP/1.1
Host: foo.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 13

say=Hi&to=Mom

使用 multipart/form-data 作为 content type 的表单:

POST /test.html HTTP/1.1
Host: example.org
Content-Type: multipart/form-data;boundary="boundary"

--boundary
Content-Disposition: form-data; name="field1"

value1
--boundary
Content-Disposition: form-data; name="field2"; filename="example.txt"

value2

PUT 和POST方法的区别是,PUT 方法是幂等的:连续调用一次或者多次的效果相同(无副作用)。连续调用同一个 POST 可能会带来额外的影响,比如多次提交订单

HEAD方法 

请求资源的头部信息,并且这些头部与 HTTP GET 方法请求时返回的一致。该请求方法的一个使用场景是在下载一个大文件前先获取其大小再决定是否要下载,以此可以节约带宽资源。HEAD 方法的响应不应包含响应正文。即使包含了正文也必须忽略掉. 虽然描述正文信息的 entity headers 可能会包含在响应中,但它们并不是用来描述 HEAD 响应本身的,而是用来描述同样情况下的 GET 请求应该返回的响应。如果 HEAD 请求的结果显示在上一次 GET 请求后缓存的资源已经过期了,即使没有发出GET请求,缓存也会失效。

PUT方法

使用请求中的负载创建或者替换目标资源,如果目标资源不存在,并且 PUT 方法成功创建了一份,那么源头服务器必须返回201(Created) 来通知客户端资源已创建。如果目标资源已经存在,并且依照请求中封装的表现形式成功进行了更新,那么,源头服务器必须返回200 (OK) 或者204 (No Content) 来表示请求的成功完成。但使用该请求方法,其响应报文是没有响应主体的,而且也不安全。

DELETE方法

用于删除指定的资源。如果 DELETE 方法成功执行,那么可能会有以下几种状态码:

  • 状态码 202 (Accepted) 表示请求的操作可能会成功执行,但是尚未开始执行。
  • 状态码 204(No Content) 表示操作已执行,但是无进一步的相关信息。
  • 状态码 200(OK) 表示操作已执行,并且响应中提供了相关状态的描述信息。 该方法同样不安全。

OPTIONS方法

用于获取目的资源所支持的通信选项。

客户端可以对特定的 URL 使用 OPTIONS 方法,也可以对整站(通过将 URL 设置为“*”)使用该方法

可以使用 OPTIONS 方法对服务器发起请求,以检测服务器支持哪些 HTTP 方法。

curl -X OPTIONS http://example.org -i

响应报文包含一个 Allow 首部字段,该字段的值表明了服务器支持的所有 HTTP 方法

HTTP/1.1 200 OK
Allow: OPTIONS, GET, HEAD, POST
Cache-Control: max-age=604800
Date: Thu, 13 Oct 2016 11:45:00 GMT
Expires: Thu, 20 Oct 2016 11:45:00 GMT
Server: EOS (lax004/2813)
x-ec-custom-error: 1
Content-Length: 0

在 CORS 中,可以使用 OPTIONS 方法发起一个预检请求,以检测实际请求是否可以被服务器所接受。预检请求报文中的 Access-Control-Request-Method 首部字段告知服务器实际请求所使用的 HTTP 方法;Access-Control-Request-Headers 首部字段告知服务器实际请求所携带的自定义首部字段。服务器基于从预检请求获得的信息来判断,是否接受接下来的实际请求。

OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type

服务器所返回的 Access-Control-Allow-Methods首部字段将所有允许的请求方法告知客户端。该首部字段与 Allow 类似,但只能用于涉及到 CORS 的场景中

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

当只包含简单头部时,一个请求则被视为简单请求并且在CORS中不需要发送preflight request

以下的 HTTP headers都可以被认为是简单头部:

CONNECT方法

可以开启一个客户端与所请求资源之间的双向沟通的通道。它可以用来创建隧道(tunnel)。 CONNECT 可以用来访问采用了 SSL (HTTPS) 协议的站点。客户端要求代理服务器将 TCP 连接作为通往目的主机隧道。之后该服务器会代替客户端与目的主机建立连接。连接建立好之后,代理服务器会面向客户端发送或接收 TCP 消息流

HTTP Headers

HTTP 消息头允许客户端和服务器通过 request和 response传递附加信息。一个请求头由名称(不区分大小写)后跟一个冒号 (:),冒号后跟具体的值(不带换行符)组成。该值前面的引导空白会被忽略。 根据不同上下文,可将消息头分为:

  • General headers: 同时适用于请求和响应消息,但与最终消息主体中传输的数据无关的消息头。取决于应用的上下文环境,通用首部可以是响应头部或者请求头部。但是不可以是实体头部。最常见的通用首部包括:DateCache-Control或 Connection
  • Request headers: 包含更多有关要获取的资源或客户端本身信息的消息头。某些请求头如 AcceptAccept-*、 If-* 允许执行条件请求。某些请求头如:CookieUser-Agent和 Referer 描述了请求本身以确保服务端能返回正确的响应。并非所有出现在请求中的 HTTP 首部都属于请求头,例如在 POST 请求中经常出现的 Content-Length实际上是一个代表请求主体大小的 entity header
  • Response headers]: 包含有关响应的补充信息,如其位置或服务器本身(名称和版本等)的消息头。并非所有出现在响应中的 http header 都属于响应头,例如Content-Length就是一个代表响应体消息大小的entity header。GET请求的响应头,可能出现Content-EncodingContent-Type都是属于entity headers
  • Entity headers: 实体报头是描述了一个 HTTP 消息有效载荷(即关于消息主体的元数据)的 HTTP 报头,实体报头包括 Content-LengthContent-LanguageContent-EncodingContent-Type和 Expires等。实体报头可能同时存在于 HTTP 请求和响应信息中

所有 HTTP 报头列表

HTTP响应码

HTTP 响应状态码用来表明特定 HTTP 请求是否成功完成。 响应被归为以下五大类:

  • 信息响应(100199)
  • 成功响应(200299)
  • 重定向消息(300399)
  • 客户端错误响应(400499)
  • 服务端错误响应(500599)

200 OK 表明请求成功,默认情况下状态码为 200 的响应可以被缓存。 不同请求方式对于请求成功的意义如下:

  • GET: 已经取得资源,并将资源添加到响应的消息体中。
  • HEAD: 响应的消息体为头部信息。
  • POST: 响应的消息体中包含此次请求的结果。
  • TRACE: 响应的消息体中包含服务器接收到的请求信息。

PUTDELETE的请求成功通常并不是响应200 OK的状态码而是 204 No Content 表示无内容(或者 201 Created表示一个资源首次被创建成功)

204 No Content 成功状态响应码,表示该请求已经成功了,但是没有响应内容,页面不需要刷新。。默认情况下 204 响应是可缓存的。一个 ETag 标头包含在此类响应中。用户代理可能会用此时请求头部信息来更新原来资源的头部缓存字段。

301 Moved Permanently 说明请求的资源已经被移动到了由 Location 头部指定的 url 上,是固定的不会再改变。搜索引擎会根据该响应修正。

302 Found 重定向状态码表明请求的资源被暂时的移动到了由该 HTTP 响应的响应头 Location 指定的 URL 上。浏览器会重定向到这个 URL,但是搜索引擎不会对该资源的链接进行更新

304 Not Modified 这是用于缓存的目的。它告诉客户端响应还没有被修改,因此客户端可以继续使用相同的缓存版本的响应。这通常是在一些安全的方法(safe),例如GET 或HEAD 或在请求中附带了头部信息: If-None-Match 或If-Modified-Since。如果是 200``OK ,响应会带有头部 Cache-ControlContent-LocationDateETagExpires,和 Vary.

400 Bad Request 由于被认为是客户端错误(例如,错误的请求语法、无效的请求消息帧或欺骗性的请求路由),服务器无法或不会处理请求。

401 Unauthorized 代表客户端错误,指的是由于缺乏目标资源要求的身份验证凭证,发送的请求未得到满足。客户端必须先对自身进行身份验证才能获得请求的响应。这个状态码会与 WWW-Authenticate 首部一起发送,其中包含有如何进行验证的信息

403 Forbidden 客户端没有访问内容的权限;也就是说,它是未经授权的,因此服务器拒绝提供请求的资源。与 401 Unauthorized 不同,服务器知道客户端的身份,即使重新验证也不会改变该状态。

404 Not Found 指的是服务器无法找到所请求的资源。返回该响应的链接通常称为坏链(broken link)或死链(dead link),它们会导向链接出错处理(link rot)页面

404 状态码并不能说明请求的资源是临时还是永久丢失。如果服务器知道该资源是永久丢失,那么应该返回 410(Gone)而不是 404

415 Unsupported Media Type 服务器不支持请求数据的媒体格式,因此服务器拒绝请求

500 Internal Server Error 服务器遇到了不知道如何处理的情况

501 Not Implemented 服务器错误响应码表示请求的方法不被服务器支持,因此无法被处理。服务器必须支持的方法(即不会返回这个状态码的方法)只有 GET 和 HEAD

请注意,你无法修复 501 错误,需要被访问的 web 服务器去修复该问题

502 Bad Gateway 是一种 HTTP 协议的服务端错误状态代码,它表示作为网关或代理的服务器,从上游服务器中接收到的响应是无效的

503 Service Unavailable 是一种 HTTP 协议的服务器端错误状态代码,它表示服务器尚未处于可以接受请求的状态。通常造成这种情况的原因是由于服务器停机维护或者已超载

缓存相关的首部在与该响应一同发送时应该小心使用,因为 503 状态码通常应用于临时状况下,而此类响应一般不应该进行缓存。

504 Gateway Timeout 是一种 HTTP 协议的服务器端错误状态代码,表示扮演网关或者代理的服务器无法在规定的时间内获得想要的响应。

更多响应码

扩展

post vs get

  • get请求能缓存,post请求不能。
  • 由于get请求参数暴露在url中,且会被浏览器保存在历史记录中,故不安全,而post的参数是放在请求体中,相对安全。
  • 受浏览器对url的长度限制,get的url长度是有限的。
  • get请求只能进行URL编码,只接收ASCII字符,而post没有限制,支持更多的编码类型,且不对数据类型做限制。
  • get的请求报文是一次性发送,而post则是分两个TCP发送,先发送header部分,待服务器响应100,再继续发送body部分。(当然有例外,firfox只发一个包)

基于HTTP的APIs 基于 HTTP 的最常用 API 是XMLHttpRequest API,可用于在user agent和服务器之间交换数据。 现代Fetch API提供相同的功能,具有更强大和灵活的功能集。

另一种 API,即服务器发送的事件,是一种单向服务,允许服务器使用 HTTP 作为传输机制向客户端发送事件。 使用EventSource接口,客户端打开连接并建立事件句柄。 客户端浏览器自动将到达 HTTP 流的消息转换为适当的Event对象,并将它们传递给专门处理这类type事件的句柄,如果有这么个句柄的话。但如果相应的事件处理句柄根本没有建立,那就交给onmessage (en-US)事件处理程序处理。

HTTP缓存

http缓存主要分为强缓存和协商缓存(验证响应)。 强缓存主要由cache-control:max-age=<seconds>,或者expires控制

过时的响应不会立即被丢弃。 HTTP 有一种机制,可以通过询问源服务器将陈旧的响应转换为新的响应。这称为验证,有时也称为重新验证(即协商缓存)。主要是通过使用包含if-Modified-Since/Last-Modified,或者If-None-Match/ETag的请求头/响应头的条件请求完成的。

Cache-Control

通用消息头字段,被用于在 http 请求和响应中,通过指定指令来实现缓存机制。

缓存指令是单向的,这意味着在请求中设置的指令,不一定被包含在响应中。

public表明响应可以被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存,即使是通常不可缓存的内容。(例如:1.该响应没有max-age指令或Expires消息头;2. 该响应对应的请求方法是POST。)

private表明响应只能被单个用户缓存,不能作为共享缓存(即代理服务器不能缓存它)。私有缓存可以缓存响应内容,比如:对应用户的本地浏览器。

no-cache在发布缓存副本之前,强制要求缓存把请求提交给原始服务器进行验证 (协商缓存验证)。

no-store不使用任何缓存。

max-age=<seconds>设置缓存存储的最大周期,超过这个时间缓存被认为过期 (单位秒)

s-maxage=<seconds>覆盖max-age或者Expires头,但是仅适用于共享缓存 (比如各个代理),私有缓存会忽略它。

must-revalidate一旦资源过期(比如已经超过max-age),在成功向原始服务器验证之前,缓存不能用该资源响应后续请求。

immutable表示响应正文不会随时间而改变。资源(如果未过期)在服务器上不发生改变,因此客户端不应发送重新验证请求头(例如If-None-Match或 If-Modified-Since)来检查更新,即使用户显式地刷新页面

指定 no-cache 或 max-age=0, must-revalidate 表示客户端可以缓存资源,每次使用缓存资源前都必须重新验证其有效性。这意味着每次都会发起 HTTP 请求,但当缓存内容仍有效时可以跳过 HTTP 响应体的下载

注意: 如果服务器关闭或失去连接,下面的指令可能会造成使用缓存。

Cache-Control: max-age=0

ETag 响应头的值是服务器生成的任意值。

参考连接: zhuanlan.zhihu.com/p/29907174/ segmentfault.com/a/119000002…