How can I test HTTPS endpoints in go?

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

How can I test HTTPS endpoints in go?

问题

所以我正在尝试使用httptest.TLSServer来模拟对https请求的响应,但是发出请求的http.Client告诉我服务器提供了无效的http响应。我猜这是因为没有实际的SSL证书。有没有办法在模拟https请求的同时让客户端忽略tls呢?

相关的测试代码如下:

func TestFacebookLogin(t *testing.T) {
    db := glob.db
    server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        resp := `
        {
          "id": "123",
          "email": "blah",
          "first_name": "Ben",
          "last_name": "Botwin"
        }
        `

        w.Write([]byte(resp))
    }))
    defer server.Close()

    transport := &http.Transport{
        Proxy: func(r *http.Request) (*url.URL, error) {
            return url.Parse(server.URL)
        },
    }

    svc := userService{db: db, client: &http.Client{Transport: transport}}

    _, err := svc.FacebookLogin("asdf")

    if err != nil {
        t.Errorf("Error found: %s", err)
    }

}

我尝试发起的请求如下:

url := "https://<Some facebook api endpoint>"
req, err := http.NewRequest("GET", url, nil)
resp, err := client.Do(req)
if err != nil {
    return nil, err
}
defer resp.Body.Close()
英文:

So I'm trying to mock out a response to an https request using httptest.TLSServer but the http.Client making the request keeps telling me that the server is giving an invalid http response. I bet this is because there's no actual SSL cert. Is there any way I can get the client to ignore tls while still mocking out an https request?

The test code in question

func TestFacebookLogin(t *testing.T) {
	db := glob.db
	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		resp := `
		{
		  &quot;id&quot;: &quot;123&quot;,
		  &quot;email&quot;: &quot;blah&quot;,
		  &quot;first_name&quot;: &quot;Ben&quot;,
		  &quot;last_name&quot;: &quot;Botwin&quot;
		}
		`

		w.Write([]byte(resp))
	}))
	defer server.Close()

	transport := &amp;http.Transport{
		Proxy: func(r *http.Request) (*url.URL, error) {
			return url.Parse(server.URL)
		},
	}

	svc := userService{db: db, client: &amp;http.Client{Transport: transport}}

	_, err := svc.FacebookLogin(&quot;asdf&quot;)

	if err != nil {
		t.Errorf(&quot;Error found: %s&quot;, err)
	}

}

And the request that I'm trying to make:

url := &quot;https://&lt;Some facebook api endpoint&gt;&quot;
req, err := http.NewRequest(&quot;GET&quot;, url, nil)
resp, err := client.Do(req)
if err != nil {
	return nil, err
}
defer resp.Body.Close()

答案1

得分: 6

测试服务器使用自签名证书。有两种方法可以避免错误。第一种方法是使用配置了test server的证书的HTTP客户端:

certs := x509.NewCertPool()
for _, c := range server.TLS.Certificates {
    roots, err := x509.ParseCertificates(c.Certificate[len(c.Certificate)-1])
    if err != nil {
        log.Fatalf("error parsing server's root cert: %v", err)
    }
    for _, root := range roots {
        certs.AddCert(root)
    }
}
client := http.Client{
    Transport: &http.Transport{
        TLSClientConfig: &tls.Config{
            RootCAs: certs,
        },
    },
}

playground示例

更简单的选择是跳过证书验证:

client := http.Client{
    Transport: &http.Transport{
        TLSClientConfig: &tls.Config{
            InsecureSkipVerify: true,
        },
    },
}

playground示例

还有一个额外的问题。客户端期望连接到代理服务器,但实际上它直接连接到了测试服务器。尝试钩住拨号函数而不是代理:

client := http.Client{
    Transport: &http.Transport{
        Dial: func(network, addr string) (net.Conn, error) {
            return net.Dial("tcp", server.URL[strings.LastIndex(server.URL, "/")+1:])
        },
        TLSClientConfig: &tls.Config{
            InsecureSkipVerify: true,
        },
    },
}

playground示例

英文:

The test server uses a self signed certificate. There are two ways to avoid errors. The first is to use an HTTP client configured with the test server's certificates:

certs := x509.NewCertPool()
for _, c := range server.TLS.Certificates {
	roots, err := x509.ParseCertificates(c.Certificate[len(c.Certificate)-1])
	if err != nil {
		log.Fatalf(&quot;error parsing server&#39;s root cert: %v&quot;, err)
	}
	for _, root := range roots {
		certs.AddCert(root)
	}
}
client := http.Client{
	Transport: &amp;http.Transport{
		TLSClientConfig: &amp;tls.Config{
			RootCAs: certs,
		},
	},
}

playground example

An easier option is to skip cert verification:

client := http.Client{
	Transport: &amp;http.Transport{
		TLSClientConfig: &amp;tls.Config{
			InsecureSkipVerify: true,
		},
	},
}

playground example

There's an additional issue. The client expects to connect to a proxy server, but the it's actually connecting to the test server directly. Try hooking the dial function instead of the proxy:

client := http.Client{
	Transport: &amp;http.Transport{
        Dial: func(network, addr string) (net.Conn, error) {
            return net.Dial(&quot;tcp&quot;, server.URL[strings.LastIndex(server.URL, &quot;/&quot;)+1:])
        },
		TLSClientConfig: &amp;tls.Config{
			InsecureSkipVerify: true,
		},
	},
}

playground example

答案2

得分: 0

你只需要使用测试服务器提供的HTTP客户端:

httpClient := server.Client()
英文:

You just need to use the http client provided by the test server:

httpClient := server.Client()

huangapple
  • 本文由 发表于 2016年4月1日 01:55:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/36340396.html
匿名

发表评论

匿名网友

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

确定