go-gemini
go-gemini implements the Gemini protocol
in Go.
It aims to provide an API similar to that of net/http to make it easy to
develop Gemini clients and servers.
Examples
See examples/client and examples/server for an example client and server.
To run the examples:
go run -tags=example ./examples/server
Overview
A quick overview of the Gemini protocol:
- Client opens connection
 - Server accepts connection
 - Client and server complete a TLS handshake
 - Client validates server certificate
 - Client sends request
 - Server sends response header
 - Server sends response body (only for successful responses)
 - Server closes connection
 - Client handles response
 
The way this is implemented in this package is like so:
- Client makes a request with 
NewRequest. The client then sends the request with(*Client).Send(*Request) (*Response, error). The client then determines whether to trust the certificate (see TOFU). - Server recieves the request and constructs a response.
The server calls the 
Serve(*ResponseWriter, *Request)method on theHandlerfield. The handler writes the response. The server then closes the connection. - Client recieves the response as a 
*Response. The client then handles the response. 
TOFU
go-gemini makes it easy to implement Trust On First Use in your clients.
The default client loads known hosts from $XDG_DATA_HOME/gemini/known_hosts.
If that is all you need, you can simply use the top-level Send function:
// Send uses the default client, which will load the default list of known hosts.
req := gemini.NewRequest("gemini://example.com")
gemini.Send(req)
Clients can also load their own list of known hosts:
client := &Client{}
if err := client.KnownHosts.LoadFrom("path/to/my/known_hosts"); err != nil {
	log.Fatal(err)
}
Clients can then specify how to trust certificates in the TrustCertificate
field:
client.TrustCertificate = func(hostname string, cert *x509.Certificate, knownHosts *gemini.KnownHosts) error {
	// If the certificate is in the known hosts list, allow the connection
	return knownHosts.Lookup(hostname, cert)
}
Advanced clients can prompt the user for what to do when encountering an unknown certificate:
client.TrustCertificate = func(hostname string, cert *x509.Certificate, knownHosts *gemini.KnownHosts) error {
	err := knownHosts.Lookup(cert)
	if err != nil {
		switch err {
		case gemini.ErrCertificateNotTrusted:
			// Alert the user that the certificate is not trusted
			fmt.Printf("Warning: certificate for %s is not trusted!\n", hostname)
			fmt.Println("This could indicate a Man-in-the-Middle attack.")
		case gemini.ErrCertificateUnknown:
			// Prompt the user to trust the certificate
			if userTrustsCertificateTemporarily() {
				// Temporarily trust the certificate
				knownHosts.AddTemporary(hostname, cert)
				return nil
			} else if userTrustsCertificatePermanently() {
				// Add the certificate to the known hosts file
				knownHosts.Add(cert)
				return nil
			}
		}
	}
	return err
}
See examples/client for an example client.