Как установить SetKeepAlivePeriod на * tls.Conn

#go #ssl #tcp

#Вперед #ssl #tcp

Вопрос:

Я хочу увеличить период ожидания моего TCP-соединения как для HTTP, так и для HTTPS-запросов.

Для HTTP-запросов это можно сделать следующим образом:

 package main

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

func main() {
    server := amp;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())
}
  

Для запросов HTTPS это невозможно сделать через, server.ConnState потому что net.Conn то, что будет передано внутри функции, является *tls.Conn . Это соединение не предоставляет такую функцию, как SetKeepAlivePeriod или предоставляет доступ к базовой *net.TCPConn .

 func main() {
    server := amp;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"))
}
  

Как я могу установить период ожидания для соединений tls?

Ответ №1:

Есть (как минимум) два способа сделать это:

Используйте сеть.ListenConfig:

У net.ListenConfig объекта есть KeepAlive time.Duration поле. Если значение не равно нулю, оно будет использоваться для настройки режима сохранения при принятых соединениях (например: для TCP в posix).

Вы можете передать прослушиватель ServeTLS :

 server := amp;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"))
  

Как уже упоминалось, для принятых TCP-соединений автоматически будет включена поддержка и периоду будет присвоено указанное значение.

Используйте обратный вызов tls.Config:

Вы можете получить доступ к net.Conn базовому tls.Conn , установив tls.Config GetConfigForClient или GetCertificate обратный вызов.

Не имеет значения, какой из них вы используете, пока вы возвращаетесь 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 := amp;tls.Config{
    ...
    GetConfigForClient: setTCPKeepAlive,
    ...
}

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

server.ListenAndServeTLS("../example.crt", "../example.key")
  

Комментарии:

1. Установка KeepAlive вкл net.ListenConfig кажется лучшим решением, никаких утверждений типа не требуется, и это просто работает. Спасибо!

2. Я конкретизировал net.ListenConfig метод и перечислил его первым, поскольку он намного проще.