Django 尝试SSE报错 AssertionError: Hop-by-hop headers not allowed 的分析

news/2024/10/18 3:28:48/

情况描述

近期计划测试一下django对日志打印的支持,一般都是用websocket的方式,想测试一下SSE (Server-sent events)的服务端推送,发现过程中存在报错:

Traceback (most recent call last):File "D:\Software\Anaconda3\lib\wsgiref\handlers.py", line 137, in runself.result = application(self.environ, self.start_response)File "D:\IDE Projects\Music\venv\lib\site-packages\django\contrib\staticfiles\handlers.py", line 76, in __call__return self.application(environ, start_response)File "D:\IDE Projects\Music\venv\lib\site-packages\django\core\handlers\wsgi.py", line 142, in __call__start_response(status, response_headers)File "D:\Software\Anaconda3\lib\wsgiref\handlers.py", line 249, in start_responseassert not is_hop_by_hop(name),"Hop-by-hop headers not allowed"
AssertionError: Hop-by-hop headers not allowed

部分代码

    response = HttpResponse(content_type='text/event-stream')response['Cache-Control'] = 'no-cache'# AssertionError: Hop-by-hop headers not allowedresponse['Connection'] = 'keep-alive'response['Transfer-Encoding'] = 'chunked'

初步分析

1)报错大致是说Hop-by-hop headers not allowed ,说是HTTP1.0 不支持这个头部

HTTP 首部字段将定义成缓存代理和非缓存代理的行为,分成 2 种类型。

端到端首部(End-to-end Header)
分在此类别中的首部会转发给请求 / 响应对应的最终接收目标,且必须保存在由缓存生成的响应中,另外规 定它必须被转发。

逐跳首部(Hop-by-hop Header)
分在此类别中的首部只对单次转发有效,会因通过缓存或代理而不再转发。HTTP/1.1 和之后版本中,如果要使用 hop-by-hop 首部,需提供 Connection 首部字段。

2)依据堆栈信息,找到对应的判断函数 is_hop_by_hop(name) ,只要头部存在如下的集合元素,就会被判定为 hop_by_hop

# wsgiref\handlers.pyif __debug__:for name, val in headers:name = self._convert_string_type(name, "Header name")val = self._convert_string_type(val, "Header value")assert not is_hop_by_hop(name),"Hop-by-hop headers not allowed"# handlers\wsgi.py
_hoppish = {'connection':1, 'keep-alive':1, 'proxy-authenticate':1,'proxy-authorization':1, 'te':1, 'trailers':1, 'transfer-encoding':1,'upgrade':1
}.__contains__def is_hop_by_hop(header_name):"""Return true if 'header_name' is an HTTP/1.1 "Hop-by-Hop" header"""return _hoppish(header_name.lower())

3)于是乎,我把Connection 和 Transfer-Encoding 注释掉,就正常了

附带SSE 在django的使用办法

(一)前端 HTML 部分代码

<p id="info">测试</p><script>var need_close = false;var eventSource = new EventSource('/sse');info_elm = document.getElementById("info");// 开启连接的监听// 解决重复请求(默认会无限重连) 也可以由后端内容决定是否关闭eventSource.onopen = function(){if (need_close){eventSource.close();}need_close  = true;};// 收到事件的监听eventSource.onmessage = function(event) {// 处理接收到的事件数据info_elm.innerText = info_elm.innerText + event.data + '\n';};</script>

(二)django

### views.py
from django.http import StreamingHttpResponsedef sse(request):def event_stream():# 测试读取当前文件with open('appname/views.py', encoding='utf-8') as file:for line in file:# time.sleep(1)yield f'data: {line}\n\n'return StreamingHttpResponse(event_stream(), content_type='text/event-stream', headers={'Cache-Control':'no-cache'})def hello(request):return render(request, 'sse.html' )### urls.py
urlpatterns = [path('sse', views.sse),path('hello', views.hello),
]

(三) 结果

http://127.0.0.1:8000/sse

能看到只有一个请求,内容是逐步刷新出来的
在这里插入图片描述

http://127.0.0.1:8000/hello

能看到有三个请求,第二个是SSE请求,结束后,EventSource自动拉起了第三个请求,由于在onopen 配置了变量控制,所以后期不会有新的推送进来导致数据重复载入前端
在这里插入图片描述

