ListenAndServeTLS cert.pem should be io.reader not (OS *File)

When I find a function that almost does what I need, I start reading the source (conveniently linked from godoc.org):

func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServeTLS(certFile, keyFile)
}

This is a convenience function as it does not provide any new functionality, but make a common use case easier.

Expanding further we see:

func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error {
	addr := srv.Addr
	if addr == "" {
		addr = ":https"
	}
  
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
  
	defer ln.Close()
  
	return srv.ServeTLS(tcpKeepAliveListener{ln.(*net.TCPListener)}, certFile, keyFile)
}

This is also a convenience function, which leads us to:

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

Sadly this isn’t quite a convenience function because it calls the non-exported method srv.setupHTTP2_ServeTLS. IIRC calling Serve will properly handle that when Server.TLSConfig is properly set (I haven’t looked in a few versions, read the docs for both Serve and ServeTLS to help figure this out).

The source of these functions teaches enough about the less-convenient, but more flexible http.Server API that you should be able to do what you need.

For a more complete example using this technique, see my post on Expanding Convenience Functions.

1 Like