HTTP

1. 引入http

1.1 HTTP最简单的例子
  • 输入URL打开页面
  • AJAX获取数据
  • img标签的src通过http加载图片
1.2.一个完整的HTTP请求过程

浏览器提供一个叫 performance 的api会记录每一个时间点,HTTP协议在每一个时间点都会发挥作用,每个环节时间过程的消耗会通过这个api进行记录,从而对耗时的环节进行优化。

  • 一开始浏览器会去判断需不需要重定向,如果重定向,重定向到了哪里。
  • 看app cache里是否有缓存,如果没有就去服务器请求资源。
  • 请求资源输入域名,通过DNS解析找到对应的是IP,从而请求资源。
  • 有了IP之后创建TCP连接,三次握手,如果这是一个HTTPS请求,和TCP的三次握手不一样,中间有一个保证安全的数据传输的过程。
  • 连接创建好之后开始真正发送请求数据包。
  • 服务器接收到请求数据包,进行数据操作之后,返回一个请求想要的内容。

2. HTTP协议基础及发展历史

2.1 五层网络模型介绍

  • 低三层

    • 物理层主要作用是定义物理设备如何传输数据(物理设备)
    • 数据链路层在通信的实体间建立数据链路连接(0101)
    • 网络层在结点之间传输创建逻辑链路(寻找google服务器的地址,建立逻辑连接)
  • 传输层

    • 向用户提供可靠的(TCP)端到端(end-to-end)服务(建立连接之后,分包,分片等操作)
    • 传输层向高层屏蔽下层数据通信的细节(http是建立在tcp协议之上的,输入url之后解析,分片等操作用户是不需要知道细节的)
  • 应用层

    • 为应用软件提供很多服务,使用http协议
    • 构建于TCP协议之上,屏蔽网络传输相关细节
2.2 HTTP协议的发展历史
  • HTTP/0.9

    • 只有一个命令get
    • 没有header等描述数据的信息
    • 服务器发送完毕,就关闭tcp连接(现在同一个tcp连接里可以发送多个http请求)
  • HTTP/1.0

    • 增加了很多命令
    • 增加status code和header
    • 多字符集支持,多部分发送,权限,缓存等
  • HTTP/1.1

    • 在1.0基础上优化整个网络链接的过程
    • 持久连接(一个http请求创建一个tcp连接,服务端返回内容后连接关闭,再建立,成本较大,持久连接可以提高性能)
    • pipeline(一个连接里可以发送多个请求,服务端按照请求先后顺序进行内容的返回,串型返回)
    • 增加了host和其他一些命令(通过host可以指向一个物理服务器上要访问的是哪个软件服务)
  • HTTP2

    • http1中数据通过字符串传输,http2中所有数据以二进制进行传输(在传输层的数据叫段, 网络层叫包,数据链路层叫帧,物理层叫比特流)
    • 同一个连接里发送多个请求不再需要按照顺序来
    • 头信息压缩以及推送等提高效率的功能(http1中头信息需要发送且原样返回,且以字符串形式传输,占用大,推送是服务端可以主动发送)
2.3 http的三次握手

http仅有请求和响应的概念,都是数据包,数据包需要经过传输的通道,在tcp中创建的连接,一个tcp连接可以发送多个http请求

  • 通过数据包传输标志位
  • 三次握手是为了防止服务端开启无用的网络连接,减少网络延迟导致的网络开销
2.4 URI/URL/URN
  • URI

    • Uniform Resource Identifier/统一资源标志符
    • 用来唯一标识互联网上的信息资源
    • 包括URL和URN
  • URL

  • URN

    • 永久统一资源定位符
2.5 HTTP报文格式

  • http方法用来定义对资源的操作,常用有get,post,有语义
  • http code定义服务器对请求的处理结果,各个区间的code有各自的语义,好的http服务可以通过code判断结果
2.6 创建一个最简单的web服务
  • 创建一个server.js文件
const http = require('http')

http.createServer(function (request, response) {      //request 传入的封装  response返回
    console.log('request come', request.url)
    response.end('123')
}).listen(8888)

console.log('server listening on 8888')
复制代码
  • 命令行node server.js启动服务,访问localhost:8888

3. HTTP各种特性总览

3.1 认识HTTP客户端
curl baidu.com

//返回
<html>
<meta http-equiv="refresh" content="0;url=http://www.baidu.com/">
</html>
复制代码
curl www.baidu.com

<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn"></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新闻</a> <a href=http://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地图</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>视频</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>贴吧</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登录</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登录</a>');</script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多产品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>关于百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>&copy;2017&nbsp;Baidu&nbsp;<a href=http://www.baidu.com/duty/>使用百度前必读</a>&nbsp; <a href=http://jianyi.baidu.com/ class=cp-feedback>意见反馈</a>&nbsp;京ICP证030173号&nbsp; <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>
复制代码
curl -v www.baidu.com

