http的那些事儿
请问你对http了解吗?
- 还比较了解。HTTP是一个应用层协议,由请求和响应构成,是一个标准的客户端服务器模型。HTTP是一个无状态的协议, 同一个客户端的这次请求和上次请求是没有对应关系。HTTP协议永远都是客户端发起请求,服务器回送响应。见下图:
一个网页的加载过程是什么样的?
-
浏览器会因页面上的css/js/image等静态资源会多次发起连接请求,可以把这个过程分成两部分
- html(jsp/php/aspx) 页面加载(假设存在简单的Nginx负载均衡)
- css/js/image等 网页静态资源加载(假设使用CDN)
-
整个过程大致经过了
DNS解析-->负载均衡-->web服务器-->浏览器渲染- DNS解析: 将域名地址解析为对应的ip地址的过程。
- 建立TCP连接
- 发起Http请求,请求网页, 其中, 请求方式的格式为:统一资源标识符(URL)、协议版本号,后边是MIME信息包括请求修饰符、客户机信息和可能的内容。
- 服务器返回请求页面:其格式为一个状态行,包括信息的协议版本号、一个成功或错误的代码,后边是MIME信息包括服务器信息、实体信息和可能的内容。
- 浏览器渲染: 根据页面内容,生成DOM Tree;根据CSS内容,生成CSS Rule Tree; 调用JS执行引擎执行js代码。根据DOM Tree和CSS Rule Tree生成Render Tree(呈现树)。根据Render Tree渲染网页.
-
但是在浏览器解析页面内容的时候,会发现页面引用了其他未加载的image、css文件、js文件等静态内容,因此开始了第二部分的静态资源请求。
常用的请求方法有哪些
- GET/POST/PUT/DELETE/TRACE/OPTION/HEAD/CONNECT/PATCH
常用的Http 请求头字段有哪些?
-
Accept:浏览器端可以接受的MIME类型
- MIME类型:
- application: 某种二进制附件,对于没有subtype的情况,默认是application/octet-stream
- text: 文本,理论上可读,对于没有subtype的情况,默认是text/plain
- image: 图像
- audio: 音频
- video: 视频
- multipart: 多部分文档文件(复合文档文件)
- MIME类型:
-
Accept-Charset 设置接受的字符编码
-
Accept-Encoding 设置接受的编码格式
-
Accept-Language 设置接受的语言
-
Authorization 设置HTTP身份验证的凭证
-
Cache-Control 设置请求响应链上所有的缓存机制必须遵守的指令
-
Content-Length 设置请求体的字节长度
-
Content-MD5 设置基于MD5算法对请求体内容进行Base64二进制编码
-
Content-Type 设置请求体的MIME类型(适用POST和PUT请求)
-
Cookie 设置服务器使用Set-Cookie发送的http cookie
-
Date 设置消息发送的日期和时间
-
Expect 标识客户端需要的特殊浏览器行为
-
Host 设置服务器域名和TCP端口号,如果使用的是服务请求标准端口号,端口号可以省略
-
If-Match 设置客户端的ETag,当时客户端ETag和服务器生成的ETag一致才执行,适用于更新自从上次更新之后没有改变的资源
-
If-Modified-Since 设置更新时间,从更新时间到服务端接受请求这段时间内如果资源没有改变,允许服务端返回304 Not Modified
-
If-None-Match 设置客户端ETag,如果和服务端接受请求生成的ETage相同,允许服务端返回304 Not Modified
-
If-Range 设置客户端ETag,如果和服务端接受请求生成的ETage相同,返回缺失的实体部分;否则返回整个新的实体
-
Forwarded 披露客户端通过http代理连接web服务的源信息
- Forwarded: for=192.0.2.60;proto=http;by=203.0.113.43
- Forwarded: for=192.0.2.43, for=198.51.100.17
-
If-Unmodified-Since 设置更新时间,只有从更新时间到服务端接受请求这段时间内实体没有改变,服务端才会发送响
-
Max-Forwards 限制代理或网关转发消息的次数
-
Origin 标识跨域资源请求(请求服务端设置Access-Control-Allow-Origin响应字段)
-
Pragma 设置特殊实现字段,可能会对请求响应链有多种影响
-
Proxy-Authorization 为连接代理授权认证信息
-
Range 请求部分实体,设置请求实体的字节数范围,具体可以参见HTTP/1.1中的Byte serving
- Range: bytes=500-999
-
Referer 设置前一个页面的地址,并且前一个页面中的连接指向当前请求,意思就是如果当前请求是在A页面中发送的,那么referer就是A页面的url地址
-
Upgrade 请求服务端升级协议
-
User-Agent 用户代理的字符串值
-
Via 通知服务器代理请求
-
Warning 实体可能会发生的问题的通用警告
常用的Http 响应头字段有哪些?
- Access-Control-Allow-Origin 指定哪些站点可以参与跨站资源共享
- Accept-Patch 指定服务器支持的补丁文档格式,适用于http的patch方法
- Accept-Patch: text/example;charset=utf-8
- Accept-Ranges 服务器通过byte serving支持的部分内容范围类型
- Age 对象在代理缓存中暂存的秒数
- Allow 设置特定资源的有效行为,适用方法不被允许的http 405错误
- Content-Encoding 设置数据使用的编码类型
- Content-Language 为封闭内容设置自然语言或者目标用户语言
- Content-Length 响应体的字节长度
- Content-Location 设置返回数据的另一个位置
- Content-MD5 设置基于MD5算法对响应体内容进行Base64二进制编码
- Content-Range 标识响应体内容属于完整消息体中的那一部分
- Content-Type 设置响应体的MIME类型
- ETag 特定版本资源的标识符,通常是消息摘要
- Expires 设置响应体的过期时间
- Last-Modified 设置请求对象最后一次的修改日期
- Set-Cookie 设置HTTP Cookie
- Server 服务器名称
- Status 设置HTTP响应状态
- Transfer-Encoding 设置传输实体的编码格式,目前支持的格式: chunked, compress, deflate, gzip, identity
- Upgrade 请求客户端升级协议
http的响应状态码
-
1xx Informational(信息性状态码)
-
2XX Success(成功状态码)
- 200(OK 客户端发过来的数据被正常处理
- 204(Not Content 正常响应,没有实体
- 206(Partial Content 范围请求,返回部分数据,响应报文中由Content-Range指定实体内容
-
3xx Redirection(重定向)
- 301(Moved Permanently) 永久重定向
- 302(Found) 临时重定向,规范要求方法名不变,但是都会改变
- 303(See Other) 和302类似,但必须用GET方法
- 304(Not Modified) 状态未改变 配合(If-Match、If-Modified-Since、If-None_Match、If-Range、If-Unmodified-Since)
- 307(Temporary Redirect) 临时重定向,不该改变请求方法
-
4XX Client Error(客户端错误状态码)
- 400(Bad Request) 请求报文语法错误
- 401 (unauthorized) 需要认证
- 403(Forbidden) 服务器拒绝访问对应的资源
- 404(Not Found) 服务器上无法找到资源
-
5xx Server Error(服务器错误状态码)
- 500(Internal Server Error)服务器故障
- 503(Service Unavailable) 服务器处于超负载或正在停机维护
url模块
url.parse(urlStr,[parseQueryString]);
- href 被转换的原URL字符串
- protocal 客户端发出请求时使用的协议
- slashes 在协议与路径之间是否使用了//分隔符
- host URL字符串中的完整地址和端口号
- auth URL字符串中的认证部分
- hostname URL字符串中的完整地址
- port URL字符串中的端口号
- pathname URL字符串的路径,不包含查询字符串
- search 查询字符串,包含?
- path 路径,包含查询字符串
- query 查询字符串,不包含起始字符串?
- hash 散列字符串,包含#
http的应用
1. 范围请求 206
客户端需要发送一个Range:bytes=0-5 服务器 Sccept-Range:bytes Content-Range: bytes 0-5/705
// 在命令行模式范围请求
curl -v --header "Range: bytes=0-5" http://www.baidu.com/img/baidu_jgylogo3.gif
server.js
let http = require('http')
let path = require('path')
let p = path.resolve(__dirname, '1.txt');
let fs = require('mz/fs')
const port = 3000;
async function listener(req, res) {
let range = req.headers['range'];
if (range) {
let [, start, end] = range.match(/(\d*)-(\d*)/);
let statObj = await fs.stat(p);
let total = statObj.size;
start = start ? Number(start) : 0;
end = end ? Number(end) : total - 1;
res.statusCode = 206;
res.setHeader('Accept-Ranges', 'bytes');
res.setHeader('Content-Range', `bytes ${start}-${end}/${total}`);
fs.createReadStream(p, {start, end, encoding: 'utf8'}).pipe(res);
} else {
// 读取文件 并响应给客户端
fs.createReadStream(p).pipe(res)
}
}
let server = http.createServer(listener);
server.listen(port, () => {
console.log(`server start at ${port}`)
})
client.js
// 定时发起请求,并把请求到的结果写入download.txt,实现断点续传的功能
let http = require('http');
let fs = require('fs');
let ws = fs.createWriteStream(__dirname + '/download.txt')
let start = 0;
const unit = 4;
let config = {
host: 'localhost',
port: 3000
}
function download() {
config.headers = {
'Range': `bytes=${start}-${start + unit}`
}
start += 5;
let client = http.request(config, res => {
let total = res.headers['content-range'].split('/')[1];
let buffers = [];
res.on('data', data => {
buffers.push(data);
});
res.on('end', () => {
let result = Buffer.concat(buffers);
ws.write(result)
setTimeout(() => {
console.log('start <= total', start, total)
if (start <= total) {
download();
}
}, 1000);
})
})
client.end(); // 必须调用end 否则不会发送内容
}
download();
2. 防盗链
server.js
/**
* 实现原理
* host: 表示资源的地址,一般是服务器地址,默认端口号80
* refer/referered: 使用资源的端地址
*
* 在服务器端配置资源可访问的白名单 WHITELIST,
* 当host与refer/referered的域名不相同,并且refer/referered的地址在WHITELIST中时,
* 表示可以该端可以访问服务器中的资源,否则不可访问.
* */
let http = require('http');
let fs = require('fs');
let url = require('url');
let path = require('path');
let static = path.resolve(__dirname);
const WHITELIST = ['linda_1.cn'];
let server = http.createServer(async (req, res) => {
let { pathname } = url.parse(req.url);
let p = path.join(static, pathname);
let flag = true;
try{
fs.accessSync(p)
} catch(e) {
flag = false;
}
if (flag) {
let refer = req.headers['referer'] || req.headers['referered'];
if (refer) {
let hostname = req.headers['host'].split(':')[0];
let refername = url.parse(refer).hostname;
if (refername != hostname && !WHITELIST.includes(refername)) {
fs.createReadStream(path.join(static, '/images/girl.png')).pipe(res);
} else {
fs.createReadStream(p).pipe(res);
}
} else {
fs.createReadStream(p).pipe(res);
}
} else{
res.statusCode = 404;
res.end();
}
})
server.listen(3000, () => {
console.log('server is starting on 3000...')
})
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Page Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<img src="http://localhost:3000/images/boy.png"/>
</body>
</html>
mac下配置域名与主机名映射
vim /etc/hosts
127.0.0.1 linda_1.cn
127.0.0.1 linda_2.cn
配置好后,可在浏览器中输入不同的域名,来测试图片的显示情况。