如何在 *tls.Conn 上设置 SetKeepAlivePeriod

jma*_*eis 4 ssl tcp go

我想增加 HTTP 和 HTTPS 请求的 TCP 连接的保持活动时间。

对于 HTTP 请求,可以这样完成:

package main

import (
    "fmt"
    "io"
    "log"
    "net"
    "net/http"
    "time"
)

func main() {
    server := &http.Server{Addr: ":8080", Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        io.WriteString(w, "Hello, World!")
    })}

    server.ConnState = func(conn net.Conn, state http.ConnState) {
        if state == http.StateNew {
            if err := conn.(*net.TCPConn).SetKeepAlivePeriod(1000 * time.Second); err != nil {
                fmt.Println("Could not set keep alive period", err)
            } else {
                fmt.Println("update keep alive period")
            }
        }
    }

    log.Fatal(server.ListenAndServe())
}
Run Code Online (Sandbox Code Playgroud)

对于 HTTPS 请求,这不能通过完成server.ConnState,因为net.Conn将在函数内部传递的 是*tls.Conn. 此连接不会公开类似的功能SetKeepAlivePeriod,也不会提供对底层*net.TCPConn.

func main() {
    server := &http.Server{Addr: ":8080", Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        io.WriteString(w, "Hello, World!")
    })}

    server.ConnState = func(conn net.Conn, state http.ConnState) {
        if state == http.StateNew {
            tlsConn := conn.(*tls.Conn)
            // how to set SetKeepAlivePeriod
        }
    }

    log.Fatal(server.ListenAndServeTLS("../example.crt", "../example.key"))
}
Run Code Online (Sandbox Code Playgroud)

如何设置 tls 连接的保持活动时间?

Mar*_*arc 5

有(至少)两种方法可以做到这一点:

使用net.ListenConfig

net.ListenConfig对象有一个KeepAlive time.Duration字段。当非零时,这将用于设置已接受连接的保持活动状态(例如:对于 posix 上的 TCP)。

您可以将侦听器传递给ServeTLS

server := &http.Server{...}

lc := net.ListenConfig{KeepAlive: 1000 * time.Second}
ln, err := lc.Listen(context.Background(), "tcp", ":8080")
if err != nil {
  panic(err)
}
defer ln.Close()

log.Fatal(server.ServeTLS(ln, "../example.crt", "../example.key"))
Run Code Online (Sandbox Code Playgroud)

如前所述,接受的 TCP 连接将自动启用保持活动状态,并将周期设置为指定值。

使用tls.Config回调:

您可以通过设置tls.Config或回调来访问net.Conn底层。tls.Conn GetConfigForClientGetCertificate

只要您返回nil以使 TLS 代码恢复为默认行为,您使用哪一种并不重要。重要的部分是访问 tls.ClientHelloInfo 它有一个.Conn指向底层连接的字段。这将把net.TCPConn.

setTCPKeepAlive := func(clientHello *tls.ClientHelloInfo) (*tls.Config, error) {
  // Check that the underlying connection really is TCP.
  if tcpConn, ok := clientHello.Conn.(*net.TCPConn); ok {
    if err := tcpConn.SetKeepAlivePeriod(1000 * time.Second); err != nil {
      fmt.Println("Could not set keep alive period", err)
    } else {
      fmt.Println("update keep alive period")
    }
  } else {
    fmt.Println("TLS over non-TCP connection")
  }

  // Make sure to return nil, nil to let the caller fall back on the default behavior.
  return nil, nil
}

tlsConfig := &tls.Config{
    ...
    GetConfigForClient: setTCPKeepAlive,
    ...
}

server := &http.Server{
    Addr:      ":8080",
    TLSConfig: tlsConfig,
}

server.ListenAndServeTLS("../example.crt", "../example.key")
Run Code Online (Sandbox Code Playgroud)