* Rebuilt URL to: www.baidu.com/
*   Trying 115.239.210.27...
* TCP_NODELAY set
* Connected to www.baidu.com (115.239.210.27) port 80 (#0)
> GET / HTTP/1.1
> Host: www.baidu.com
> User-Agent: curl/7.54.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
< Connection: Keep-Alive
< Content-Length: 2381
< Content-Type: text/html
< Date: Tue, 23 Apr 2019 08:18:50 GMT
< Etag: "588604fc-94d"
< Last-Modified: Mon, 23 Jan 2017 13:28:28 GMT
< Pragma: no-cache
< Server: bfe/1.0.8.18
< Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
< 
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn"></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新闻</a> <a href=http://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地图</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>视频</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>贴吧</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登录</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登录</a>');</script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多产品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>关于百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>&copy;2017&nbsp;Baidu&nbsp;<a href=http://www.baidu.com/duty/>使用百度前必读</a>&nbsp; <a href=http://jianyi.baidu.com/ class=cp-feedback>意见反馈</a>&nbsp;京ICP证030173号&nbsp; <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>
* Connection #0 to host www.baidu.com left intact
复制代码
3.2 CORS跨域请求的限制与解决

跨域浏览器也发送也接收了返回数据,但是浏览器因为安全策略做了限制,需要head里返回一个值告诉浏览器这是被允许的。

  • node server.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', 'utf8')   //设置格式utf8
    response.writeHead(200, {
        'Content-Type': 'text/html'                //设置格式text/html,不写默认是这种 text/plain 解析成字符
    })
    response.end(html)

}).listen(8888)

console.log('server listening on 8888')
复制代码
  • node server2.js
const http = require('http')

http.createServer(function (request, response){
    console.log('request come', request.url)

    response.writeHead(200, {
        'Access-Control-Allow-Origin': '*'
    })

    response.end('123')

}).listen(8887)

console.log('server listening on 8887')
复制代码
  • test.html页面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    
</body>
<script>
    var xhr = new XMLHttpRequest()
    xhr.open('GET', 'http://127.0.0.1:8887/')
    xhr.send()
</script>

<!-- JSONP是利用 link image src没有跨域限制,可以不设置头 -->
<!-- <script src="http://127.0.0.1:8887/"></script> -->
</html>
复制代码
3.3 CORS跨域限制以及预请求验证
<!--test.html中-->
<script>
  fetch('http://localhost:8887/', {
      method: 'POST',
      headers: {
          'X-Test-Cors': '123'
      }
  })
</script>
复制代码

自定义的头在跨域请求中是不被允许的

  • CORS允许方法

    • get head post
    • 这三个是不需要CORS预请求的,是直接允许的
  • CORS允许的Content-Type

    • text/plain
    • multipart/form-data
    • application/x-www-form-urlencoded
  • CORS其他限制:

    • 请求头限制
    • XMLHttpRequestUpload对象均没有注册任何事件监听器
    • 请求中没有使用ReadableStream对象
  • 发送预请求

'Access-Control-Allow-Headers': 'X-Test-Cors'
'Access-Control-Allow-Methods': 'POST PUT Delete'
复制代码
  • 发送预请求,headers中Request Method: OPTIONS
'Access-Control-Allow-Max-Age': '1000'
//1000秒之内不需要发送预请求
复制代码
3.4 缓存头Cache-Control的含义和使用
  • Cache-Control的可缓存性
    • public:所有客户端以及服务器都可以缓存
    • private:发起请求的浏览器可以缓存
    • no-cache:都不可以缓存
  • Cache-Control到期
    • max-age=数字 到期重新发送请求,请求一个新的内容
    • s-maxage=数字 代理服务器中才会生效,会代替max-age
    • max-stale=数字 即便缓存已经过期,在max-stale的时间内还可以使用,不需要重新发送请求,发起端设置,服务端设置没用
  • 重新验证
    • must-revalidate: max-age到期后必须去原服务端重新请求
    • proxy-revalidate: 用在缓存服务器中,作用同上
  • 其他
    • no-store:本地和服务器都不能使用缓存
    • no-transform:用在proxy服务器上,告诉服务器不要改动返回数据的内容

  • cache-control 达到长缓存实例

server.js


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', 'utf8')
    response.writeHead(200, {
      'Content-Type': 'text/html'
    })
    response.end(html)
  }

  if (request.url === '/script.js') {
    response.writeHead(200, {
      'Content-Type': 'text/javascript',
      'Cache-Control': 'max-age=20'       //客户端(浏览器)缓存
    }) 
    response.end('console.log("script loaded")')
  }
}).listen(8888)

console.log('server listening on 8888')
复制代码

test.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
    
