HTTP学习笔记

1. HTTP发展史

HTTP/0.9 - 单行协议

http 0.9版本很简单,因为请求指令只由单行构成,所以被称之为单行协议,请求命令由:GET+url构成,例如:

GET /index.html

没有请求头等描述信息,服务器接收或者发送完数据后立马关闭TCP连接。

HTTP/1.0 - 构建可扩展性

增加了其他的请求方式,状态码,请求头等信息,增加了多字符集支持,多部分内容发送(发送多个文件时会对内容进行拆分),权限验证,缓存控制等功能。另外,在请求头的帮助下,http能够传输除了纯文本之外其他类型文档的能力。这时,一个典型的请求如下:

// request
GET /index.html HTTP/1.0
User-Agent: NCSA_Mosaic/2.0 (Windows 3.1)

//response
200 OK
Date: Tue, 15 Nov 1994 08:12:31 GMT
Server: CERN/3.0 libwww/2.17
Content-Type: text/html

HTTP/1.1 - 标准化的协议

1997年初,http 1.1标准发布,该标准消除了大量的歧义,并且引入了多项改进:

  • 增加了connection,使得tcp连接持久化,节省了频繁建立tcp连接的开销
  • 增加了pipelining技术,允许在同一tcp连接里面按发送多个http请求,并且按照请求顺序返回内容
  • 增加了缓存机制
  • 增加了内容协商机制
  • 增加了host头,使得同一服务器上可以部署不同的服务
    下面是一个http 1.1的典型请求:
GET /en-US/docs/Glossary/Simple_header HTTP/1.1
Host: developer.mozilla.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0
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, br
Referer: https://developer.mozilla.org/en-US/docs/Glossary/Simple_header

200 OK
Connection: Keep-Alive
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Date: Wed, 20 Jul 2016 10:55:30 GMT
Etag: "547fa7e369ef56031dd3bff2ace9fc0832eb251a"
Keep-Alive: timeout=5, max=1000
Last-Modified: Tue, 19 Jul 2016 00:59:33 GMT
Server: Apache
Transfer-Encoding: chunked
Vary: Cookie, Accept-Encoding

HTTP/2 - 为了更优异的表现

http 2主要解决了一些安全性问题和优化了管道复用技术,与之前的版本相比较:

  • http 2是二进制协议而非文本协议,所有的报文内容被封装成帧,不再可读,也不能无障碍的手工创建
  • 同一tcp连接可以并行发送和接受http请求,移除了顺序和阻塞的约束
  • 压缩了headers,因为headers在一系列的请求中往往是相似的
  • 允许服务端主动推送数据到客户端

TCP三次握手和四次挥手

因为http协议是应用层协议,是建立在tcp协议之上的,所以我们有必要了解下tcp连接的创建过程。

三次握手

假定 A为客户端,也就是请求的发起者,B为服务端,一次tcp建立的过程应该是:

// seq, ack(acknowledge,确认码) 均存在TCP报文的首部中,占4个字节,
// seq(sequence number,随机码) 是请求时随机生成的,
// syn = 1的意思是 syn(synchronous,建立码)这个标志放在了第一位

A (syn=1, seq=x)  ==> B
A  <== B (ack=x+1, syn=1, seq=y)
A (ack=y+1, seq=z)  ==> B

四次挥手

因为tcp连接是全双工的,所以断开连接时得双方都确认关闭,具体流程如下:

// fin(finish,结束码)

A (fin=m) ==> B
A <== B (ack=m+1)
A <== B (fin=n)
A (ack=n+1) ==> B

2. CORS和CORS预请求

跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求
出于安全考虑,浏览器会限制从脚本内发起的跨域 http请求,例如xhrfetch,这意味着一个web应用程序只能加载来自同一个域的资源。

解决跨域的办法

解决跨域有很多种办法,例如jsonP,或者代理,但是这些和http协议并不相关,这里主要介绍如何配置headers来实现跨域。**跨域资源共享标准新增了一组http首部字段,可以声明允许跨域的源和方法等。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。

CORS预请求

简单请求不会触发预请求,对于简单请求,这里是这样定义的:

  • 请求方法仅限于get,post,head
  • 请求头内不应该除了以下字段(Accept, Accept-Language, Content-Language, Content-type, DPR, DownLink, Viewport-Width, Width)之外的头信息
  • Content-type仅限于text/plainmultipart/form-dataapplication/x-www-form-urlencoded
    预请求得到正确的响应后,浏览器才会发送真正的跨域请求,响应结果示例:
    预请求响应

    其中Access-Control-*类的头信息可以在服务端设置,Access-Control-Allow-Headers表示服务器可接受的头信息中的字段,Access-Control-Allow-Methods表示可接受的请求的方法,Access-Control-Allow-Origin表示可以接受访问的域名(这里不建议写*,会埋下隐患),Access-Control-Max-Age表示该预请求的缓存有效时间,可以避免重复发送预请求。这里需要补充几点:
  • 每个浏览器可支持的最大的Access-Control-Max-Age可能都不同,FireFox支持最长24h,而Chrome只支持最长10min
  • 每个预请求的缓存的内容只对该请求的url有效,如果发送其它请求,尽管上个预请求没过期,这里仍然会发送预请求

3. Cache-Control和验证头

重用已获取的资源能够有效的提升网站与应用的性能。Web 缓存能够减少延迟与网络阻塞,进而减少显示某个资源所用的时间。借助 HTTP 缓存,Web 站点变得更具有响应性。

Cache-Control头

这个头是服务器用来声明该资源的可缓存性的,它的值有以下几种情况:

  • public公有缓存 表示任何请求节点,包括发起请求的客户端,代理服务器等都可以缓存该资源
  • private私有缓存 表示该资源是私有的,只有发起请求的客户端才能缓存该资源
  • no-cache强制确认缓存表示在此种方式下,每次发出该请求,浏览器都会带有该缓存的验证头想服务器确认是否可以使用该缓存,如果服务器返回304,则表示可以使用该缓存副本。
  • no-store禁止缓存 任何请求的节点不得缓存该资源。
    但其实,这些头都是一些声明性的头,不具有强制约束力,主流浏览器都会遵从该协议,但是一些中间代理可就不一定了~

缓存的生命周期

  • Max-Age = 1000,设置有效时间1000秒
  • S-MaxAge = 1000,这是专门给代理节点设置的,如果同时存在该值和上述值,则该值对代理生效,而上述值对客户端生效
  • Max-Stale = 1000,请求头内携带该内容,表示尽管服务端设置的缓存有效期到了,客户端依旧会使用该缓存
    举个栗子:
// node语法
response.writeHeader(200, {
  'Cache-Control': 'Max-Age=86400, public' // 设置有效期24小时,公共缓存
})

验证头信息

验证头一般出现在缓存方式为Cache-control: must-revalidate的情况下,因为该类资源可能会频繁改变,所以为了确保客户端的资源是具有时效性的,服务器会要求客户端在使用该缓存副本的时候向浏览器确认该缓存是否可用。

  • Last-Modified 直译就是上次修改的时间,这个是在response header里面的,针对该缓存,浏览器再再次请求该内容时会携带头信息If-Modified-Since或者If-Unmodified-Since,来想服务器确认该资源是否修改了。
  • ETag 即一个类似于数字签名的标志,服务器在响应该请求时会返回资源的ETag,下次请求时会在请求头中加入If-Match或者If-None-Match
    在上述请求得到200 OK或者304 NotModified的返回时表示浏览器可以使用本地缓存,同时304的响应头还可以更新cache的有效时间。

4. Cookie和Session

