Skip to content

如果只获取Response(), 当WithContext在SetTimeout前面时,会导致context cancel错误 #383

@aeroplane

Description

@aeroplane

Bug Report

在SSE等流式响应模式中(返回时间很长),需要获取Response

如下代码,会报错

response, err := gout.New().
                WithContext(ctx).
		SetTimeout(100 * time.Second).
		
		SetMethod("POST").
		SetURL(url).
		SetJSON(request).
		Response()
if err != nil {
    panic(err.Error())
}
defer response.Body.Close()

// 读取Response.Body
for {
  _, err :=  response.Body.Read(....)
}

WithContext(ctx)SetTimeout上面时,读取Response.Body(一段时间后)会出现context cancel的异常

产生BUG原因:

func (s *Setting) SetTimeout(d time.Duration) {
	s.Index++
	s.TimeoutIndex = s.Index
	s.Timeout = d
}

// retry模块需要context.Context,所以这里也返回context.Context
func (r *Req) GetContext() context.Context {
	if r.Timeout > 0 && r.TimeoutIndex > r.ctxIndex {
		r.c, r.cancel = context.WithTimeout(context.Background(), r.Timeout)  // 当`WithContext(ctx)`在`SetTimeout`上面时,会执行此行
	}
	return r.c
}

// Response 获取原始http.Response数据结构
func (r *Req) Response() (rsp *http.Response, err error) {
	_, rsp, err = r.getReqAndRsp()
	defer r.Reset()   // 此处会调用r.cancel 
	return
}

因为r.cancel被调用,r.c会直接退出

解决办法

将setTimeout放在withContext上面

SetTimeout(100 * time.Second).
 WithContext(ctx).
		

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions