Go:read一个已经被canceled的http.Request的应答

article/2025/9/17 10:12:22

Go:read一个已经被canceled的http.Request的应答


1.复现

最近发现项目在处理chunk类型的http应答时,出现读数据异常报错,代码示例如下:

server

package mainimport ("bytes""net/http"
)func main() {http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {// 不设置Content-Length,应答默认超过2KB时使用chunk模式writer.Write(bytes.Repeat([]byte{'.'}, 5<<10))})http.ListenAndServe(":9080", nil)
}

client

package mainimport ("context""io/ioutil""log""net/http""strings""time"
)func main() {log.Println("> create cancel-context")ctx, cancel := context.WithCancel(context.TODO())log.Println("> create http request(with context)")req, err := http.NewRequestWithContext(ctx,"POST","http://127.0.0.1:9080",strings.NewReader("{}"),)go func() {time.Sleep(time.Second*1)log.Println("> cancel context")cancel()}()log.Println("> send http request")resp, err := http.DefaultClient.Do(req)log.Println("> recv http response")if err != nil {panic(err)}log.Println("> wait...")time.Sleep(time.Second*2)defer resp.Body.Close()log.Println("> try to read http response")data, err := ioutil.ReadAll(resp.Body)if err != nil {panic(err)}log.Println("> read done: ", len(data))
}

client主要逻辑如下:

1.创建协程,等待1秒后主动cancel http request
2.发送请求,等待应答;
3.收到应答,休眠2秒;
4.休眠结束,读取响应;(此时,http request已经被canceled)

client执行结果:

2021/05/02 16:43:52 > create cancel-context
2021/05/02 16:43:52 > create http request(with context)
2021/05/02 16:43:52 > send http request
2021/05/02 16:43:52 > recv http response
2021/05/02 16:43:52 > wait...
2021/05/02 16:43:53 > cancel context
2021/05/02 16:43:54 > try to read http response
panic: context canceledgoroutine 1 [running]:
main.main()C:/Users/EB/Desktop/chunk/client/main.go:46 +0x4ebProcess finished with exit code 2

client在读取应答时,出现报错:context canceled。

但是,当将server侧应答体长度缩小,例如1KB时:

func main() {http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {// 不设置Content-Length,应答默认超过2KB时使用chunk模式writer.Write(bytes.Repeat([]byte{'.'}, 1<<10))})http.ListenAndServe(":9080", nil)
}

再次多次执行client,均无报错:

2021/05/02 16:47:30 > create cancel-context
2021/05/02 16:47:30 > create http request(with context)
2021/05/02 16:47:30 > send http request
2021/05/02 16:47:30 > recv http response
2021/05/02 16:47:30 > wait...
2021/05/02 16:47:31 > cancel context
2021/05/02 16:47:32 > try to read http response
2021/05/02 16:47:32 > read done:  1024Process finished with exit code 0

是否报错,与应答数据长度有关?

抓包发现,基于chunk的应答,都会出现这个报错,难道是chunk的应答,在请求被取消后不能再读取数据?


2.原因

一般来说,chunk的数据都会比较大(多),初步猜测原因是:

client的HTTP链路的TCP接收缓冲区收到大量的数据(满),当client取消http.Request时对未读完的链路造成影响?

我们打断点到ioutil.ReadAll:

在这里插入图片描述
使用DEBUG模式运行client:

在这里插入图片描述
逐步调试,进入到bytes.Buffer的ReadFrom函数:

在这里插入图片描述
其中ReadFrom的参数就是http response的Body。

在ReadFrom中for循环调用http response Body的Read接口获取应答正文。
其中,http response Body实际类型是:*net/http.bodyEOFSignal

在这里插入图片描述
依次单步调试,最终定位到bufio.go中Reader的Read函数,读取Reader.rd(http.persistConn):

在这里插入图片描述
在这里插入图片描述
最终,在conn.Read读取TCP接收缓冲区数据时报错:

在这里插入图片描述
期间,在bodyEOFSignal.Read调用persitConn中,*net.OpError被转换为context canceled错误。

在这里插入图片描述
在这里插入图片描述
到此为止我们可知:

因request被canceled,导致TCP链路persistConn的状态发生变化断开,此时再Read将错误:use of closed network connection。

但是为什么服务端返回少量数据时不会有问题呢?

当非chunk模式时,将使用io.LimitedReader,此时在读到数据结束时:

我理解的数据结束是:
1.Content-Length时,已经读到指定的Content-Length字节数据;
2.chunk时,已经读到末尾0标识没有更多数据;

将直接返回不再读:

在这里插入图片描述


3.终

当server返回响应数据时,client的TCP接收缓冲区接收,然后解析。

当server返回响应数据比较多时,将client的TCP接收缓冲区堆满,此时需不断循环读取TCP链路上的数据然后存留在应用层。

如果在http request被cancel前,所有的应答数据都已经被收全到client的接收缓冲区中,则read一个已经被canceled的http request的response,没有影响;反之,request已经被canceled,但是还有server的响应在server的发送缓冲区、网络介质中,当client应用层cancel请求时,关闭TCP链路,client尝试从已关闭的链路中读取数据,返回错误。

所以:针对响应很长(包含应答头长度),在长时间的read期间,请求被canceled,将得到此错误。

不要对一个已经被canceled的request的response做read。

如果read resp期间请求被cancel,理解其是TCP链路close引起错误。


http://chatgpt.dhexx.cn/article/QHHuPXLJ.shtml

相关文章

Idea通过git拉取代码的时候出现Update canceled问题

当在IDEA中通过Git更新代码时&#xff0c;拉取失败&#xff0c;报如下错误 12:31 Update failedInvocation failed Server returned invalid Response.java.lang.RuntimeException: Invocation failed Server returned invalid Response.at git4idea.GitAppUtil.sendXmlRequest…

Xmodem operation was canceled by remote peer问题已解决

1.Xmodem operation was canceled by remote peer. 传输的时候就会出现注意的问题 2.使用df -h命令查看内存状况&#xff0c;可以发现root已经满了。 3.进入根目录&#xff0c;ls显示&#xff0c;使用rm命名将其中的文件删除 4.显示&#xff0c;可以看见内存占用变少。 5.…

vue proxy发出的post请求出现超时导致的canceled

0 问题 vue的proxy代理好了之后&#xff0c;get请求没问题&#xff0c;post请求出现canceled&#xff0c;如下图所示&#xff1a; 解决方案 参考 https://github.com/chimurai/http-proxy-middleware/issues/40 devServer: {host: 0.0.0.0,port: 8085,proxy: {/api: {targ…

IDEA中git拉取代码的时出现Update canceled问题

IDEA中git拉取代码的时出现Update canceled问题 当在IDEA中通过Git更新代码时&#xff0c;拉取失败&#xff0c;报如下错误 解决办法&#xff1a; 勾选上以后&#xff0c;点击 OK 后拉取代码&#xff1b; 然后就成功了

解决Canceled future for execute_request message before replies were done

报错&#xff1a;Canceled future for execute_request message before replies were done The Kernel crashed while execut 解决办法&#xff0c;在代码中添加 import os os.environ["KMP_DUPLICATE_LIB_OK"]"TRUE"就完美解决了

ajax请求取消状态,Ajax请求响应状态status为canceled

需求:业务数据提交成功之后,根据表单 ‘项目阶段’ 字段的值发送邮件; 我的实现逻辑是这样的:在业务数据提交成功后的回掉函数中发起发送邮件的请求,然后关闭表单页面。 $.ajax({url: url, type: post, data: {...}, dataType: json, success: function (result) {$.ajax(…

前端axios请求form-data,status显示canceled

前端axios请求form-data,status显示canceled 起因改进&报错发现&解决问题 PS&#xff1a;前排提示本文略微啰嗦&#xff0c;解决办法在 “发现&解决问题” 部分。 起因 最近在网上跟着学习axios在vue中的使用&#xff0c;包含axios的基本数据请求&#xff0c;实例…

Http响应状态Status为canceled

现象 Ajax发送请求 在浏览器的Network发现 响应状态 变为 cnaceled 解决方案 1.表单提交时用的是自定义的button 调用ajax 和form表单中的属性action冲突&#xff0c; form action与绑定于button上的click事件会同时触发。form action将表单内容以get请求追加至当前url上&…

iOS xcode无故build canceled解决办法

iOS xcode无故build canceled解决办法 简单说下原因和处理方法&#xff0c;后面有发现具体原因再补充 原因: 代码更改确定没有影响到xcodeproj&#xff0c;但查看确发现project.pbxproj文件有变化&#xff0c;导致LaunchImage设定有变化 Bulid Setting 中查看影响到的是Launc…

跨域上传,请求状态canceled

上图展示&#xff1a; 解决方法&#xff1a; 仔细看看前端代码&#xff0c;哪个地方设置了timeout

关于浏览器请求队列和超时表现(canceled)

前端在向服务器 API 发送请求时一般会设置一个超时时间&#xff0c;避免超过期望时间的持续等待。 以 Axios 为例&#xff0c;一般会设置 timeout 请求超时选项。 但是浏览器判断超时并不是这么简单。 搭建环境 express axios 搭建 web 服务。 在项目目录下安装依赖&…

http请求响应状态为canceled

http请求响应状态为canceled问题 背景&#xff1a;vue3结合axios , 某个接口请求总是会出现canceled状态&#xff0c;如下图&#xff1a; 分析一下&#xff0c;可能是超时时间设置的问题&#xff0c;原始超时时间显示设置的是3s, 而这个接口响应时间大概有5&#xff5e;10s&a…

探究请求的接口响应status为canceled的原因

最近在和后端小伙伴调试接口的时候&#xff0c;有一个接口是响应status一直是canceled&#xff0c;导致接收不到数据或者说没有响应值返回&#xff0c;后端小伙伴看日志说是请求的数据已经返回了。正常的响应status应该是200&#xff0c;如果响应有误&#xff0c;响应的status应…

ajax表单提交造成请求canceled

表单提交中遇到了请求canceled问题 可以看到我们的button确认click事件是处于form表单内的, 当点击了button之后, form表单也有自己的默认表单提交事件, 这两个事件同时触发, from表单会将表单内容拼接到url后面, 导致页面重新加载, 会导致click事件中断,造成很长的延时, 所以出…

ItemTouchHelper.Callback 详解

ItemTouchHelper是在操作RecyclerView时&#xff0c;堆Item进行长按移动&#xff0c;左右滑动删除效果的一个辅助类&#xff0c;但是我们要考虑&#xff0c;为什么这个辅助类就可以实现移动跟左右删除Item的效果呢&#xff1f;我们的touch事件是怎么作用到RecyclerView的Item上…

JavaScript中的回调函数(callback)

前言 callback&#xff0c;大家都知道是回调函数的意思。如果让你举些callback的例子&#xff0c;我相信你可以举出一堆。但callback的概念你知道吗&#xff1f;你自己在实际应用中能不能合理利用回调实现功能&#xff1f; 我们在平时的学习中容易犯不去深究的病&#xff0c;功…

scrapy.Request callback不执行

上一篇博客介绍了Scrapy Post Request payload数据的问题&#xff0c;本篇记录一下scrapy.Request callback不执行的问题。 1、大家都说的解决方式&#xff0c;虽然没有解决我的问题&#xff0c;但是还是写到这里&#xff0c;算是一个总结&#xff1a; 1.在scrapy.Request方法里…

UVM: callback机制 uvm_callback和uvm_callbacks

callback机制 callback机制提高代码的可重用性&#xff0c;还用于构建异常的测试用例。 广义的callback机制有post_randomize()&#xff0c;pre_body(),post_body(), pre_do(), mid_do()等&#xff0c;它们提供了额外的接口给用户。 原理 以在driver中提供callback函数接口为…

SV和UVM的Callback例程

文章目录 前言一、SystemVerilog Callback例程二、UVM Callback例程总结 前言 记录SV和UVM的两个callback的例程&#xff0c;方便以后用的时候进行查阅。 一、SystemVerilog Callback例程 二、UVM Callback例程 总结 记录SV和UVM的callback例程。

ajax中的callback,ajax callback是什么

ajax的callback是什么? callback是ajax中的回调函数,是在服务器对一个请求对象作出响应时调用的函数。浏览器会在某个时刻”回调”这个函数,根据5种返回状态的情况处理ajax请求结果。 callback回调函数怎么理解? 想弄明白回调函数,首先的清楚地明白函数的规则。在javascri…