当前位置:网站首页>使用curl指令发起websocket请求
使用curl指令发起websocket请求
2022-08-10 02:24:00 【dotNET跨平台】
昨日的文章没指出websocket请求协商切换的精髓,删除重发。
前文相关:
• .NET WebSockets 核心原理初体验[1]
• SignalR 从开发到生产部署避坑指南[2]
tag:浏览器--->nginx--> server
其中提到nginx默认不会为客户端转发Upgrade、Connection标头[3], 因为为了让被代理的后端服务器知道客户端要升级协议,故要在nginx上显式转发标头:
# 以下为 /realtime/路径请求添加 Connection、Upgrade标头
location /realtime/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}事情本该就就这么简单, 但devops总会有各种奇怪的姿势。
小动作引起的头脑风暴
但是运维在给nginx配置的时候,给/根路径配置了webcoket协议升级标头。
按照字面理解,导致所有的客户端转发请求都在要求切换到websocket协议,但是除了/realtime路径, 服务器其他http路径并没有做websocket协议的逻辑,那其他http请求是不是都该报错了。

是实际看,所有的请求(websocket、http)都没有报错,都按照指定预期返回。
刨一下
利用asp.netcore默认脚手架项目:
已知http://localhost:5000/WeatherForecast是http请求,返回一大坨json数据;
在WeatherForecast添加断言日志:

模拟ops的错配效果,我们给这个请求添加websocket协议升级标头。
第一次:curl 'http://localhost:5000/WeatherForecast' -H 'Upgrade: websocket' -H 'Connection: Upgrade' --verbose =====> 200ok、大坨json数据。
日志记录:
该请求是不是webcocket请求:False,headers:[Accept, */*], [Connection, Upgrade], [Host, localhost:5000], [User-Agent, curl/7.79.1], [Upgrade, websocket]以上说明,服务端并不认为是websocket请求,但是按照http业务处理返回了200ok+大坨json数据,这演示了ops虽然错配,但对于常规的http请求没造成影响。
那服务端到底是怎么认定websocket请求?
从服务端认定websocket请求的源码[4]看

依次判断;
• HttpMethod: GET
• Sec-WebSocket-Version标头==13
• Connection标头==Upgrade
• Upgrade标头==websocket
• 有效的Sec-WebSocket-Key标头
这样我们就明白了,虽然websocket协议基于http,添加了httpConnection、Upgrade协商标头,但是浏览器实际会给我们带上Sec-WebSocket-Key[5]、Sec-WebSocket-Version等标头,以向服务器证明这是一个有效的websocket握手。
于是我们可以使用 curl 'http://localhost:5000/WeatherForecast' -H 'Upgrade: websocket' -H 'Connection: Upgrade' -H 'Sec-WebSocket-Version: 13' -H 'Sec-webSocket-Key: eeZn6lg/rOu8QbKwltqHDA==' --verbose 仿造客户端websocket请求。=====> 200ok、 大坨json数据
这里提示:浏览器websocket会自动帮我们加上这些标头;在前端编程 let ss = new WebSocket("ws://localhost:5000/WeatherForecast") 也会自动帮助我们带上这些标头。

日志记录:
该请求是不是webcocket请求:True,headers:[Accept, */*], [Connection, Upgrade], [Host, localhost:5000], [User-Agent, curl/7.79.1], [Upgrade, websocket], [Sec-WebSocket-Version, 13], [Sec-WebSocket-Key, eeZn6lg/rOu8QbKwltqHDA==]服务器认可这是websocket请求,服务端处理逻辑没改,故按原http代码逻辑返回200ok和JSON数据。
真正要让服务端按照websocket姿势, 要使用HttpContext.WebSockets.AcceptWebSocketAsync()告知客户端开始切换协议,返回101响应码[6],并在原tcp上发起全双工通信。
协商切换
以上行为完美诠释了协商切换 的理念。
客户端仅携带 Connection、Upgrade标头,被服务端当成一般的http标头。
但是若带上sec-websocket-verison、sec-websocket-key,则被认为是有效的websocket请求,既然是“协商”, 服务器依旧可以拒绝切换,用原http协议返回。
就坡下驴,将脚手架项目改成一个同时支持http和websocket协议的Action吧:
// 服务端对于websocket请求,使用服务端单向推送
[HttpGet(Name = "GetWeatherForecast")]
public async Task Get()
{
_logger.LogInformation("该请求是不是webcocket请求:"+ HttpContext.WebSockets.IsWebSocketRequest+",headers:{0}", Request.Headers);
if (HttpContext.WebSockets.IsWebSocketRequest)
{
var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
Enumerable.Range(1, 5).ToList().ForEach(async x =>
{
var wf = new WeatherForecast
{
Date = DateTime.Now.AddDays(x),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
};
var ss = JsonConvert.SerializeObject(wf);
var serverMsg = Encoding.UTF8.GetBytes(ss);
// 下面参数3:true 表示结束此次消息
await webSocket.SendAsync(new ArraySegment<byte>(serverMsg, 0, serverMsg.Length), WebSocketMessageType.Text, true, CancellationToken.None);
});
}
else
{
var arr = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
await Response.WriteAsJsonAsync(arr);
}
}curl 'http://localhost:5000/WeatherForecast' -H 'Upgrade: websocket' -H 'Connection: Upgrade' -H 'Sec-WebSocket-Version: 13' -H 'Sec-webSocket-Key: eeZn6lg/rOu8QbKwltqHDA==' --verbose

