HTTP协议零碎笔记

先引入几张基础图片

应用层:为应用软件提供了很多服务,构建于TCP、UDP等协议之上,屏蔽网络相关传输细节;

传输层:向用户提供可靠的端到端的END-TO-END服务,传输层向高层屏蔽了下层数据通信的细节;(tcp面向连接,可靠;udp面向无连接,不可靠)

网路层:为数据在节点之间传输创建逻辑链路;

数据链路层:在通信实体之间建立数据链路连接;

物理层:定义物理设备如何传输数据;

 

 

历史

http 0.9  是发布的第一版 ,请求方式只支持get方式,且不支持header等描述信息,请求发送完毕,就关闭TCP链接,即用即销,所以每次建立和销毁都需要经历三次握手和四次挥手的过程,大大增加了通讯开销;

http 1.0 增加了header status code ,可手动设置keep-alive(这个很晚的时候才支持,甚至可以认为是从1.1开始的),请求结束后不再关闭TCP连接,即将短连接转为长连接;

http 1.1 默认开启keep-alive,即默认建立长连接,且增加了pipeline策略(管线化)(在一个TCP连接中发送多个请求,但这些请求依然是有先后顺序的,串行,不是并发);

即是串行,在一个请求没处理完之前,其他请求都需要排队等待,这就是传说的http队头阻塞HOLB(Head of line blocking);

因为TCP连接不再是即用即销,为了控制资源占用,浏览器必须限制同时存在的TCP连接个数,如,chrome支持最多6个TCP连接,其他浏览器一般也为4-8个;

另外keep-alive可以设置timeout,来指定TCP连接的过期时间,默认情况下,默认15s内没有请求,TCP连接自动关闭;

http 2.0 所有数据都以二进制传输,请求头压缩,

信道复用,也叫多路复用,同一个域名下,仅建立一个TCP连接,所有请求都复用这一个,且可以并发请求;

分帧传输,同一条数据可以被拆为不同帧,且保持其上下文关系,并发发送,无论哪一部分先到,最终都会依据上下文关系,将其还原为源数据

server-push,服务端消息推送(如果当前https的证书是不被信任的,那么后台推送的数据浏览器会拒绝接收)

注意,http2只有在开启https后才能使用,因为http2是有谷歌之前开发的SPDY协议演化而来,SPDY协议要求必须使用https才能使用,导致现在http2也是如此。

 

名词解析

URI 统一资源标识符 包括URL+URN 用来唯一标识互联网上的资源信息

URL 统一资源定位器 即常见的访问地址 schema://host:port/path?query#hash

URN 永久统一资源定位符 为了实现在资源移动位置后还能被找到(这个目前还没有非常成熟的方案)

 

User-Agent

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36

Mozilla/5.0  因为浏览器最早是由网景公司出的,默认头为Mozilla/5.0,很多老的http服务器只支持这个头,所以现在很多浏览器为了兼容老的服务器,都默认给他加上这个头;

Windows NT 10.0; Win64; x64  本机环境,windows环境和系统位数;

AppleWebKit/537.36  浏览器内核,目前chrome和safari内核都是webkit,苹果公司研发的,所以都已AppWebKit声明;

KHTML, like Gecko  渲染引擎版本,gecko是火狐的渲染引擎;

Chrome/84.0.4147.135  当前chrome版本号;

Safari/537.36  因为用的是苹果公司的内核,所以强制加上这个声明,没啥原因

 

CORS预请求

浏览器对跨域的拦截,其实是请求已正常发送,并且response内容已正常响应,但在浏览器解析返回的数据时,发现不满足服务端设置的Access-Control-Allow-Origin,才抛出跨域错误信息;

可在服务端通过Access-Control-Allow-Origin设置白名单,甚至可以通过程序来动态设置;

可在服务端通过Access-Control-Allow-Methods属性设置允许的请求方法,如put\delete等,因为默认情况下跨域只允许get/head/post三种请求方式;

可在服务端通过Access-Control-Allow-Headers属性设置允许的自定义请求头,如X-Test-Cors等;

