随机的REFUSED_STREAM导致了一个POST多部分表单请求。

huangapple go评论132阅读模式
英文:

Random REFUSED_STREAM making a POST multipart form request

问题

这是使用golang将apk文件(几MB)上传到appetize.io的代码:

  1. func uploadToAppetize(file multipart.File, branchName string, displayName string) (result *AppetizeRes, ok bool) {
  2. file.Seek(0, 0)
  3. url, _ := getUrl()
  4. var buffer bytes.Buffer
  5. writer := multipart.NewWriter(&buffer)
  6. fileName := displayName + "/" + branchName
  7. part, err := writer.CreateFormFile("file", fileName)
  8. if err != nil {
  9. fmt.Fprintf(os.Stderr, "Error creating form file %v: %v\n", fileName, err)
  10. return nil, false
  11. }
  12. size, err := io.Copy(part, file)
  13. if err != nil {
  14. fmt.Fprintf(os.Stderr, "Error copying apk file data: %v\n", err)
  15. return nil, false
  16. }
  17. fmt.Fprintf(os.Stdout, "Copied %v bytes for uploading to appetize...\n", size)
  18. writer.Close()
  19. response, err := http.Post(url, writer.FormDataContentType(), &buffer) // Random error on this line
  20. if err != nil || response == nil {
  21. fmt.Fprintf(os.Stderr, "Error occurred uploading apk data to appetize.io: %v %v\n", err, response)
  22. return nil, false
  23. }
  24. defer response.Body.Close()
  25. if response.StatusCode != http.StatusOK {
  26. return nil, false
  27. }
  28. var appetizeRes AppetizeRes
  29. if err := json.NewDecoder(response.Body).Decode(&appetizeRes); err != nil {
  30. return nil, false
  31. }
  32. return &appetizeRes, true
  33. }

然而,我在http.Post(...)这一行遇到了一个随机错误。它返回一个空的响应和一个错误。错误信息是"stream error: stream ID 1; REFUSED_STREAM"。这个错误会随机发生,但在go程序首次发出请求后肯定会发生。

这是go的版本:

  1. go version go1.8.1 darwin/amd64

如果没有出错,这是服务器的响应头:
随机的REFUSED_STREAM导致了一个POST多部分表单请求。

我还在另一台运行go 1.6.*的Mac上运行了这个程序,我不记得在那台Mac上遇到过这个问题。

有什么想法吗?

英文:

This is the code to upload an apk file (several MB) to appetize.io using golang:

  1. func uploadToAppetize(file multipart.File, branchName string, displayName string) (result *AppetizeRes, ok bool) {
  2. file.Seek(0, 0)
  3. url, _ := getUrl()
  4. var buffer bytes.Buffer
  5. writer := multipart.NewWriter(&buffer)
  6. fileName := displayName + "/" + branchName
  7. part, err := writer.CreateFormFile("file", fileName)
  8. if err != nil {
  9. fmt.Fprintf(os.Stderr, "Error creating form file %v: %v\n", fileName, err)
  10. return nil, false
  11. }
  12. size, err := io.Copy(part, file)
  13. if err != nil {
  14. fmt.Fprintf(os.Stderr, "Error copying apk file data: %v\n", err)
  15. return nil, false
  16. }
  17. fmt.Fprintf(os.Stdout, "Copied %v bytes for uploading to appetize...\n", size)
  18. writer.Close()
  19. response, err := http.Post(url, writer.FormDataContentType(), &buffer) // Random error on this line
  20. if err != nil || response == nil {
  21. fmt.Fprintf(os.Stderr, "Error occurred uploading apk data to appetize.io: %v %v\n", err, response)
  22. return nil, false
  23. }
  24. defer response.Body.Close()
  25. if response.StatusCode != http.StatusOK {
  26. return nil, false
  27. }
  28. var appetizeRes AppetizeRes
  29. if err := json.NewDecoder(response.Body).Decode(&appetizeRes); err != nil {
  30. return nil, false
  31. }
  32. return &appetizeRes, true
  33. }

However I am receiving an random error on the line http.Post(...). It returns a nil response and an error. The error is "stream error: stream ID 1; REFUSED_STREAM". It happens randomly but will surely happen the first time the go program make the request after launching.

This is the go version:

  1. go version go1.8.1 darwin/amd64

This is the response header from the server if it doesn't fail:
随机的REFUSED_STREAM导致了一个POST多部分表单请求。

I also run this program on another mac running go 1.6.*, I didn't remember I ever run into this issue on that mac.

Any idea what's going on?

答案1

得分: 2

golang标准库net/http中存在一个bug,无法正确处理REFUSED_STREAM的http/2错误。可能发生的情况如下:

  1. golang客户端打开一个TCP连接到HTTP/2的www服务器,并将连接中的最大HTTP流数量设置为1000,并立即开始上传。

  2. HTTP/2的www服务器告诉golang客户端只使用给定数量的流,但是golang客户端已经开始了超过该数量的流。

  3. HTTP/2的www服务器对此做出反应,重置多余的流。

  4. golang的net/http代码中缺乏对重试的正确支持,导致流在被服务器重置后失败,最终上传也失败。

关于这个问题在GitHub上有一个开放的问题:x/net/http2: retry requests rejected with REFUSED_STREAM - golang/go/issues/20985

**这个问题已经在主分支中关闭,修复将包含在未来的Golang版本1.10中。

1: https://github.com/golang/go/issues/20985/ "x/net/http2: retry requests rejected with REFUSED_STREAM"

英文:

There is a bug in the golang standard library net/http that fails to handle the REFUSED_STREAM http/2 error properly. This is what is likely going on:

  1. golang client opens a TCP connection to the HTTP/2 www server, sets the maximum number of HTTP streams in the connection to 1000 and starts uploading immediately.

  2. The HTTP/2 www server tells the golang client to only use a given number of streams, but the golang client has already started more than that amount of streams.

  3. The HTTP/2 www server reacts to this by resetting the excess streams.

  4. The lack of proper support for a retry in the golang net/http code causes the stream to fail after being reset by the server, and eventually the upload fails as well.

There is a ticket open about this issue on github: x/net/http2: retry requests rejected with REFUSED_STREAM - golang/go/issues/20985

**This issue has been closed in the master branch, the fix will be included in future Golang version 1.10

1: https://github.com/golang/go/issues/20985/ "x/net/http2: retry requests rejected with REFUSED_STREAM"

huangapple
  • 本文由 发表于 2017年5月21日 01:50:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/44089111.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定