Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set SetKeepAlivePeriod on a *tls.Conn

Tags:

tcp

ssl

go

I want to increase the keep alive period of my TCP connection for both HTTP and HTTPS requests.

For HTTP requests this can be done like this:

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())
}

For HTTPS requests this cannot be done via server.ConnState because the net.Conn that will be passed inside the function is a *tls.Conn. This connection does not expose a function like SetKeepAlivePeriod or gives access to the underlying *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"))
}

How can I set the keep alive period for tls connections?

like image 511
jmattheis Avatar asked Sep 09 '25 14:09

jmattheis


1 Answers

There are (at least) two ways to do it:

Use a net.ListenConfig:

The net.ListenConfig object has a KeepAlive time.Duration field. When non-zero, this will be used to set the keep-alive on accepted connections (eg: for TCP on posix).

You can pass the listener to 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"))

As mentioned, accepted TCP connections will automatically have keep-alive enabled and the period set to the specified value.

Use a tls.Config callback:

You can access the net.Conn underlying the tls.Conn by setting the tls.Config GetConfigForClient or GetCertificate callback.

It does not matter which one you're using as long as you return nil to make the TLS code fall back to the default behavior. The important part is to get access to the tls.ClientHelloInfo which has a .Conn field pointing to the underlying connection. This will the 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")
like image 52
Marc Avatar answered Sep 12 '25 04:09

Marc