可在服务端通过Access-Control-Max-Age 属性设置预请求的过期时间。

跨域只支持三种数据类型text/plain、multipart/form-data、application/x-www-form-urlencoded 三种content-type

   multipart/form-data 为“拆分上送”或“部分上送”,就是把数据拆为各个部分上送,上送file类型的数据时,必须选此格式,因为file类型禁止字符串的形式上传,必须以二进制上送。

注意,header里面时间单位都是秒,一个key对应多个值时,以逗号分隔,如Access-Control-Allow-Methods:"put,delete"等

 

缓存

public  请求经过的所有节点如浏览器、代理服务器等都可缓存;

private  仅允许请求发起的终端使用缓存,如浏览器;

no-store  表示直接禁用缓存;

no-cache 表示不直接使用缓存,而是发起服务端验证,配合last-modified或etag使用;

max-age 请求终端(浏览器)缓存失效时间,单位是秒;

s-maxage  代理服务器缓存时间;

代理服务器的缓存即为代理缓存,可在多个终端共享;

服务器缓存验证:

          last-modified  服务端最后一次修改的时间,配合if-modified-since或者if-unmodified-since使用,后者很少用;

                                对比上次修改的时间,验证缓存是否需要更新;

          etag   数据签名,最后一次修改服务端资源是,经过特定算法得出的hash值,配合if-match或者if-non-match使用;

                                对比资源的签名判断是否一致,验证缓存是否需要更新;

另外,服务端response的header中可以设置vary字段,表示只有在请求头中携带的vary字段值相同时,才能使用缓存,如下

   response的header设置{vary:"user-agent"},那么所有的请求头中只有request中的header里有user-agent字段,且其value相同,才可以共用缓存,

   这个很常见,同一个应用程序用来区分移动端和PC端的缓存。

 

 

为什么页面中的meta中的cache-control不生效?(这个好多人都在问)

很多人发现,页面中的cache-control如何配置,对我们的缓存策略丝毫不产生作用,那么既然没作用为什么好多项目的页面中还要配置呢?

其实,造成这一问题的原因大致可为分两个维度,浏览器原因和http协议原因

从浏览器来讲,很简单,这个只是IE时代的私有属性,在IE9以前支持的,而现在主流的chrome\firefox\safari,包括IE9-11都不再支持了,如果应用需要兼容低版本的IE浏览器(如银行、zf等),可以加上这个东西,否则就完全没有必要了;

从http来讲,这东西是http1.0时代的产物,因为http1.0里关于缓存可设定的内容太少(要知道,http0.9压根就不支持服务端的response.header,http1.0虽是添加了header,但除了status code,也没多少可以设置的),而且http1.1发布早期,并不是所有浏览器都支持,所以把控制缓存的cache-control放到了前端html的页面中。

现在http1.1已经普及,几乎找不到还支持1.0的web服务器了,而且随着https的普及,很快就能全面实现http2了。

另外,https://stackoverflow.com/questions/49547/how-do-we-control-web-page-caching-across-all-browsers 看这篇的文章的意思,应该是说目前网页中的cache-control仅在file://协议才对此生效。(自己没有验证)

 

HTTP请求的数据协商

request

   Accept  接受的数据格式,如(*,type/text,application/json等);

   Accept-Encoding 接受的数据压缩格式,如(*,gzip,deflate,br等);

   Accept-Language 接受的语言类型,如(zh-CN,en,zh-TW等);

response

   Content-Type 返回的数据格式;

   Content-Encoding 返回的压缩格式;

   Content-Language 返回的语言类型;

 

重定向

302  重定向跳转,每次都经过服务器转发;

301 永久重定向,只有第一次请求有服务器转发,后面依赖终端缓存,直接转发,清除缓存后,会再次经过服务器。(这种很少用)

 

CSP(content-security-policy) 内容安全策略

这个值得讲的是,可以在前端meta中设置,也可以在后台的reponse.header中设置;

前后端同时设置,按最高安全的级别执行,取前后端规则的交集,如下:

后台设置 "Content-Security-Policy": 'default-src \'self\'; '      仅支持同域名下的连接

前端设置<meta http-equiv="Content-Security-Policy" Content="default-src https:" />  仅支持https的连接

最终执行策略:只允许即是同域名,又是https连接(这也就要求了当前域名必须是https,否则不会同域名)

 

https的握手过程

证书即公钥会用于传输,私钥永远只放在服务器,不进行传输,所以也不会被获取,所以更安全。
    1.客户端向服务端发送 一个随机数 和 支持的加密套件(算法),可以是一种或多种;
    2.服务端向客户端返回 一个随机数 和 选用的一种加密套件,同时,服务端向客户端发送 证书 即公钥;
    3.客户端使用证书(公钥)对一个新的随机数加密,生成预主密钥,然后将预主密钥发送至服务端;
    4.服务端利用私钥对预主密钥解密,得到客户端加密前的随机串,  至此前后端各有三个相同的随机串
    5.利用加密套件对三个随机串加密  生成最终的主密钥。

 

nginx配置https

1.先生成证书和密钥 (在nginx根目录下执行git bash)

        openssl req -x509 -newkey rsa:2048 -nodes -sha256 -keyout localhost-privkey.pem -out localhost-cert.pem
               此命令在windows下默认不支持,可在git bash中执行,git bash已集成openssl,一路回车即可;

2.执行后在nginx根目录下生成两个文件localhost-privkey.pem  localhost-cert.pem;

3.配置nginx.conf

listen  443;//这个是默认的https端口
        
ssl on;   //nginx1.5以下需要这么开启https  1.5+不需要这句

ssl_certificate_key ../localhost-privkey.pem;

ssl_certificate ../localhost-cert.pem;

4.如何实现浏览器输入域名或localhost时自动跳转https

server{
   listen 80 default_server
   listen [::]:80 default_server
   server_name localhost
   return 302 https://$server_name$request_uri
}

注意:nginx 1.5以上  开启https 和http2可以在端口号后面直接添加 ssl  http2,如下:

listen  443 ssl http2;

 

另外nginx 开启http2后,采用 ALPN 的兼容策略与服务端协商,服务端支持H2就用http2 ,不支持就用1.1。 ALPN 兼容策略优先使用http2

这点很重要,现在http2之所以没有流行,

一是受https的限制,

二是多数公司现有项目或库,都是不支持http2的,要推行http2的成本太高。

那么有了nginx这种代理服务器后,是不是可以解决第二个问题,即,

在原服务器的前面设置一层代理服务器,并开启http2,如此以来,浏览器和nginx之间建立http2连接进行通讯,什么分帧传输、信道复用、头部压缩等h2的好处都能使用;请求到达nginx后,nginx执行ALPN策略,判断后台是否支持h2,支持最好,直接原样发送,若不支持,再转为http1.1与后台服务器通讯。看到此处,可能有人会问,这样一来,节省掉的tcp建立连接(http1.1中浏览器与后台服务器)的开销不一样又补回来了吗,并且请求无故多了一次中转,我们大费周章岂不止做了无用功,还延迟了请求的时间线?我不这么认为,因为代理服务器和后台服务在同一局域网内,建立连接的开销比通过外网时的要小太多了,而且他们之间可以同时建立非常多的连接,连接也都可以keep-alive,并且他们之间的数据通信远远高于我们浏览器-服务器。所以总体来看这套方案是把原来“浏览器-服务器的http1.1的请求”拆分为“浏览器-nginx  h2请求”和“nginx-服务器 http1.1请求”,而后者的效率是不足以让我们担心的。如下图:

前提是服务端支持https,且所提的nginx是部署在后台服务器的,而非我们前端配置nginx。

上述策略,仅仅是我个人猜想,自己服务器相关技术能力有限,并未验证,如果真的具有可行性,即能体验h2所带来的快感,提高前后端的通讯效率,又能避免维护后台库所带来的的高额成本。有条件的老铁,可以帮忙验一下。

 

以上总结,不对之处,还请批评指正。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值