</body>
<script src="/script.js"></script>
</html>
复制代码

启动server.js后,url='/',读取到test.html文件,执行script文件,url变为/script.js,客户端设置cache-control为max-age=20,在设置时间内,即使respond.end中的输出改变,客户端也不会变,在设置时间内使用客户端缓存,不会经过服务端验证。

  • 目前通过打包后文件路径后的hash码,对比是否改变,从而发起新的请求,刷新浏览器缓存。

3.5 缓存验证Last-Modified和Etag的使用

-control设置了no-cache,每一个浏览器发起请求都会到服务器端进行资源的验证,验证之后允许读取本地缓存,才会读取本地缓存

  • 验证头(验证通过验证头进行)
    • Last-Modified
      • 上次修改时间
      • 配合返回的if-modified-since或者if-unmodified-since使用
      • 对比上次修改时间以验证资源是否需要更新
    • Etag(更加严格的验证)
      • 通过数据签名,有修改会生成唯一的新签名
      • 配合返回的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', 'utf8')
    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') {
      response.writeHead(304, {
        'Content-Type': 'text/javascript',
        'Cache-Control': 'max-age=2000000, no-cache',
        'Last-Modified': '123',
        'Etag': '777'
      })
      response.end()
    } else {
      response.writeHead(200, {
        'Content-Type': 'text/javascript',
        'Cache-Control': 'max-age=2000000, no-cache',
        'Last-Modified': '123',
        'Etag': '777'
      })
      response.end('console.log("script loaded twice")')
    }
  }
}).listen(8888)

console.log('server listening on 8888')
复制代码
  • 设置max-age超长时间,no-cache,不允许直接使用缓存,需要向服务器验证


3.6 cookie和session
  • cookie

    • cookie的基本概念
      • 在服务端返回数据时,通过set-cookie设置到浏览器中的内容,浏览器在和服务端传送数据时,携带cookie来保证用户数据一致性
      • 浏览器下次请求会自动带上
      • 键值对,可以设置多个
    • cookie的属性
      • max-age和expires设置过期时间
      • secure表示只在https中才会发送
      • 设置httponly则无法使用document.cookie
  • session和cookie是不同的,一般把session存在cookie中


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', 'utf8')
    response.writeHead(200, {
      'Content-Type': 'text/html',
      'Set-Cookie': ['id=123; max-age=2', 'abc=456;domain=test.com']
    })
    response.end(html)
  }

}).listen(8888)

console.log('server listening on 8888')
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <div>Content</div>
</body>
<script>
  console.log(document.cookie)
</script>
</html>
复制代码

3.7 HTTP长连接
  • http请求是建立在tcp连接上的,tcp链接是长链接,复用tcp连接,一个tcp连接可以发送多个http请求
  • chrome支持并发的6个连接

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', 'utf8')
  const img = fs.readFileSync('test.jpg')
  if (request.url === '/') {
    response.writeHead(200, {
      'Content-Type': 'text/html',
    })
    response.end(html)
  } else {
    response.writeHead(200, {
      'Content-Type': 'image/jpg',
      'Connection': 'keep-alive' // or close
    })
    response.end(img)
  }

}).listen(8888)

console.log('server listening on 8888')
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <img src="/test1.jpg" alt="">
  <img src="/test2.jpg" alt="">
  <img src="/test3.jpg" alt="">
  <img src="/test4.jpg" alt="">
  <img src="/test5.jpg" alt="">
  <img src="/test6.jpg" alt="">
  <img src="/test7.jpg" alt="">
  <img src="/test11.jpg" alt="">
  <img src="/test12.jpg" alt="">
  <img src="/test13.jpg" alt="">
  <img src="/test14.jpg" alt="">
  <img src="/test15.jpg" alt="">
  <img src="/test16.jpg" alt="">
  <img src="/test17.jpg" alt="">
  <img src="/test111.jpg" alt="">
  <img src="/test112.jpg" alt="">
  <img src="/test113.jpg" alt="">
  <img src="/test114.jpg" alt="">
  <img src="/test115.jpg" alt="">
  <img src="/test116.jpg" alt="">
  <img src="/test117.jpg" alt="">
</body>
</html>
复制代码


3.8 数据协商

客户端发送给服务端请求时,客户端会申明想要的数据格式等,服务端会做出判断从而返回

  • 客户端请求
    • Accept声明想要的数据
      • Accept声明想要的数据类型(多种)
      • Accept-Encoding限制数据格式
      • Accept-Language返回语言
      • User-Agent声明客户端处于哪种环境(判断返回pc还是移动页面)
  • 服务端返回
    • Content
      • Content-Type选择Accept中的一种进行返回,声明实际返回的类型
      • Content-Encoding服务端用了什么压缩方式
      • Content-Language返回语言
3.9 Redirect
3.10 CSP
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值