【问题标题】:http.ResponseWriter.WriteHeader() caused deadlockhttp.ResponseWriter.WriteHeader() 导致死锁
【发布时间】:2018-06-04 00:45:56
【问题描述】:

我试图在 golang 中使用 httptest 包。我发现了一些我不明白的东西。这是code

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "net/http/httptest"
)

func main() {
    ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello1"))
    }))
    ts.Close()
    ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello2"))
    }))
    ts.Close()
    ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(100)
        w.Write([]byte("Hello3"))
    }))

    res, err := http.Get(ts.URL)
    if err != nil {
        log.Fatal(err)
    }
    greeting, err := ioutil.ReadAll(res.Body)
    res.Body.Close()
    if err != nil {
        log.Fatal(err)
    }

    ts.Close()
    fmt.Printf("%s", greeting)
}

在这个代码示例中,我多次尝试打开和关闭httptest 服务器。不知何故,它在 The Go Playground 中造成了僵局。我在自己的环境(Go 版本:go1.7.4 darwin/amd64)上尝试过,它导致挂起,根本没有响应。

我的问题是:为什么w.WriteHeader(100) 会导致死锁而w.WriteHeader(200) 不会?是Golang核心库的错误还是我误解了一些用法?谢了!

【问题讨论】:

  • 可能100 Continue 回复代码假设一些操作进一步,因此库正在等待下一步。
  • 你为什么要发送100?你预计会发生什么?
  • Go 似乎没有向客户端发送 100 (StatusContinue) 响应。如果你重新排序它以便你先写 Hello3 那么它最终会发送一个成功的响应(查看play.golang.org/p/gX1ra-xFSA8 - 在操场上不起作用,但它会在你的系统上运行),然后尝试使用它。是的,顺便说一句,你想做什么?
  • 这不能回答问题。你期望会发生什么?您使用 100 的原因是什么?只是“看看会发生什么”吗?如果是这样,现在你知道会发生什么了。
  • @Ravi 感谢您的示例!我想知道是否是因为 100 导致服务器等待来自客户端的以下请求。而我只是在玩httptest 包!

标签: http testing go


【解决方案1】:

如果你稍微修改代码:

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "net/http/httptest"
)

func main() {
    ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello1"))
    }))
    ts.Close()
    ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello2"))
    }))
    ts.Close()
    ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(100)
        w.Write([]byte("Hello3"))
    }))

    fmt.Println("before get")   ///// . <----
    res, err := http.Get(ts.URL)
    fmt.Println("after get")    ///// . <----
    if err != nil {
        log.Fatal(err)
    }
    greeting, err := ioutil.ReadAll(res.Body)
    res.Body.Close()
    if err != nil {
        log.Fatal(err)
    }

    ts.Close()
    fmt.Printf("%s", greeting)
}

并运行它 - 你只会看到第一行。

这意味着 Go http 客户端需要您提供更多数据。所以就挂了

    res, err := http.Get(ts.URL)

并且无法访问下面的ts.Close()

接下来 - 让我们修改一个测试,所以它会关闭一个连接,这样会释放一个客户端等待锁:

ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(100)
    w.Write([]byte("Hello3"))
    hj, _ := w.(http.Hijacker)
    conn, _, _ := hj.Hijack()
    conn.Close()
}))

我明确地关闭了连接,但这样你就可以得到检查字符串和测试完成。试试看。

当然这是违反 HTTP 协议的,所以我收到错误:

Get http://127.0.0.1:54243: net/http: HTTP/1.x transport connection broken: unexpected EOF

【讨论】:

  • 好吧,我想知道我们是否可以将其称为 Go 永远不会像 RFC (tools.ietf.org/html/rfc7231#section-6.2.1) 那样将继续状态传达给客户端的错误。可能会假设没有太多应用程序使用它?
猜你喜欢
  • 2018-04-20
  • 2012-09-20
  • 2016-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-03
相关资源
最近更新 更多