http.ResponseWriter.WriteHeader() 导致死锁

标签 http testing go

我试图在 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)上试了一下,导致挂了,一点 react 都没有。

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

最佳答案

如果稍微修改一下代码:

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 协议(protocol)的,所以我得到一个错误:

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

关于http.ResponseWriter.WriteHeader() 导致死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47923284/

相关文章:

监听数据时出现 HttpClientResponse 错误

php - Angular 应用程序不调用 php

C# - 通过 HTTP 发送文件

ruby-on-rails - assert_select 在测试中不起作用

google-app-engine - 为什么 gcloud 组件更新命令总是显示重新启动命令

mysql - Golang 加入数组接口(interface)

java - Volley : MultipartRequest. getBody:IOException 写入 ByteArrayOutputStream

android - 如何使用android espresso test设置textview的可见性

ruby - 在 Capybara Webkit 测试中将 Ctrl-S 发送到浏览器

go - Viper 在解码时不考虑我的结构中的 yaml 标签