Go闭包死锁

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

Go closure deadlock

问题

尝试在Go测试中模拟HTTP响应。如果我使用以下命令运行它,代码片段将永远不会终止:

  1. package auth_test
  2. import (
  3. "testing"
  4. "net/http/httptest"
  5. "net/http"
  6. )
  7. func TestAuthorization(t *testing.T) {
  8. t.Log("当网关返回401时应返回401")
  9. {
  10. url := oneOffUrlWithResponseCode(http.StatusUnauthorized)
  11. request, _ := http.NewRequest("GET", url, nil)
  12. response, _ := http.DefaultClient.Do(request)
  13. if response.StatusCode != http.StatusUnauthorized {
  14. t.Fatalf("响应应为401(未授权)")
  15. }
  16. }
  17. }
  18. func oneOffUrlWithResponseCode(responseCode int) string {
  19. var server *httptest.Server
  20. server = httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
  21. defer server.Close()
  22. response.WriteHeader(responseCode)
  23. }))
  24. return server.URL
  25. }

然而,如果我注释掉这一行:

  1. defer server.Close()

一切都正常工作。

理想情况下,我不想在oneOffUrlWithResponseCode函数之外"泄漏" *httptest.Server,并且显然在第一次请求之后关闭它。

为什么它永远不会终止?我做错了什么?正确的做法是什么?

英文:

Trying to mock http response in Go test. The code snippet below never terminates if I run it with

> go test example.com/auth/...

  1. package auth_test
  2. import (
  3. "testing"
  4. "net/http/httptest"
  5. "net/http"
  6. )
  7. func TestAuthorization(t *testing.T) {
  8. t.Log("Should return 401 when Gateway returns 401")
  9. {
  10. url := oneOffUrlWithResponseCode(http.StatusUnauthorized)
  11. request, _ := http.NewRequest("GET", url, nil)
  12. response, _ := http.DefaultClient.Do(request)
  13. if response.StatusCode != http.StatusUnauthorized {
  14. t.Fatalf("Response should be 401 (Unauthorized)")
  15. }
  16. }
  17. }
  18. func oneOffUrlWithResponseCode(responseCode int) string {
  19. var server *httptest.Server
  20. server = httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
  21. defer server.Close()
  22. response.WriteHeader(responseCode)
  23. }))
  24. return server.URL
  25. }

However, if I comment out this line

> defer server.Close()

everything works fine.

Ideally, I do not want to "leak" *httptest.Server outside of oneOffUrlWithResponseCode function and obviously close it after first request.

Why it never terminates? What am I doing wrong? What is the right thing to do?

答案1

得分: 4

程序不会因为死锁而终止(与闭包无关)。你不能在处理程序内部调用Close,因为内部的Close会等待所有处理程序完成。

修复它的最简单方法是将httptest.Server“泄漏”到oneOffUrlWithResponseCode之外:

  1. func TestAuthorization(t *testing.T) {
  2. ...
  3. server := oneOffUrlWithResponseCode(http.StatusUnauthorized)
  4. defer server.Close()
  5. request, _ := http.NewRequest("GET", server.URL, nil)
  6. ...
  7. }
  8. func oneOffUrlWithResponseCode(responseCode int) *httptest.Server {
  9. return httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
  10. response.WriteHeader(responseCode)
  11. }))
  12. }
英文:

The program doesn't terminate because of a deadlock (and it has nothing to do with closures). You cannot call Close inside a handler because internally Close waits for all handlers to finish.

The easiest way to fix it is to "leak" httptest.Server outside of oneOffUrlWithResponseCode:

  1. func TestAuthorization(t *testing.T) {
  2. ...
  3. server := oneOffUrlWithResponseCode(http.StatusUnauthorized)
  4. defer server.Close()
  5. request, _ := http.NewRequest("GET", server.URL, nil)
  6. ...
  7. }
  8. func oneOffUrlWithResponseCode(responseCode int) *httptest.Server {
  9. return httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
  10. response.WriteHeader(responseCode)
  11. }))
  12. }

huangapple
  • 本文由 发表于 2015年10月12日 09:10:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/33071854.html
匿名

发表评论

匿名网友

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

确定