1. 了解部分有意义的头部信息
头部信息 | |
Catch-Control:max-age=100 | 静态资源缓存100s |
Content-Type,Content-Encoding 等 | 用来约束数据类型 |
Cookie | 保持会话信息 |
CORS | 实现跨域并保持安全性限制 |
2. 页面如何到达浏览器并展现给用户
Redirect(跳转)--> App catch(应用缓存)-->DNS(DNS查找)-->TCP (创建TCP链接)-->Request(发送请求)
-->Response(接受响应)
1. redirect : 对于浏览器已经记录过的301请求,直接跳转,属于纯客户端的行为。
2. cache: 根据资源是否有设置cache control ,然后判断缓存。如果超时则要重新请求缓存。
3. DNS查询:当通过浏览器请求一个web页面,浏览器会创建一个线程去处理这个请求,随后开始远程dns查找,远程dns服务器会将输入的URL对应的IP地址返回给浏览器。(DNS域名系统)
4. TCP连接: 三次握手 (HTTP请求是在TCP连接上发送的,一个TCP连接可以发送多个HTTP请求)
5. 发送请求: 数据经过代理服务器或代理服务器缓存直接读取。浏览器通过连接发送一个HTTP GET请求到Web服务端。
6. 接受响应。Web服务端找到请求的资源,然后在HTTP响应中将其返回,状态200表示响应正常。
常用状态码:
- 200: 服务端成功响应
- 301: 永久重定向
- 302: 临时重定向
- 403: 请求被拒绝
- 404: 服务端找不到请求的资源
- 500: 处理请求时出错
- 503: 服务不可用
- 504: 网关超时
最后,浏览器收到页面HTML,就开始解析并渲染页面内容
3. 网络协议分层
经典的五层网路模型:
物理层:定义物理设备如何传输数据(电脑硬件,网卡端口,光缆...)。
数据链路层: 在通信的实体间建立数据链路连接。
网络层:为数据在节点之间传输创建逻辑链路。
传输层: 向用户提供可靠的端对端(end-to-end)服务。更多情况下实用TCP/IP协议传输数据。
定义如何传输数据,传输数据的方式。
应用层:为应用软件提供了很多的服务。构建于TCP协议之上。
4. HTTP/1.1
pipeline: 可以通过声明,在同一个连接里发送多个请求
增加host:可以在同一台物理服务器跑多个不同的web服务
HTTP 2 : 所有数据以二进制传输,同一个连接里发送多个请求不再需要按照顺序,头信息压缩以及推送等提高效率的功能。
推送功能(请求html的同时,可以将html中需要引用到的JavaScript文件和css文件推送到客户端,实现html和css js文件的发送顺序是闭型的,提高整体的传输效率)
5. 三次握手
为了防止服务端开启无用的连接。(网络服务器延迟等)
浏览器通过与远程web服务端的三次握手来建立一个TCP/IP请求。这个握手由浏览器与远程服务端之间的SYN(标志位),SYN-ACK,ACK消息组成。(SYN:同步序列编号)
图1 TCP 三次握手
6. 统一资源定位器 Uniform Resource Locators
scheme://host.domain:port/path/filename
说明:
- scheme - 定义因特网服务的类型。最常见的类型是 http
- host - 定义域主机(http 的默认主机是 www)
- domain - 定义因特网域名,比如 runoob.com
- :port - 定义主机上的端口号(http 的默认端口号是 80)
- path - 定义服务器上的路径(如果省略,则文档必须位于网站的根目录中)。
- filename - 定义文档/资源的名称
7. 创建一个最简单的web服务
server.js文件下创建如下内容:
const http = require('http')
http.createServer((request, response) => {
console.log('request', request.url)
response.end('123')
}).listen(8888)
console.log('server listening on 8888')
启动: node server.js
浏览器打开:localhost:8888, 查看内容
利用curl命令查看http报文信息
$ curl -v www.baidu.com
能得到报文头和header的内容:
* Rebuilt URL to: www.baidu.com/
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying 14.215.177.39...
* TCP_NODELAY set
* Connected to www.baidu.com (14.215.177.39) port 80 (#0)
> GET / HTTP/1.1
> Host: www.baidu.com
> User-Agent: curl/7.57.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: Fri, 17 Aug 2018 05:07:47 GMT
< Etag: "588604cf-94d"
< Last-Modified: Mon, 23 Jan 2017 13:27:43 GMT
< Pragma: no-cache
< Server: bfe/1.0.8.18
< Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
<
{ [1048 bytes data]
100 2381 100 2381 0 0 2381 0 0:00:01 --:--:-- 0:00:01 74406
8. 跨域请求
新建server.js: 创建请求
//在8888端口中请求了一个8887端口的内容
const http = require('http')
const fs = require('fs')
http.createServer((request, response) => { //创建请求
console.log('request', request.url)
const html = fs.readFileSync('test.html', 'utf8'); //同步读取html内容
response.writeHead(200, {
'Content-Type': 'text/html'
})
response.end(html) //响应内容
}).listen(8888)
console.log('server listening on 8888')
server2.js
const http = require('http')
http.createServer((request, response) => {
console.log('request', request.url)
// 允许跨域请求 写返回的状态
response.writeHead(200, {
'Access-Control-Allow-Origin': '*'
})
response.end('123')
}).listen(8887)
console.log('server listening on 8887')
test.html 中向8887端口发送请求:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<script>
var xhr = new XMLHttpRequest()
xhr.open('GET', 'http://127.0.0.1:8887/')
xhr.send()
</script>
</body>
</html>
发送预请求:(跨域限制:保证服务端的安全)
预请求 | |
---|---|
允许的方法 | GRT、POST、HEAD |
允许Content-Type | text/plain、multipart/formdata、 application/x-www-form-urlencoded |
通过options请求获得服务器的认可
server.js:
//相当于在8888端口中请求了一个8887端口的内容
const http = require('http')
const fs = require('fs')
http.createServer((request, response) => { //创建请求
console.log('request', request.url)
const html = fs.readFileSync('test.html', 'utf8'); //同步读取html内容
response.writeHead(200, {
'Content-Type': 'text/html'
})
response.end(html) //响应内容
}).listen(8888)
console.log('server listening on 8888')
server2.js:
const http = require('http')
http.createServer((request, response) => {
console.log('request', request.url)
// 允许跨域请求 写返回的状态
response.writeHead(200, {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'X-Test-Cors', //允许自定义的头
'Access-Control-Allow-Methods': 'PUT, POST, GET',
'Access-Control-Max-Age': '1000'
})
response.end('123')
}).listen(8887)
console.log('server listening on 8887')
Access-Control-Max-Age 这个响应首部表示 preflight request (预检请求)的返回结果(即 Access-Control-Allow-Methods 和Access-Control-Allow-Headers 提供的信息) 可以被缓存多久。
缓存Cache-Control: (哈希值用于刷新浏览器缓存)
可缓存性 | |
---|---|
public | 任何地方都可以对返回的内容缓存 |
private | 只有发起请求的地方可以 |
no-cache | 通过服务器端验证后才能使用本地缓存 |
到期 | |
max-age=<seconds> | 缓存还有多过期。 可以将max-age设置为0, 从而让每次访问时缓存都进行刷新。 |
max-stale=<seconds> | 可以使用多久的过期缓存 |
重新验证 | |
must-revalidata | 如果要提供过期的数据给客户端,则必须向服务器验证数据的新鲜度 |
缓存验证:
缓存验证 | |
---|---|
Last-Modified | '123' 标记此文件在服务器端最后被修改的时间 |
Etag | '777' 用于标示URL对象是否改变 |
no-store: 忽略缓存
9. Cookie和Session
Cookie:1. 通过Set-Cookie设置 2. 下次请求会自动带上 3.键值对,可以设置多个
属性:
Cookie属性 | |
---|---|
max-age和expires | 设置过期时间 |
Secure | 只在https的时候发送 |
HttpOnly | 无法通过document.cookie访问,保证用户安全 |
服务端server.js:
const http = require('http')
const fs = require('fs')
http.createServer((request, response) => { //创建请求
console.log('request', request.url)
if (request.url === '/') {
const html = fs.readFileSync('test.html', 'utf8'); //同步读取html内容
response.writeHead(200, {
'Content-Type': 'text/html',
'Set-Cookie': ['abc=123; max-age=2','id=456'], //可以以数组方式传递多个cookie 前面一个字段的有效时间为两秒
// 2s后abc=123这个数据就过期,request headers中就不会有这个信息出现
})
response.end(html) //响应内容
}
}).listen(8888)
console.log('server listening on 8888')
客户端test.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<div>content</div>
<script src="/script.js">
console.log(document.cookie)
</script>
</body>
</html>
10. 数据协商:
客户端请求时会声明希望拿到的数据格式和限制,服务端根据请求头返回不同的数据
客户端请求 | |
---|---|
Accept |
声明想要的数据类型 [主类型]/[子类型] ,如text/html,image/jpg |
Accept-Encoding |
声明进行传输的编码方式 主要是数据压缩的算法,如gzip, deflate, br |
Accept-Language |
声明希望返回信息的语言 如zh-CN,zh;q=0.9(q表权重,0~1) |
Usrer-Agent |
声明浏览器和操作系统的相关信息 |
服务端返回 | |
---|---|
Content-Type |
声明返回的数据格式, 如'X-Content-Type-Options':'nosniff',可阻止浏览器自行猜测返回数据类型而引发的安全问题。 |
Content-Encoding |
声明返回的编码方式,即数据压缩 |
Content-Language |
声明返回的语言 |
表单发送到服务器时的三种编码方式:
enctype(编码方式) | |
---|---|
application/x-www-form-urlencoded | 默认的编码方式。但是在用文本的传输和MP3等大型文件的时候,使用这种编码就显得 效率低下。 |
multipart/form-data | 指定传输数据为二进制类型,比如图片、mp3、文件 |
text/plain | 纯文体的传输。空格转换为 “+” 加号,但不对特殊字符编码 |
将表单的enctype(编码方式)定义为"multipart/form-data":适用于上传文件的方式
<form method="POST" enctype="multipart/form-data">
<input type="text" name="name">
<input type="password" name="psd">
<input type="submit">
</form>
提交表单之后:能够在浏览器请求头中得到如下信息:
Request Header:
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryCoZcoYObU8Ubn5U2
boundary后的字符串用于分割表单中的每一个部分。
------WebKitFormBoundaryCoZcoYObU8Ubn5U2
Content-Disposition: form-data; name="name"
8月5
------WebKitFormBoundaryCoZcoYObU8Ubn5U2
Content-Disposition: form-data; name="psd"
sss
------WebKitFormBoundaryCoZcoYObU8Ubn5U2--
控制台中的connection ID 代表TCP连接的ID,可以用来区分是否用的同一个连接。
connection: close, //没有重复利用TCP连接,每次TCP请求发送完就关闭了
(HTTP请求是在TCP连接上发送的,一个TCP连接可以发送多个HTTP请求)
11. Content Security Policy (内容安全策略)
1. 限制资源获取 2. 报告资源获取越权
CSP 大大增强了网页的安全性。
两种方法启动CSP:
1. 一种是通过 HTTP 头信息的Content-Security-Policy
的字段
Content-Security-Policy: script-src 'self'; object-src 'none';
2. 通过网页的<meta>
标签。
<meta http-equiv="Content-Security-Policy" content="script-src 'self'; object-src 'none'">
( 脚本:只信任当前域名 <object>
标签:不信任任何URL,即不加载任何资源)
只允许通过http,https方式加载src资源:
'Content-Security-Policy': 'default-src http: https:'
只能根据本域名下的JavaScript内容进行加载:
'Content-Security-Policy': 'default-src self'