美文网首页
http协议篇

http协议篇

作者: 三天两觉_ | 来源:发表于2019-01-21 21:57 被阅读0次

    cors预请求(跨域的时候回出现预请求的验证 )

    允许的方法有
    1. GET
    2. HEAD
    3. POST
    允许的Content-Type有
    1. text/plain
    2. multipart/form-data
    3. application/x-www-form-urlencoded
    其他限制
    1. 请求头限制
    • 自定义的请求头
    • XMLHttpRequestUpload对象没有注册任何事件监听器
    • 请求中没有使用ReadableStream对象
      但我们可以在服务端设置允许的请求头,比如
      response.writeHead(200,{
      'Access-Control-Allow-Origin' : '*', //允许跨域的域名
      'Access-Control-Allow-Headers' : 'custom-Test',//允许跨域的请求头,可以是自定义的
      'Access-Control-Allow-Methods' : 'POST, PUT, Delete',//允许跨域的请求方法
      'Access-Control-Max-Age' : '1000'//不需要再次发送预请求的最大时间,单位是秒
      });
      在跨域的时候,会发现程序写的只发送了一条请求,但network出现了两条,其中一条请求头的除了Mehods的类型是OPTIONS,其他信息和我们发出的请求一模一样,该请求就是发送的预请求。

    缓存Cache-Control

    可缓存性(哪些地方可以去缓存)

    • public 指的是发送的http请求返回内容所经过的任何路径当中,包括一些中间的http的代理服务器,以及发出这个请求的客户端浏览器,都可以进行对于返回内容的缓存的操作,缓存操作指的就是把这份内容缓存在本地,下一次请求可以直接读缓存去使用
    • private 指的是只有发起求情的浏览器才能够进行缓存
    • no-cache 指的是任何节点都不能进行缓存。

    到期

    • max-age = <seconds>缓存多少秒后过期
    • s-maxage = <seconds>只在代理服务器才生效,同时设置max-age和s-max-age,s-max-age会代替maxage;是专门为代理服务器设置的,客户端浏览器还是读取max-age作为缓存的时间。
    • max-stale = <seconds>max-age是发起请求的一方主动带的请求头,在max-age过期之后,如果返回的资源中有max-stale的设置,代表即便缓存过期了,但是只要是在max-stale的时间内,还可以使用过期的缓存,而不需要去源服务器去请求新的内容,只有在发起端设置是有用的,服务端设置是没有用的。

    重新验证

    • must-revalidate 在设置了max-age的缓存当中,如果过期了,必须从服务端,源服务端去方请求重新获取数据验证内容是否真的过期了,而不能直接使用本地的缓存。
    • proxy-revalidate 和must-revalidate基本一个意思,不过是作用于缓存服务器当中的,在过期后,缓存服务器必须从源服务器请求一遍,而不能使用本地的缓存。

    其他

    • no-store 要和no-cache做一个区分,no-cache是可以在本地进行缓存的,也可以在代理服务器进行缓存,但每次发起请求都要去服务器那边做验证,如果服务器返回告诉可以用本地的缓存,然后请求方才能去使用缓存。no-store指本地和代理服务器都不可以缓存,打个不恰当的比方,即使告诉浏览器可以使用缓存,浏览器本地也没有缓存。
    • no-transform 作用于代理服务器,有些代理服务器觉得返回的资源太大了,代理服务器会进行一些压缩,进行一些格式转换,通过no-transform禁止代理服务器的这种行为。


      image.png

    验证

    • Last-Modifiled 上次修改时间,配合If-Modified-Since或者If-Unmodified-Since使用
      我们请求一个资源,请求的资源返回了Last-Modifield头信息,指定了一个时间,这个时间在下一次浏览器发起请求的时候就会带上Last-Modifield传过来的值,比如值为val,那么浏览器会设置头信息If-Modified-Since或者If-Unmodified-Since,一般浏览器都是使用If-Modified-Since,If-Unmodified-Since很少会被用到,头信息的值就是val,服务器可以通过读取request的头信息,找到资源存在的地方,找到其修改的时间,如果发现这两个时间是一致的,代表该资源未被修改过,然后就告诉浏览器可以使用缓存的资源,如果不一致,就返回新的资源。
    • Etg 是一种更加严格的验证,通过数据签名 ,配合If-Match或者If-Non-Match使用,我们的资源,也就是内容会产生一个唯一的签名,如果资源的数据进行过修改,那么签名就会产生一个新的,最典型的做法就是会对资源的内容进行一个hash计算得到一个唯一值。也就是浏览器在请求一个资源的时候响应头会返回该资源的签名值,下次浏览器去请求的时候会带上头信息If-Match或者If-Non-Match,服务器收到后会和现在该资源的签名去做对比,如果一致,就不需要返回新的内容。
    const http = require('http');
    const fs = require('fs');
    
    
    http.createServer(function (request, response) {
        console.log('request come',request.url);
    
        if(request.url == '/'){
            const html = fs.readFileSync('test.html','utf-8');
            response.writeHead(200,{
                'Content-Type':'text/html'
            });
            response.end(html);
        }
        if(request.url == '/script.js'){
            const etag = request.headers['if-none-match'];
            if(etag === '777'){
                /**
                 * 1.当写入头为304的时候,即使我们把Etag的值改变了,浏览器也不会重新拿到新资源,
                 * 也不会更新响应头返回的Etag的新值,所以下次请求的时候用的还是老的Etag的值。
                 * 2.当设置Cache-Control的值no-cache时候,如果响应头返回了Last-Modified和Etag的值,
                 * 浏览器再次发送请求的时候会添加Last-Modified和Etag的请求头;
                 * 当设置Cache-Control的值no-cache时候,即使应头返回了Last-Modified和Etag的值,
                 * 浏览器再次发送请求也不会添加Last-Modified和Etag的请求头;
                 **/
                response.writeHead(304,{
                    'Content-Type':'application/javascript',
                    'Cache-Control':'max-age=2000000,no-store',
                    'Last-Modified':'123',
                    'Etag':'7778'
                });
                response.end('some word')
            }else{
                response.writeHead(200,{
                    'Content-Type':'application/javascript',
                    'Cache-Control':'max-age=2000000,no-store',
                    'Last-Modified':'123',
                    'Etag':'777'
                });
                response.end('console.log("script loaded twice")');
            }
        }
    
    }).listen(8888);
    
    console.log('service listening on 8888');
    

    Cookie

    • 通过Set-Cookie设置 :Cookie是在服务端返回数据的时候通过添加Set-Cookie请求头设置在浏览器里面保存内容的;保存的内容就叫做Cookie。
    • 下次求情会自动带上
    • 键值对,可以设置多个

    Cookie属性

    • max-age和expires设置过期时间
    • Secure 只在https的时候发送
    • HttpOnly 设置了HttpOnly属性以后,浏览器端就无法通过 document.cookie访问
    const http = require('http');
    const fs = require('fs');
    
    http.createServer(function (request, response) {
        console.log('request come',request.url);
        const host = request.headers.host;
        if(request.url == '/'){
            const html = fs.readFileSync('./test.html','utf-8');
            /**注意,下面代码我们在a.test.com的域名设置domain的值为test.com,其实是不能成功的,也就是说不能
             * 在二级域名下设置一级域名的cookie;此时cookie不能被设置成功。
             * **/
            if(host==='a.test.com'){
                response.writeHead(200,{
                    'Content-Type':'text/html',
                    'Set-Cookie':['id=123;max-age=2','abc=456;HttpOnly','def=789;domain=test.com']
                });
        }
            response.end(html);
        };
    }).listen(8888);
    
    console.log('service listening on 8888');
    

    修改代码

    if(host==='test.com'){ 
          response.writeHead(200,{
              'Content-Type':'text/html',
              'Set-Cookie':['id=123;max-age=2','abc=456;HttpOnly','def=789;domain=test.com']
          });
    }
    

    此时是可以设置成功的,由于设置的domain的值是一级域名test.com。那么a.test.com以及b.test.com都会带上所设置的cookie。

    Http长连接

    发送http请求需要创建TCP连接,在http0.9和http1.0的时候每发送一次请求都需要建立创建一个TCP的连接,请求响应后这个连接就会关闭,下次发请求会重新创建TCP连接;http1.1之后包括http1.1是支持长连接的,意思是只创建一次TCP连接,
    之后的请求都通过这个连接。我们打开百度,随意输入一个关键字去搜索,打开network截图如下:


    image.png

    图中有一列显示的是connectionID,connectionID相同表明是同一个TCP连接。http1.1的连接在TCP上发送请求是有先后顺序的,不能在同一个TCP连接上去并发的发送。我们在加载首页的时候是希望去并发的,因为这样效率会高一点;所以浏览器可以允许并发的创建TCP连接,数目会有限制,比如chrome浏览器限制的数量为6;


    image.png
    图中可以看出,connectionID为93370的TCP连接发送了三次请求,后一次请求要等待前一次的返回。

    数据协商

    分类

    • 请求
      1.Accept通过Accept 声明浏览器想要的数据
      2.Accept-Encoding 代表数据的编码方式,主要限制服务端如何进行数据压缩,比如gzip,deflate,br
      3.Accept-Language
      4.User-Agent
    • 返回
      1.Content-Type
      2.Content-Encoding
      3.Content-Language


      image.png

    Redirect

    const http = require('http');
    http.createServer(function (request, response) {
        console.log('request come',request.url);
        if(request.url=='/'){
            response.writeHead(301,{
               'Location':'/new'
            });
            response.end('');
        }
        if(request.url=='/new'){
            response.writeHead(200,{
                'Content-Type':'text/html'
            });
            response.end('<h3>this is content</h3>');
        }
    }).listen(8888);
    console.log('service listening on 8888');
    

    301(临时重定向)和302(永久重定向)区别是 302会先发送请求到服务端,服务端返回之后浏览器再次发送请求,一共发送了两次请求;302是服务端高告知浏览器如果再次出现某个资源的请求路径,直接在浏览器端改变求情资源的地址,所以只会发送一次请求。要特别注意的是,一旦做了301的跳转,浏览器会尽可能长时间的去做缓存,即使之后服务端修改了响应头,改为200,浏览器也是无感知的,除非是客户端用户自己手动清理浏览器的缓存才能得到新的响应资源,所以使用301一定要慎重。

    CSP Content-Security-Policy 内容安全策略

    作用

    • 限制资源获取
    • 报告资源获取越权

    限制方式

    • default-src限制全局 限制跟请求链接的作用范围
    • 制定资源类型
      类型种类很多,比如:default-src(全局的限制,包括img资源、css资源、js资源等等) , content-src ,img-src , mainfest-src , font-src , media-src ,
      style-src , frame-src , script-src ...
      test.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h3>scp</h3>
        <script>
            console.log('scp test...')
        </script>
        <script src="/test.js"></script>
        <script src="http://code.jquery.com/jquery-3.2.1.min.js"></script>
    </body>
    </html>
    

    服务端 service.js

    const http = require('http');
    const fs = require('fs');
    
    http.createServer(function (request, response) {
        console.log('request come',request.url);
        const html = fs.readFileSync('./test.html','utf-8');
        if(request.url==='/'){
            response.writeHead(200,{
                'Content-Type':'text/html',
                //只允许通过外链加载的js执行
                // 'Content-Security-Policy':'default-src http: https:'
    
                // 只允许通过外链的从本域加载的js执行
                'Content-Security-Policy':'default-src \'self\''
    
                //只允许通过外链的从本域或者 http://code.jquery.com/加载的js执行
                // 'Content-Security-Policy':'default-src \'self\' http://code.jquery.com/'
            });
            response.end(html);
        }else{
            response.writeHead(200,{
                'Content-Type':'application/javascript'
            });
            response.end('console.log("loaded script")');
        }
    
    }).listen(8888);
    
    console.log('service listening on 8888');
    
    image.png

    Content-Security-Policy的设置可以在服务端,亦可以在客户端返回的html文档。

    nginx代理

    • 下载安装http://nginx.org/en/download.html
      image.png
      下载后解压
      目录结构如下:
      image.png
      配置文件在conf下面的nginx.conf文件中,我们建一个servers文件夹
      在下面配置需要代理的域名。
      文件nginx.conf中加入
      include servers/*.conf;
      表明include servers文件夹下面所有以.conf结尾的文件。
      我们配置一个test.conf内容如下
    proxy_cache_path cache levels=1:2 keys_zone=my_cache:10m;
    
    server {
        listen          80;
        server_name     test.com;
        location / {
            proxy_cache my_cache;
            proxy_pass http://127.0.0.1:8888;
            proxy_set_header Host $host;
        }
    }
    
    server {
        listen          80;
        server_name     a.test.com;
        location / {
            proxy_pass http://127.0.0.1:8888;
            proxy_set_header Host $host;
        }
    }
    
    server {
        listen          80;
        server_name     b.test.com;
        location / {
            http://127.0.0.1:8888 http://127.0.0.1:8888;
            proxy_set_header Host $host;
        }
    }
    

    其中server是指要在电脑上起一个服务,其端口是80,它的server_name即域名为test.com,这个server_name指的就是我们在浏览器上面要访问的hostname,nginx会根据要访问的hostname来判断要把服务启动在什么地方,代理到什么地方。比如上面配置了test.com,说明要访问的地址是test.com,然后test.com被代理到的服务是http://127.0.0.1:8888。(要记得在hosts文件中配置test.com的映射地址,如127.0.0.1);之后我们写后端服务。
    service.js代码如下

    const http = require('http');
    const fs = require('fs');
    
    const wait = (seconds)=>{
        return new Promise((resolve,reject)=>{
           setTimeout(()=>{
               resolve();
           },seconds*1000);
        });
    };
    
    http.createServer(function (request, response) {
        console.log('request come',request.url);
        const host = request.headers.host;
        /**
         * const html = fs.readFileSync('./test.html','utf-8');
         * 此处不能用utf-8编码,因为utf-8读取时按照字符串读取,使用zlib的话,
         * 希望读取的是buffer
         * */
        const html = fs.readFileSync('./test.html');
        if(request.url==='/'){
            response.writeHead(200,{
                'Content-Type':'text/html',
            });
            response.end(html);
        }
        if(request.url==='/data'){
            /**s-maxage是专门给代理缓存用的,同时使用了max-age=20, s-maxage=20,
             * 那么代理缓存会使用s-maxage,浏览器会使用max-age。
             * **/
            response.writeHead(200,{
                'Cache-Control':'max-age=2;s-maxage=20'
            });
            wait(2).then(()=>{
                response.end('success')
            });
        }
    }).listen(8888);
    
    console.log('service listening on 8888');
    

    test.html代码如下

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>nginx-cache</title>
    </head>
    <body>
        <div>
            <span>this is content, and data is: </span>
            <span id="data"></span>
        </div>
        <script>
            fetch('/data').then(function (resp) {
               return resp.text();
            }).then(function (text) {
                document.getElementById('data').innerText = text;
            })
        </script>
    </body>
    </html>
    

    test.conf的配置是

    proxy_cache_path cache levels=1:2 keys_zone=my_cache:10m;
    
    server {
        listen          80;
        server_name     test.com;
        location / {
            proxy_cache my_cache;
            proxy_pass http://127.0.0.1:8888;
            
        }
    }
    
    server {
        listen          80;
        server_name     b.test.com;
        location / {
            proxy_pass http://127.0.0.1:8888;
            proxy_set_header Host $host;
        }
    }
    

    从代码中可以看出,在访问'/'路径会返回test.html,在test.html中发起了一个请求,并将结果文本显示到页面上。服务端我们延时了2s去返回请求,并设置了响应头'Cache-Control':'max-age=2, s-maxage=20',告诉浏览器可以缓存结果2秒(max-age=2),告诉代理服务器可以缓存20秒(s-maxage=20)。启用代理服务缓存还需要在具体的server配置 proxy_cache。参照上面的test.conf。
    修改service.js ,设置Cache-Control的值为

    response.writeHead(200,{
                'Cache-Control':'max-age=5,s-maxage=20,private'
            });
    

    在浏览器端重新刷新,会发现s-maxage=20将不再生效。private是指只有浏览器才可以缓存数据。

    HTTPS 即http+secret

    加密

    • 私钥
    • 公钥
      公钥放在互联网上所有人都可以拿到的遗传加密的字符串,这个加密字符串是用来加密我们传输的信息的,使用公钥加密的数据传输到服务器之后,只有服务器通过私钥进行解密才能把公钥加密过的数据解析出来。而私钥只放在服务器上,其他任何人都拿不到私钥。所以中间人即便截取了http的消息,但没有私钥还是解密不了数据的。公钥和私钥主要是用在握手的时候进行一个传输,传输内容是一个正真的在后期传输的过程中使用的加密字符串,因为加密字符串是使用公钥私钥的方式进行加密传输的,所以中间人拿不到加密字符串,后续的数据传输过程中客户端和服务端都是用这个加密字符串进行数据加密传输。
    image.png

    相关文章

      网友评论

          本文标题:http协议篇

          本文链接:https://www.haomeiwen.com/subject/vztcdqtx.html