超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。所有的WWW文件都必须遵守这个标准。设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法。

  • 请求的一方叫客户端,响应的一方叫服务器端

通过请求和响应达成通信 HTTP是一种不保存状态的协议

http

URI和URL

URI

URI(Uniform Resource Identifier)是统一资源标识符,在某个规则下能把这个资源独一无二标示出来,比如人的身份证号

  • Uniform 不用根据上下文来识别资源指定的访问方式

Resource 可以标识的任何东西 Identifier 表示可标识的对象

URL

统一资源定位符,表示资源的地点,URL时使用浏览器访问WEB页面时需要输入的网页地址

  • Uniform 不用根据上下文来识别资源指定的访问方式

Resource 可以标识的任何东西 Location 定位

URL的格式

报文

请求报文

  • 请求行 方法 协议 url

请求头 请求体

响应报文

  • 响应行 协议 状态 状态码短语

响应头 响应体

首部

通用首部字段

首部字段名 说明
Cache-Control 控制缓存行为
Connection 链接的管理
Date 报文日期
Pragma 报文指令
Trailer 报文尾部的首部
Trasfer-Encoding 指定报文主体的传输编码方式
Upgrade 升级为其他协议
Via 代理服务器信息
Warning 错误通知

请求首部字段

首部字段名 说明
Accept 用户代理可处理的媒体类型
Accept-Charset 优先的字符集
Accept-Encoding 优先的编码
Accept-Langulage 优先的语言
Authorization Web认证信息
Expect 期待服务器的特定行为
From 用户的电子邮箱地址
Host 请求资源所在的服务器
If-Match 比较实体标记
If-Modified-Since 比较资源的更新时间
If-None-Match 比较实体标记
If-Range 资源未更新时发送实体Byte的范围请求
If-Unmodified-Since 比较资源的更新时间(和If-Modified-Since相反)
Max-Forwards 最大传输跳数
Proxy-Authorization 代理服务器需要客户端认证
Range 实体字节范围请求
Referer 请求中的URI的原始获取方
TE 传输编码的优先级
User-Agent HTTP客户端程序的信息

响应首部字段

首部字段名 说明
Accept-Ranges 是否接受字节范围
Age 资源的创建时间
ETag 资源的匹配信息
Location 客户端重定向至指定的URI
Proxy-Authenticate 代理服务器对客户端的认证信息
Retry-After 再次发送请求的时机
Server 服务器的信息
Vary 代理服务器缓存的管理信息
www-Authenticate 服务器对客户端的认证

实体首部字段

首部字段名 说明
Allow 资源可支持的HTTP方法
Content-Encoding 实体的编码方式
Content-Language 实体的自然语言
Content-Length 实体的内容大小(字节为单位)
Content-Location 替代对应资源的URI
Content-MD5 实体的报文摘要
Content-Range 实体的位置范围
Content-Type 实体主体的媒体类型
Expires 实体过期时间
Last-Modified 资源的最后修改时间

状态码

状态码负责表示客户端请求的返回结果、标记服务器端是否正常、通知出现的错误

类别 原因短语
1XX Informational(信息性状态码)
2XX Success(成功状态码)
3XX Redirection(重定向)
4XX Client Error(客户端错误状态码)
5XX Server Error(服务器错误状态吗)

2XX 成功

  • 200(OK) 客户端发过来的数据被正常处理
  • 204(Not Content) 正常响应,没有实体
  • 206(Partial Content) 范围请求,返回部分数据,响应报文中由Content-Range指定实体内容

3XX 重定向

  • 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)) 临时重定向,不该改变请求方法-

5XX 服务器端错误

  • 500(Internal Server Error) 服务器故障
  • 503(Service Unavailable) 服务器处于超负载或正在停机维护

http应用

范围请求

  • client Range:bytes=0-5
  • server
    Accept-Ranges: bytes
    Content-Range: bytes 0-5/705
    Status Code 206
