【问题标题】:How to simulate 504 timeout error for http request inside a test in Go?如何在 Go 中的测试中模拟 http 请求的 504 超时错误?
【发布时间】:2018-12-21 12:23:30
【问题描述】:

我正在尝试向 Go 中的库添加 timeout 选项,并编写了以下测试来模拟该行为。

func TestClientTimeout(t *testing.T) {
    backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        d := map[string]interface{}{
            "id":    "12",
            "scope": "test-scope",
        }

        time.Sleep(100 * time.Millisecond)
        e := json.NewEncoder(w)
        err := e.Encode(&d)
        if err != nil {
            t.Error(err)
        }
        w.WriteHeader(http.StatusOK)
    }))

    url := backend.URL
    ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
    defer cancel()
    req, err := http.NewRequest("GET", url, nil)
    if err != nil {
        t.Error("Request error", err)
    }

    resp, err := http.DefaultClient.Do(req.WithContext(ctx))
    if err != nil {
        t.Error("Response error", err)
    }

    defer resp.Body.Close()

    t.Log(">>>>>>>Response is: ", resp)
}

但我总是遇到错误,而不是http.StatusGatewayTimeout

=== RUN TestClientTimeout

--- 失败:TestClientTimeout (0.05s)

client_test.go:37: Timestamp before req 2018-07-13 09:10:14.936898 +0200 CEST m=+0.002048937
client_test.go:40: Response error Get http://127.0.0.1:49597: context deadline exceeded

恐慌:运行时错误:无效的内存地址或 nil 指针取消引用 [已恢复]

恐慌:运行时错误:无效的内存地址或 nil 指针取消引用

如何修复此测试,以返回带有http.StatusGatewayTimeout(504) 状态码的响应?

【问题讨论】:

    标签: http go timeout


    【解决方案1】:

    您收到错误context deadline exceeded 的原因是因为请求客户端的context.Context 上的超时时间短于服务器端处理程序中的超时时间。这意味着context.Context 以及客户端http.DefaultClient 在写入任何响应之前就放弃了。

    panic: runtime error: invalid memory address... 是因为您推迟关闭响应正文,但如果客户端返回错误,则响应为nil

    这里响应为nil,如果错误为非nil,则将t.Error改为t.Fatal

    resp, err := http.DefaultClient.Do(req.WithContext(ctx))
    if err != nil {
        // this should be t.Fatal, or don't do the body close if there's an error
        t.Error("Response error", err)
    }
    
    defer resp.Body.Close()
    

    找到问题的真正根源,http.StatusGatewayTimeout 是服务器端超时,这意味着创建的任何超时都必须在服务器端。客户端http.DefaultClient 永远不会创建自己的服务器错误响应代码。

    要创建服务器端超时,您可以将处理程序函数包装在 http.TimeoutHandler 中:

    handlerFunc := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        d := map[string]interface{}{
            "id":    "12",
            "scope": "test-scope",
        }
    
        time.Sleep(100 * time.Millisecond)
        e := json.NewEncoder(w)
        err := e.Encode(&d)
        if err != nil {
            t.Error(err)
        }
        w.WriteHeader(http.StatusOK)
    })
    
    backend := httptest.NewServer(http.TimeoutHandler(handlerFunc, 20*time.Millisecond, "server timeout"))
    

    但是,这将创建一个503 - Service Unavailable 错误响应代码。

    了解 504 的重要一点是,这是一个“网关”或“代理”错误响应代码。这意味着这段代码不太可能来自实际处理请求的服务器。这段代码在负载均衡器和代理中更常见。

    504 网关超时 服务器在充当网关或代理时,没有收到来自它需要访问以完成请求的上游服务器的及时响应。

    您已经使用httptest.NewServer(...) 在测试方法中模拟了http.Server,因此您可以在处理函数中手动返回http.StatusGatewayTimeout 响应状态。

    handlerFunc := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusGatewayTimeout)
    })
    
    backend := httptest.NewServer(handlerFunc)
    

    【讨论】:

    • 当我尝试你的建议时。我收到消息:`HTTP response not ok: 504`。但它无法通过检查超时的代码:if e,ok := err.(net.Error); ok && e.Timeout() { // Should got there. But it doesn't. }。有什么建议吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-19
    • 2012-07-09
    • 1970-01-01
    • 1970-01-01
    • 2015-08-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多