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

2023-10-30 13:15

本文主要是介绍Django 尝试SSE报错 AssertionError: Hop-by-hop headers not allowed 的分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

情况描述

近期计划测试一下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专题

这篇关于Django 尝试SSE报错 AssertionError: Hop-by-hop headers not allowed 的分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/308166

相关文章

Go标准库常见错误分析和解决办法

《Go标准库常见错误分析和解决办法》Go语言的标准库为开发者提供了丰富且高效的工具,涵盖了从网络编程到文件操作等各个方面,然而,标准库虽好,使用不当却可能适得其反,正所谓工欲善其事,必先利其器,本文将... 目录1. 使用了错误的time.Duration2. time.After导致的内存泄漏3. jsO

Spring Boot 3.4.3 基于 Spring WebFlux 实现 SSE 功能(代码示例)

《SpringBoot3.4.3基于SpringWebFlux实现SSE功能(代码示例)》SpringBoot3.4.3结合SpringWebFlux实现SSE功能,为实时数据推送提供... 目录1. SSE 简介1.1 什么是 SSE?1.2 SSE 的优点1.3 适用场景2. Spring WebFlu

Spring事务中@Transactional注解不生效的原因分析与解决

《Spring事务中@Transactional注解不生效的原因分析与解决》在Spring框架中,@Transactional注解是管理数据库事务的核心方式,本文将深入分析事务自调用的底层原理,解释为... 目录1. 引言2. 事务自调用问题重现2.1 示例代码2.2 问题现象3. 为什么事务自调用会失效3

SpringBoot启动报错的11个高频问题排查与解决终极指南

《SpringBoot启动报错的11个高频问题排查与解决终极指南》这篇文章主要为大家详细介绍了SpringBoot启动报错的11个高频问题的排查与解决,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一... 目录1. 依赖冲突:NoSuchMethodError 的终极解法2. Bean注入失败:No qu

找不到Anaconda prompt终端的原因分析及解决方案

《找不到Anacondaprompt终端的原因分析及解决方案》因为anaconda还没有初始化,在安装anaconda的过程中,有一行是否要添加anaconda到菜单目录中,由于没有勾选,导致没有菜... 目录问题原因问http://www.chinasem.cn题解决安装了 Anaconda 却找不到 An

Spring定时任务只执行一次的原因分析与解决方案

《Spring定时任务只执行一次的原因分析与解决方案》在使用Spring的@Scheduled定时任务时,你是否遇到过任务只执行一次,后续不再触发的情况?这种情况可能由多种原因导致,如未启用调度、线程... 目录1. 问题背景2. Spring定时任务的基本用法3. 为什么定时任务只执行一次?3.1 未启用

springboot报错Invalid bound statement (not found)的解决

《springboot报错Invalidboundstatement(notfound)的解决》本文主要介绍了springboot报错Invalidboundstatement(not... 目录一. 问题描述二.解决问题三. 添加配置项 四.其他的解决方案4.1 Mapper 接口与 XML 文件不匹配

Django序列化中SerializerMethodField的使用详解

《Django序列化中SerializerMethodField的使用详解》:本文主要介绍Django序列化中SerializerMethodField的使用,具有很好的参考价值,希望对大家有所帮... 目录SerializerMethodField的基本概念使用SerializerMethodField的

java常见报错及解决方案总结

《java常见报错及解决方案总结》:本文主要介绍Java编程中常见错误类型及示例,包括语法错误、空指针异常、数组下标越界、类型转换异常、文件未找到异常、除以零异常、非法线程操作异常、方法未定义异常... 目录1. 语法错误 (Syntax Errors)示例 1:解决方案:2. 空指针异常 (NullPoi

SpringBoot项目启动报错"找不到或无法加载主类"的解决方法

《SpringBoot项目启动报错找不到或无法加载主类的解决方法》在使用IntelliJIDEA开发基于SpringBoot框架的Java程序时,可能会出现找不到或无法加载主类com.example.... 目录一、问题描述二、排查过程三、解决方案一、问题描述在使用 IntelliJ IDEA 开发基于