服务端(1.server.js)
let http = require('http');
let path = require('path');
let util = require('util');
let p = path.resolve(__dirname, '1.range.txt');
// let fs = require('fs');
// let stat = utils.promisify(fs.stat);
// let readFile = utils.promisify(fs.readFile);//mz
let fs = require('mz/fs');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}).pipe(res);}else {//读取文件 把它响应给客户端fs.createReadStream(p).pipe(res);}
}
let server = http.createServer(listener);
server.listen(3000, () => {console.log('server start ' + 3000)
})
复制代码
客户端(1.client.js)
let http = require('http');
let path = require('path');
let config = {host: 'localhost',port: 3000,
}
let fs = require('fs');
let ws = fs.createWriteStream(path.join(__dirname, '/download.txt'))
let start = 0;
function download () {config.headers = {'Range': `bytes=${start}-${start + 4}`}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 buf = Buffer.concat(buffers);ws.write(buf);setTimeout(function() {if (start < total) {download();}}, 1000);})}); //必须调用 end,否则请求不会发送client.end();
}download();
运行
node 1.client.js
复制代码

图片防盗链

  • 从一个网站跳转,或者网页引用到某个资源文件时,HTTP请求中带有Referer表示来源网页的URL
  • 检查请求头中的Referer来判断来源网页的域名
  • 如果来源域名不在白名单内,则返回错误提示
  • 用浏览器直接访问图片地址是没有referer的
服务端
/*
- 请求头 host referer
- 映射 (C:\Windows\System32\drivers\etc\host)
- 目录结构 public/1.jpg、2.jpg、index.html
*/let http = require('http');
let path = require('path');
let url = require('url');
let {exists} = require('mz/fs');
let fs = require('fs');
let static = path.resolve(__dirname, 'public');
let server = http.createServer(async (req, res) => {let { pathname } = url.parse(req.url, true);let p = path.join(static, pathname);let flag = await exists(p); //如果路径存在返回trueconsole.log(flag);if (flag) {let refer = req.headers['referer'] || req.headers['referered'];if (refer) {refer = url.parse(refer).hostname;let host = req.headers['host'].split(':')[0];console.log(refer, host);if (refer != host) {fs.createReadStream(path.join(static, '2.jpg')).pipe(res);}else {fs.createReadStream(path.join(static, '1.jpg')).pipe(res); }}else {fs.createReadStream(p).pipe(res);}}else {res.statusCode = 404;res.end('Not Found');}
});
server.listen(3000)
复制代码
index.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><img src="http://zf1.cn:3000/1.jpg" alt=""><br/><img src="http://zf2.cn:3000/1.jpg" alt="">
</body>
</html>
复制代码

正向代理

  • 访问原来无法访问的资源,如google
  • 可以做缓存,加速访问资源-
  • 对客户端访问授权,上网进行认证
  • 代理可以记录用户访问记录(上网行为管理),对外隐藏用户信息
server.js
let http = require('http');
let httpProxy = require('http-proxy');
let proxy = httpProxy.createProxyServer();http.createServer((req, res) => {proxy.web(req, res, {target: 'http://localhost:4000'})proxy.on('error', (err) => {console.log(err);})
}).listen(5000);
复制代码
4000.js
let http = require('http');
http.createServer((req, res) => {res.end('hello world')
}).listen(4000)
复制代码

反向代理

客户端是无感知代理的存在的,反向代理对外都是透明的,访问者者并不知道自己访问的是一个代理。因为客户端不需要任何配置就可以访问。

  • 保证内网的安全,可以使用反向代理提供WAF功能,阻止web攻击
  • 负载均衡,通过反向代理服务器来优化网站的负载
server.js
let http = require('http');
let httpProxy = require('http-proxy');let proxy = httpProxy.createProxyServer();
let ops = {'myword1.com': 'http://localhost:4000','myword2.com': 'http://localhost:4001'
}http.createServer((req, res) => {//res.end('hello')let host = req.headers['host'];proxy.web(req, res, {target: ops[host]})proxy.on('error', (err) => {console.log(err);})
}).listen(80);
复制代码
4000.js
let http = require('http');
http.createServer((req, res) => {res.end('myword1')
}).listen(4000)
复制代码
4001.js
let http = require('http');
http.createServer((req, res) => {res.end('myword2')
}).listen(4001)
复制代码