curl 'http://localhost:5000/WeatherForecast' -H 'Upgrade: websocket' -H 'Connection: Upgrade' --verbose

回顾
1. 本文记录了nginx在转发websocket请求时要添加的配置。
2. 虽然ops错配了nginx for websocket url:nginx为http请求转发了
Connection、Upgrade标头, 但是服务器并不认可这是websocket升级协议,仅认为是携带了特殊标头的http请求,走原来的http业务处理逻辑是没有问题的。3. 在curl指令添加了sec-websocket-version、sec-websocket-key 标头,从客户端仿造了真实的websocket请求。
再次提示:浏览器对websocket协商切换会自动帮我们加上这些标头;在前端编程 let ss = new WebSocket("ws://localhost:5000/WeatherForecast") 也会自动帮助我们带上这些标头。
4. websocket是基于http协议为蓝本,是一个协商切换协议的行为,既然是协商, 服务端是可以拒绝协议切换,依旧采用原http协议来处理。
本文内容和制图均为原创,文章永久更新地址请参阅左下角博客园原文;
如公号内容对您有所帮助,请一键三连,方便的话置一个星标 ~。。~。
引用链接
[1] .NET WebSockets 核心原理初体验: https://www.cnblogs.com/JulianHuang/p/14681331.html[2] SignalR 从开发到生产部署避坑指南: https://www.cnblogs.com/JulianHuang/p/15434137.html[3] nginx默认不会为客户端转发Upgrade、Connection标头: https://nginx.org/en/docs/http/websocket.html[4] 服务端认定websocket请求的源码: https://github.com/dotnet/aspnetcore/blob/main/src/Middleware/WebSockets/src/WebSocketMiddleware.cs#L219[5] Sec-WebSocket-Key: https://www.rfc-editor.org/rfc/rfc6455#section-11.3.1[6] 开始切换协议,返回101响应码: https://github.com/dotnet/aspnetcore/blob/main/src/Middleware/WebSockets/src/WebSocketMiddleware.cs#L134
边栏推荐
- P1564 Worship
- QT modal dialog and non-modal dialog learning
- 2022 Top Net Cup Quals Reverse Partial writeup
- 官宣出自己的博客啦
- 网页挖矿溯源?浏览器浏览历史查看工具Browsinghistoryview
- Example 044: Matrix Addition
- [Semantic Segmentation] 2022-HRViT CVPR
- 浅写一个下拉刷新组件
- Redis - String|Hash|List|Set|Zset数据类型的基本操作和使用场景
- Completion of the flag set in 2022
猜你喜欢

MySQL: What MySQL optimizations have you done?

Introduction and application of quantitative trading strategies

Deep Learning (5) CNN Convolutional Neural Network

nodejs 时钟案例(fs模块),重复使用fs.writeFile方法,旧内容会被覆盖

web crawler error

【Image Classification】2022-CycleMLP ICLR

The Evolutionary History of the "Double Gun" Trojan Horse Virus

兴业数金一面

【图像分类】2022-ConvMixer ICLR

【Kali安全渗透测试实践教程】第6章 密码攻击
随机推荐
官宣出自己的博客啦
2022.8.8 Exam area link (district) questions
HACKTHEBOX——Bank
Web mining traceability?Browser browsing history viewing tool Browsinghistoryview
数据治理(五):元数据管理
【二叉树-中等】508. 出现次数最多的子树元素和
C - The Battle of Chibi (dp加树状数组前缀和优化)
2022.8.8考试游记总结
2022/08/09 学习笔记 (day26) IO流
【二叉树-中等】687. 最长同值路径
HRnet
2020.11.22考试哥德巴赫猜想题解
《GB39707-2020》PDF下载
P1564 Worship
HRnet
2022.8.8 Exam questions for photographer Lao Ma (photographer)
Go语言JSON文件的读写操作
nodejs 时钟案例(fs模块),重复使用fs.writeFile方法,旧内容会被覆盖
剑指offer专项突击版第25天
Example 046: Breaking the Cycle