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

Hello,
I’m very new to programming and golang.
I do have a problem I want to solve, that lead me to wondering about the below.
Yes I have a problem to solve, but here i am trying to understand the what and why of it all.
not find a work around.

I have a question, maybe someone can help me understand better.

I am trying to launch a webserver with TLS
using net.http

http.ListenAndServe(":443", certFile, keyFile, mux, )

in the above both certFile and KeyFIle are of type string
however for this to work they need to be of type (os *File)
(stat and lstat)

when you run ListenAndServerTLS( , cert, key, ,)
it will call x509.LoadKeyPair(certFile, keyFile)
passing the strings along from the original fucntion
then x509LKP will try to open those files and load their content, then pass it back up to the http.LaSTLS

MY question is why does this need to be an actual file?
in the end arent we really only needing an io.Reader{} interface?

Before you tell me this is an XY problem.
Yes i do have an actual problem i am trying to solve, which my cert.pem is not in a file form
additionally for trust chain to be proven (in the docs) I need to cat the CA and cert.pem
which again if not already in a single file
will require a mem based operation. Am I expected to write this to a new file just so http.ListenAndServeTLS can open again?

so yes I have a problem I am trying to solve, but also I am trying to understand
so one day I won;t be a go n00b

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

I dont know what the protocol is for expressing thanks. I’ve been lambasted for it on other forums.
But I do want to express my gratitude for your kind, thorough, not condescending answer.
I greatly appreciate it.

2 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.