多语言

通过Accept-Language检测浏览器的语言

请求头格式 Accept-Language: Accept-Language:zh-CN,zh;q=0.9
响应头格式 Content-Language:zh-CN

let languageConfig = {'zh-CN': '你好','en': 'hello','Fr': 'Bonjour'
}
let defaultLanguage = 'en';
//获取accept-language 根据语言进行权重排序
//zh-CN,zh;q=0.8 => [{name: 'zh-cn', q: 0.8}]
let http = require('http');
http.createServer((req, res) => {let language = req.headers['accept-language'];if (language) {let lans = language.split(',');lans = lans.map((lan) => {let [name, q] = lan.split(';')q = q ? parseFloat(q.split("=")[1]) : 1;return {name: name,q}});for (let i = 0; i < lans.length; i++) {let content = languageConfig[lans[i].name];if (content){return res.end(content);}}res.end(languageConfig[defaultLanguage]);}else {res.end(languageConfig[defaultLanguage]);}}).listen(4000)
复制代码

缓存

  • 减少了冗余的数据传输
  • 减少了服务器的负担,提高了网站的性能
  • 加快了客户端加载网页的速度

缓存分类

  • 强制缓存,不需要再和服务器发生交互,
  • 对比缓存,都需要与服务端发生交互

强制缓存

  • Cache-Control: max-age=10 相对时间
  • Exprires: GMTString 绝对时间

Cache-Control
private 客户端可以缓存
public 客户端和代理服务器都可以缓存
max-age=60 缓存内容将在60秒后失效
no-cache 需要使用对比缓存验证数据,强制向源服务器再次验证
no-store 所有内容都不会缓存,强制缓存和对比缓存都不会触发