(四) 不可行方案
如下方案主要是 response.flush() 并不能把数据刷新到客户端,所以这个与直接返回结果没太大区别,也可能是我目前使用方式不对或理解没到位。

官方文档: response.flush() 这个方法使一个 HttpResponse 实例成为一个类似文件的对象

def sse(request):response = HttpResponse( content_type='text/event-stream')response['Cache-Control'] = 'no-cache'for i in range(5):response.write(f'data:{i} \n\n')response.write('id: {i} ')response.write(f'message: {datetime.datetime.now()}')response.write('event: 发送数据')# 数据发送到客户端response.flush()time.sleep(1)return response

总结

1、发现网上很多给的代码都跑不起来,要么不和预期,终究还是得找官方
2、尝试看看源码,可能能找到问题原因还有一些没接触过的写法
3、可以用while True ,然后将数据读取写入到循环内部,然后通过控制时间间隔来减少推送

参考链接:
HTTP 首部字段详细介绍
Python __debug__内置变量的用法
SSE介绍
W3shcool SSE
关于SSE关闭的问题
EventSource专题


http://www.ppmy.cn/news/1185659.html

相关文章

Flask后端开发(二) - 功能实现和项目总结

目录 1. 功能1:修改文件参数值1.1. 获取网页端传参1.2. 读取文件1.2.1. 一般文件读取方式1.2.2. 特殊文件 —— mlx文件1.2.3. 特殊文件 —— .xlx文件1.3. 查找数据修改位置,替换数据2. 功能2:读取结果数据2.1. 实时数据展示如何存储相关数据?2.2. 读取相关数据,整理、打…

550MW发电机变压器组继电保护的整定计算及仿真

摘要 电力系统继电保护设计是根据系统接线图及要求选择保护方式&#xff0c;进行整定计算&#xff0c;电力系统继电保护的设计与配置是否合理直接影响到电力系统的安全运行。如果设计与配置不当&#xff0c;保护将不能正确工作&#xff0c;会扩大事故停电范围&#xff0c;造成…

MySQL数据库干货_07—— MySQL中的约束

MySQL中的约束 本专栏从本篇开始正式介绍MySQL中的约束内容&#xff0c;这是关系型数据库的一个重点&#xff0c;在接下来的几篇博文中我会详细介绍每种约束&#xff0c;包括概念&#xff0c;创建方式&#xff0c;应用场景等等&#xff0c;希望小伙伴们关注&#xff01;约束概…

深度强化学习用于博弈类游戏-基础测试与说明【1】

深度强化学习用于博弈类游戏-基础【1】 1. 强化学习方法2. 强化学习在LOL中的应⽤2.1 环境搭建2.2 游戏特征元素提取1)小地图人物位置:2)人物血量等信息3)在整个图像上寻找小兵、防御塔的位置4)自编码器提取3. 策略梯度算法简介参考资料1. 强化学习方法 伴随着人工智能的潮起…

springboot--基本特性--自定义 Banner

SpringApplication的使用 前言效果1.1 自定义banner1.2 自定义SpringApplication配置文件优先级高于程序化调整的优先级启动自定义banner关闭自定义banner 1.3 FluentBuilder API 前言 修改启动时候的修改banner 效果 1.1 自定义banner banner制定官网链接 在配置文件中设置…

【数据结构】 队列详解!庖丁解牛般细致讲解!

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; 数据结构解析 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言&#x1f324;️队列的概念剖析☁️什么是队列☁️队列的特性☁️队列的图解 &#x1…

Power BI 傻瓜入门 17. 共享和Power BI工作区

本章内容包括&#xff1a; 设置与Power BI服务的共享和协作使用监控和性能工具加快业务运营通过查看数据联机排除数据故障 在经历了跨数据源的整个数据生命周期、构建可视化、了解DAX和发布报告之后&#xff0c;作为power BI的高级用户&#xff0c;您的下一步是与业务中的所有…

C语言KR圣经笔记 2.8自增和自减 2.9位运算 2.10赋值

2.8 自增和自减操作符 C提供了两个不同寻常的操作符&#xff0c;用于对变量进行自增和自减。自增操作符对操作数加上1&#xff0c;而自减操作符 -- 对操作数减去1。我们已经频繁使用 对变量进行自增&#xff0c;如&#xff1a; if (c \n)nl; 不寻常之处在于 和 -- 既能用作…