英文:
How can I add a 10 second timeout with tls.Dial ? (There is no tls.DialTimeout to correspond to net.DialTimeout)
问题
在使用Go语言的tls.Dial
时,添加超时的最佳方法是使用context
或Dialer
来实现超时功能。以下是几种可能的方法:
- 使用
tls.DialWithDialer
函数,该函数接受一个net.Dialer
参数,可以配置超时:
func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*Conn, error)
你可以创建一个net.Dialer
并设置超时时间,然后将其传递给tls.DialWithDialer
函数。
- 使用
tls.DialContext
函数,该函数接受一个context.Context
参数,可以在上下文中设置超时:
func (d *Dialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error)
你可以使用context.WithTimeout
函数创建一个带有超时的上下文,并将其传递给tls.DialContext
函数。
- 你还可以使用
net.DialTimeout
函数建立初始连接,然后升级连接并继续进行TLS握手。这种方法需要手动处理TLS握手过程,可能会更复杂一些。
希望这些信息对你有帮助!
英文:
What is the best way to add a timeout when using tls.Dial
in Go?
I see the net
package has net.DialTimeout
, but unfortunately, the tls
package doesn't have a corresponding function.
I presume I should be using a context
or Dialer
to implement a timeout, but I'm not an expert in Go and I can't find any good examples.
(1) I found tls.DialWithDialer
, but I'm not sure how to create a net.Dialer
that is configured with a timeout.
func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*Conn, error)
(2) I also found tls.DialContext
, but I'm not sure how to use that to implement a timeout.
func (d *Dialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error)
(3) I presume it might be possible to establish an initial connection using net.DialTimeout
and then upgrade the connection and continue with the TLS handshake, but I can't find any examples that show how to do that.
Any help or guidance would be appreciated.
Here is my simple program that connects to a list of servers and prints some info about the certificate chain. When a server is not responding, this program hangs for a long time. All I want to do is time out after 10 seconds.
package main
import (
"bufio"
"crypto/tls"
"fmt"
"os"
)
func main() {
port := "443"
conf := &tls.Config{
InsecureSkipVerify: true,
}
s := bufio.NewScanner(os.Stdin)
for s.Scan() {
host := s.Text()
conn, err := tls.Dial("tcp", host+":"+port, conf)
if err != nil {
fmt.Println("Host:", host, "Dial:", err)
continue
}
defer conn.Close()
certs := conn.ConnectionState().PeerCertificates
for _, cert := range certs {
fmt.Println("Host:", host, "Issuer:", cert.Issuer)
}
}
}
答案1
得分: 4
根据你的问题提到,有几种选项可以使用DialContext
来实现:
package main
import (
"bufio"
"context"
"crypto/tls"
"fmt"
"os"
"time"
)
func main() {
port := "443"
conf := &tls.Config{
InsecureSkipVerify: true,
}
s := bufio.NewScanner(os.Stdin)
for s.Scan() {
host := s.Text()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
d := tls.Dialer{
Config: conf,
}
conn, err := d.DialContext(ctx, "tcp", host+":"+port)
cancel() // 确保始终调用cancel
if err != nil {
fmt.Println("Host:", host, "Dial:", err)
continue
}
// 警告:在循环中使用defer可能不会产生预期的结果
// 连接将保持打开状态,直到函数退出
defer conn.Close()
tlsConn := conn.(*tls.Conn)
certs := tlsConn.ConnectionState().PeerCertificates
for _, cert := range certs {
fmt.Println("Host:", host, "Issuer:", cert.Issuer)
}
}
}
使用上述方法可以相对简单地允许你的代码用户取消请求(接受一个context
并在上述代码中使用context.Background()
)。如果这对你来说不重要,那么使用带有Timeout
的Dialer
更简单:
conn, err := tls.DialWithDialer(&net.Dialer{Timeout: 10 * time.Second}, "tcp", host+":"+port, conf)
if err != nil {
fmt.Println("Host:", host, "Dial:", err)
continue
}
certs := conn.ConnectionState().PeerCertificates
for _, cert := range certs {
fmt.Println("Host:", host, "Issuer:", cert.Issuer)
}
conn.Close()
英文:
As you mention in your question there are a few options; using DialContext
is a common technique:
package main
import (
"bufio"
"context"
"crypto/tls"
"fmt"
"os"
"time"
)
func main() {
port := "443"
conf := &tls.Config{
InsecureSkipVerify: true,
}
s := bufio.NewScanner(os.Stdin)
for s.Scan() {
host := s.Text()
ctx, cancel := context.WithTimeout(context.Background(), 10 * time.Second)
d := tls.Dialer{
Config: conf,
}
conn, err := d.DialContext(ctx,"tcp", host+":"+port)
cancel() // Ensure cancel is always called
if err != nil {
fmt.Println("Host:", host, "Dial:", err)
continue
}
// warning: using defer in a loop may not have the expected result
// the connection will remain open until the function exists
defer conn.Close()
tlsConn := conn.(*tls.Conn)
certs := tlsConn.ConnectionState().PeerCertificates
for _, cert := range certs {
fmt.Println("Host:", host, "Issuer:", cert.Issuer)
}
}
}
Using the above approach makes it relatively simple to allow users of your code to cancel the request (accept a context
and use it where the above has context.Background()
). If this is not important to you then using a Dialer
with Timeout
is simpler:
conn, err := tls.DialWithDialer(&net.Dialer{Timeout: 10 * time.Second}, "tcp", host+":"+port, conf)
if err != nil {
fmt.Println("Host:", host, "Dial:", err)
continue
}
certs := conn.ConnectionState().PeerCertificates
for _, cert := range certs {
fmt.Println("Host:", host, "Issuer:", cert.Issuer)
}
conn.Close()
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论