let util = require('util');
let fs = require('fs');
let stat = util.promisify(fs.stat);
let path = require('path');
let url = require('url');
let mime = require('mime');
let p = path.resolve(__dirname);
http.createServer(async (req, res) => {let {pathname} = url.parse(req.url, true);let realPath = path.join(p, pathname);console.log(realPath);try {await stat(realPath);   res.setHeader('Content-Type', mime.getType(realPath) + ';chartset=utf8');//console.log(realPath);res.setHeader('Cache-Control', 'max-age=10');res.setHeader('Expries', new Date(Date.now() + 10 * 1000).toGMTString());fs.createReadStream(realPath).pipe(res);}catch(e) {res.statusCode = 404;res.end('Not Found')}
}).listen(80);
复制代码

对比缓存

1、响应头 last-modified 请求头 if-modified-since

let http = require('http');
let util = require('util');
let fs = require('fs');
let stat = util.promisify(fs.stat);
let path = require('path');
let url = require('url');
let mime = require('mime');
let p = path.resolve(__dirname);
//第一次访问,发送respones头Last-Modified
//第二次访问时,带个request头if-modified-since
http.createServer(async (req, res) => {let {pathname} = url.parse(req.url, true);let realPath = path.join(p, pathname);console.log(realPath);try {let statObj = await stat(realPath);   res.setHeader('Content-Type', 'no-cache');let since = req.headers['if-modified-since'];if (since === statObj.ctime.toGMTString()) {res.statusCode = 304;res.end();}else {res.setHeader('Last-Modified', statObj.ctime.toGMTString())fs.createReadStream(realPath).pipe(res);   }}catch(e) {res.statusCode = 404;res.end('Not Found');}
}).listen(80);
复制代码

存在的问题:

某些服务器不能精确得到文件的最后修改时间,无法通过最后修改时间来判断文件是否更新了。 某些文件的修改非常频繁,在秒以下的时间内进行修改. Last-Modified只能精确到秒。 一些文件的最后修改时间改变了,但是内容并未改变。 我们不希望客户端认为这个文件修改了。 如果同样的一个文件位于多个CDN服务器上的时候内容虽然一样,修改时间不一样。

2、 响应头 Etag 请求头 if-none-match

let http = require('http');
let util = require('util');
let fs = require('fs');
let stat = util.promisify(fs.stat);
let path = require('path');
let url = require('url');
let mime = require('mime');
let p = path.resolve(__dirname);
//第一次访问,发送respones头etag
//第二次访问时,带个request头if-none-match
http.createServer(async (req, res) => {let {pathname} = url.parse(req.url, true);let realPath = path.join(p, pathname);console.log(realPath);try {let statObj = await stat(realPath);   res.setHeader('Content-Type', 'no-cache');let match = req.headers['if-none-match'];if (match ===  statObj.size.toString()) {res.statusCode = 304;res.end();}else {res.setHeader('Etag', statObj.size.toString())fs.createReadStream(realPath).pipe(res);   }}catch(e) {res.statusCode = 404;res.end('Not Found');}
}).listen(80);
复制代码

压缩

  • 请求头 Accept-Encodeing
  • 响应头 Content-Encoding
let http = require('http');
let path = require('path');
let zlib = require('zlib');
let url = require('url');
let fs = require('fs');
let mime = require('mime');let p = path.resolve(__dirname);
http.createServer((req, res) => {let {pathname} = url.parse(req.url, true);let realPath = path.join(p, pathname);let encodeing = req.headers['accept-encoding'];if (encodeing) {res.setHeader('Content-Type', mime.getType(realPath));if (encodeing.match(/\bgzip\b/)) {res.setHeader('Content-Encoding', 'gzip')return fs.createReadStream(realPath).pipe(zlib.createGzip()).pipe(res);}else if (encodeing.match(/\bdeflate\b/)) {res.setHeader('Content-Encoding', 'deflate')return fs.createReadStream(realPath).pipe(zlib.createDeflate()).pipe(res);}else {fs.createReadStream(realPath).pipe(res);}}else {fs.createReadStream(realPath).pipe(res);}
}).listen(4000);
复制代码

压缩之前

压缩之后

转载于:https://juejin.im/post/5b540578e51d451757325529

最新文章

  1. java B2B2C 多租户电子商城系统-Spring Cloud Zipkin
  2. 【Android】解析Json数据
  3. 信息系统项目管理师十大常见问题汇总
  4. DelayQueue源码
  5. js、react对象名和对象属性赋值
  6. 如何将 Mac 恢复还原到以前的日期
  7. oracle工作日执行job,Oracle中执行Job定时执行
  8. cmd 卸载mysql_彻底卸载mysql
  9. 零基础该怎么去学游戏建模?
  10. 华硕路由器里的虚拟服务器在哪里,华硕RT-AC86U路由器怎么设置端口转发服务
  11. 南昌大学计算机导师林振荣,南昌大学各学院研究生导师介绍
  12. 视频分割算法在移动端如何应用
  13. 实现isPrime()函数,参数为整数,要有异常处理,如果是质数返回True,否则返回False
  14. jy-12-SPRINGMYBATIS02——云笔记07-刘苍松
  15. socket 套接字通信研究与讨论
  16. Java GUI项目,一个练手的泡泡龙小游戏
  17. Java_多线程、并发控制、分布式锁、存储结构、消息队列常见问题
  18. ftl模板导出excel_freemarker导出定制excel
  19. MATLAB数学建模(一):MATLAB与数学建模
  20. 专为摩托车点火器中直流升压电路设计的控制芯片MST2218

热门文章

  1. qt与python互联_PYQT5 vscode联合操作qtdesigner的方法
  2. 基于android的家庭财务通 .apk,毕业论文(设计)基于Android的家庭财务管家的设计与实现.doc...
  3. 图例放在图的外面_Origin做双Y轴箱型图(图文讲解)
  4. cleanmymac3.9.6下载_单耳兔o2oapp下载-单耳兔o2o商城官方版下载v10.6 安卓版
  5. 关于智能车竞赛总结 | 山东大学(威海) - 山魂五队
  6. 电路的静态与动态特性
  7. 第十五届全国大学生智能汽车竞赛线上比赛流程规范
  8. Eclipse中Java文件图标由实心J变成空心J的问题
  9. dcba oracle,【转】dcba的文章:Oracle的SET UNUSED COLUMN操作到底做了什么?
  10. android 多个应用,Android中一个应用实现多个图标的几种方式