http http应用
超文本传输协议(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
最新文章
- java B2B2C 多租户电子商城系统-Spring Cloud Zipkin
- 【Android】解析Json数据
- 信息系统项目管理师十大常见问题汇总
- DelayQueue源码
- js、react对象名和对象属性赋值
- 如何将 Mac 恢复还原到以前的日期
- oracle工作日执行job,Oracle中执行Job定时执行
- cmd 卸载mysql_彻底卸载mysql
- 零基础该怎么去学游戏建模?
- 华硕路由器里的虚拟服务器在哪里,华硕RT-AC86U路由器怎么设置端口转发服务
- 南昌大学计算机导师林振荣,南昌大学各学院研究生导师介绍
- 视频分割算法在移动端如何应用
- 实现isPrime()函数,参数为整数,要有异常处理,如果是质数返回True,否则返回False
- jy-12-SPRINGMYBATIS02——云笔记07-刘苍松
- socket 套接字通信研究与讨论
- Java GUI项目,一个练手的泡泡龙小游戏
- Java_多线程、并发控制、分布式锁、存储结构、消息队列常见问题
- ftl模板导出excel_freemarker导出定制excel
- MATLAB数学建模(一):MATLAB与数学建模
- 专为摩托车点火器中直流升压电路设计的控制芯片MST2218
热门文章
- qt与python互联_PYQT5 vscode联合操作qtdesigner的方法
- 基于android的家庭财务通 .apk,毕业论文(设计)基于Android的家庭财务管家的设计与实现.doc...
- 图例放在图的外面_Origin做双Y轴箱型图(图文讲解)
- cleanmymac3.9.6下载_单耳兔o2oapp下载-单耳兔o2o商城官方版下载v10.6 安卓版
- 关于智能车竞赛总结 | 山东大学(威海) - 山魂五队
- 电路的静态与动态特性
- 第十五届全国大学生智能汽车竞赛线上比赛流程规范
- Eclipse中Java文件图标由实心J变成空心J的问题
- dcba oracle,【转】dcba的文章:Oracle的SET UNUSED COLUMN操作到底做了什么?
- android 多个应用,Android中一个应用实现多个图标的几种方式