valyala / fasthttp

Fast HTTP package for Go. Tuned for high performance. Zero memory allocations in hot paths. Up to 10x faster than net/http
MIT License
21.94k stars 1.76k forks source link

fix:fasthttp server with tlsConfig #1595

Closed zxpdmw closed 1 year ago

zxpdmw commented 1 year ago
// fasthttp tls Sample code
func main() {
    r := router.New()
    r.GET("/hello", func(ctx *fasthttp.RequestCtx) {
        ctx.Response.SetBody([]byte("hello workflow"))
    })
    s := &fasthttp.Server{
        Handler:   r.Handler,
        TLSConfig: &tls.Config{},
    }
    if err := s.ListenAndServeTLS("0.0.0.0:443", "", ""); err != nil {
        log.Println(err)
    }
}

// net/http  tls Sample code
func main() {
    http.HandleFunc("/hello", func(writer http.ResponseWriter, request *http.Request) {
        fmt.Fprint(writer, "hello world")
    })
    s := http.Server{
        Addr: "0.0.0.0:443",
        TLSConfig: &tls.Config{},
    }
    if err := s.ListenAndServeTLS("", ""); err != nil {
        log.Println(err)
    }
}

The code is as above, due to the company's security requirements, we need to encrypt and store the SSL certificate and password. Before using it, we need to decrypt and construct *tls.Config. The native http server is listening to the tls port and passing in an empty certificate and key. After that, the TLS port can be monitored normally, but it cannot be monitored normally by calling ListenAndServeTLS with fasthttp to pass in an empty certificate and key. I compared the source code of native http and fasthttp and found that native http has done compatibility processing but fasthttp has not, so there is got this pr.

net/http source code

// ServeTLS accepts incoming connections on the Listener l, creating a
// new service goroutine for each. The service goroutines perform TLS
// setup and then read requests, calling srv.Handler to reply to them.
//
// Files containing a certificate and matching private key for the
// server must be provided if neither the Server's
// TLSConfig.Certificates nor TLSConfig.GetCertificate are populated.
// If the certificate is signed by a certificate authority, the
// certFile should be the concatenation of the server's certificate,
// any intermediates, and the CA's certificate.
//
// ServeTLS always returns a non-nil error. After Shutdown or Close, the
// returned error is ErrServerClosed.
func (srv *Server) ServeTLS(l net.Listener, certFile, keyFile string) error {
    // Setup HTTP/2 before srv.Serve, to initialize srv.TLSConfig
    // before we clone it and create the TLS Listener.
    if err := srv.setupHTTP2_ServeTLS(); err != nil {
        return err
    }

    config := cloneTLSConfig(srv.TLSConfig)
    if !strSliceContains(config.NextProtos, "http/1.1") {
        config.NextProtos = append(config.NextProtos, "http/1.1")
    }

    configHasCert := len(config.Certificates) > 0 || config.GetCertificate != nil
    if !configHasCert || certFile != "" || keyFile != "" {
        var err error
        config.Certificates = make([]tls.Certificate, 1)
        config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
        if err != nil {
            return err
        }
    }

    tlsListener := tls.NewListener(l, config)
    return srv.Serve(tlsListener)
}
erikdubbelboer commented 1 year ago

Thanks!