HTTP Cookie(也叫Web Cookie或浏览器Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie使基于无状态的HTTP协议记录稳定的状态信息成为了可能。
我们一般在服务端设置Cookie,例如:

res.writeHeader(200,{
'Set-Cookie': ['token=123; max-age=10', 'name=bing; HttpOnly', 'id=123; domain=test.com'] // 多个cookie元素使用数组来表示(node js)
})

Node.js中我们一般通过数组来一次性设置多个Cookiemax-age=10表示该Cookie的有效时间为10s,如果不设置过期时间,浏览器会默认该Cookie的过期时间就是浏览器关闭的时间;HttpOnly表示该Cookie仅能被Http请求读取与使用,无法通过JavaScript读取,提供了一定的安全性保证;domain=test.com表示该Cookie所属的域是test.com,我们不能跨域访问Cookie;另外,如果我们想要达到这样的效果:同一个一级域名下的二级域名都可以访问到主域名的Cookie。那我们只需要在访问主域名的时候设置了Cookie,在访问二级域名的时候就可以获取Cookie了。另外补充一点就是POST请求是不会携带Cookie的。
Session的意思就是会话,我们可以通过Cookie来记录当前登录状态,当我们意外关闭网站后,下次打开该网站,仍然能够回到上次登录的地方,就好像没有离开过一样,这就是一种Session的实现方式。

5. Http长链接以及数据协商

持久连接

曾经见到过一个很有意思的面试题:当一个页面需要同时加载20张图片时,浏览器是如何处理的?看了这部分内容相信你会有答案的。我们前面讲过,从http/1.1开始支持持久连接,即一个tcp连接在完成一次http请求后不会立即被服务器关闭,后面的http请求可以复用该连接,这里涉及到一个请求头中间的关键字Connection,它只有两个值:

  • keep-alive在一定时间内不会关闭连接
  • close在一次请求后立马关闭连接
    所以,如果浏览器需要同时请求20张图片,那么此时会并发尽可能多的http请求,而http请求是建立在tcp连接之上的,所以我们会尽可能多建立tcp连接,但这样对服务器很不友好,所以浏览器规定对某一个域产生的tcp连接不得超过一定数量,Chrome规定的是6个连接,也就是说我们会同时和服务器建立6个tcp连接(如果之前不存在tcp连接),然后同时发送6个http请求,然后浏览器会等待这6个请求的返回,当某一个请求返回之后也就是当前tcp连接空闲了之后才会发起新的http请求,这样说可能会和http/1.1管线复用技术相悖,这是因为一些阻塞和时序性问题,代理服务器很难处理类似请求,所以一般情况下浏览器默认关闭该功能!

数据协商

顾名思义,数据协商就是客户端和服务端交互数据时会定一些规则,来确保交互的信息都满足双方的要求,该功能主要通过Content-Type实现:

请求头

  • Accept,告诉服务端我需要什么样的数据
  • Accept-Encoding,支持的压缩方式
  • Accept-Language,支持的语言
  • User-Agent,可以通过该字段来知道客户端的设备,操作系统,浏览器内核,渲染引擎等

响应头(与请求头对应的)

  • Content-Type,返回内容的数据格式,比如 text/plain, text/html, text/javascript, application/json (主类型 / 分类型)
  • Content-Encoding,编码方式
  • Content-Language, 语言例如 zh-CN
  • x-Content-Type-Options: noSniff, 这个是告诉浏览器不要去猜测数据格式,按照返回的格式来解析内容,主要是为了安全性。
    这里有一个压缩方式的例子:
const zlib = require('zlib')
const html = fs.readFileSync('test.html')
res.writeHeader(200, {
'Content-Type': 'text/html',
'Content-Encoding': 'gzip'
)
res.end(zlib.gzipsync(html))  // 把这个html压缩后发送, 可以节省传输时间,提高性能

6. 重定向

当我们在网络上的资源更换位置后,原有的请求应该被重定向到新的地址,比如:

res.writeHead(302, { // 注意这个status code 一定要写对,302 指的是我们临时跳转到这个url,301表示永久跳转到搞路由
'Location': '/new' ,// 因为是同域跳转,这里写新的路由就可以了
})

这里需要注意的就是要合理使用状态码,如果状态码为301,那么这个时候浏览器会把新的地址缓存起来,接下来每次访问该地址都会被重定向到被缓存的地址,除非用户手动清除缓存;如果状态码为302,表示临时重定向,下次浏览器请求该资源时还是会先请求重定向之前的旧地址。

参考资料

HTTP协议原理+实践Web开发工程师必学
HTTP | MDN

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • Web 页面的实现 Web 基于 HTTP 协议通信 客户端(Client)的 Web 浏览器从 Web 服务器端...
    毛圈阅读 1,072评论 0 2
  • 原文github地址 HTTP遵循经典的client-server 模型,HTTP是无状态协议在请求与响应之间,还...
    fish_yijinc阅读 210评论 0 0
  • 网络基础知识 URL和URI URI(Uniform Resource Idenifier)统一资源标识符。即由某...
    d9fc24a0c9a9阅读 1,115评论 0 6
  • http协议有http0.9,http1.0,http1.1和http2三个版本,但是现在浏览器使用的是htt...
    一现_阅读 1,855评论 0 3
  • 人生若只如初见 阳光 灿烂 进去心里就像春柳 顺畅 昂然 可是四季还未走完 就不知怎么长出了小刺 放着伤心 抽出割...
    云不知陌阅读 176评论 0 0