Documentation ¶
Overview ¶
Package gemini implements the Gemini protocol.
Send makes a Gemini request with the default client:
req := gemini.NewRequest("gemini://example.com") resp, err := gemini.Send(req) if err != nil { // handle error } // ...
For control over client behavior, create a custom Client:
var client gemini.Client resp, err := client.Send(req) if err != nil { // handle error } // ...
The default client loads known hosts from "$XDG_DATA_HOME/gemini/known_hosts". Custom clients can load their own list of known hosts:
err := client.KnownHosts.Load("path/to/my/known_hosts") if err != nil { // handle error }
Clients can control when to trust certificates with TrustCertificate:
client.TrustCertificate = func(hostname string, cert *x509.Certificate, knownHosts *gemini.KnownHosts) error { return knownHosts.Lookup(hostname, cert) }
If a server responds with StatusCertificateRequired, the default client will generate a certificate and resend the request with it. Custom clients can do so in GetCertificate:
client.GetCertificate = func(hostname string, store *gemini.CertificateStore) *tls.Certificate { // If the certificate is in the store, return it if cert, err := store.Lookup(hostname); err == nil { return &cert } // Otherwise, generate a certificate duration := time.Hour cert, err := gemini.NewCertificate(hostname, duration) if err != nil { return nil } // Store and return the certificate store.Add(hostname, cert) return &cert }
Server is a Gemini server.
var server gemini.Server
Servers must be configured with certificates:
err := server.CertificateStore.Load("/var/lib/gemini/certs") if err != nil { // handle error }
Servers can accept requests for multiple hosts and schemes:
server.RegisterFunc("example.com", func(w *gemini.ResponseWriter, r *gemini.Request) { fmt.Fprint(w, "Welcome to example.com") }) server.RegisterFunc("example.org", func(w *gemini.ResponseWriter, r *gemini.Request) { fmt.Fprint(w, "Welcome to example.org") }) server.RegisterFunc("http://example.net", func(w *gemini.ResponseWriter, r *gemini.Request) { fmt.Fprint(w, "Proxied content from http://example.net") })
To start the server, call ListenAndServe:
err := server.ListenAndServe() if err != nil { // handle error }
Index ¶
- Constants
- Variables
- func Certificate(w *ResponseWriter, r *Request) (*x509.Certificate, bool)
- func CertificateNotAuthorized(w *ResponseWriter, r *Request)
- func CertificateRequired(w *ResponseWriter, r *Request)
- func Fingerprint(cert *x509.Certificate) string
- func Gone(w *ResponseWriter, r *Request)
- func Input(w *ResponseWriter, r *Request, prompt string) (string, bool)
- func NewCertificate(host string, duration time.Duration) (tls.Certificate, error)
- func NotFound(w *ResponseWriter, r *Request)
- func PermanentRedirect(w *ResponseWriter, r *Request, url string)
- func Redirect(w *ResponseWriter, r *Request, url string)
- func SensitiveInput(w *ResponseWriter, r *Request, prompt string) (string, bool)
- type CertificateStore
- type Client
- type Dir
- type FS
- type File
- type KnownHosts
- func (k *KnownHosts) Add(hostname string, cert *x509.Certificate)
- func (k *KnownHosts) AddTemporary(hostname string, cert *x509.Certificate)
- func (k *KnownHosts) Load(path string) error
- func (k *KnownHosts) LoadDefault() error
- func (k *KnownHosts) Lookup(hostname string, cert *x509.Certificate) error
- func (k *KnownHosts) Parse(r io.Reader)
- func (k *KnownHosts) Write(w io.Writer)
- type Line
- type LineHeading1
- type LineHeading2
- type LineHeading3
- type LineImage
- type LineLink
- type LineListItem
- type LinePreformattedText
- type LinePreformattingToggle
- type LineQuote
- type LineText
- type Request
- type Responder
- type ResponderFunc
- type Response
- type ResponseWriter
- type ServeMux
- type Server
- type Text
Constants ¶
const ( StatusInput = 10 StatusSensitiveInput = 11 StatusSuccess = 20 StatusRedirect = 30 StatusRedirectPermanent = 31 StatusTemporaryFailure = 40 StatusCGIError = 42 StatusProxyError = 43 StatusSlowDown = 44 StatusPermanentFailure = 50 StatusNotFound = 51 StatusGone = 52 StatusProxyRequestRefused = 53 StatusBadRequest = 59 StatusCertificateRequired = 60 StatusCertificateNotAuthorized = 61 StatusCertificateNotValid = 62 )
Status codes.
const ( StatusClassInput = 1 StatusClassSuccess = 2 StatusClassRedirect = 3 StatusClassTemporaryFailure = 4 StatusClassPermanentFailure = 5 StatusClassCertificateRequired = 6 )
Status code categories.
Variables ¶
var ( ErrInvalidURL = errors.New("gemini: invalid URL") ErrInvalidResponse = errors.New("gemini: invalid response") ErrCertificateUnknown = errors.New("gemini: unknown certificate") ErrCertificateExpired = errors.New("gemini: certificate expired") ErrCertificateNotTrusted = errors.New("gemini: certificate is not trusted") ErrNotAFile = errors.New("gemini: not a file") ErrBodyNotAllowed = errors.New("gemini: response status code does not allow for body") )
Errors.
Functions ¶
func Certificate ¶
func Certificate(w *ResponseWriter, r *Request) (*x509.Certificate, bool)
Certificate returns the request certificate. If one is not provided, it returns nil and responds with StatusCertificateRequired.
func CertificateNotAuthorized ¶
func CertificateNotAuthorized(w *ResponseWriter, r *Request)
CertificateNotAuthorized responds to the request with the CertificateNotAuthorized status code.
func CertificateRequired ¶
func CertificateRequired(w *ResponseWriter, r *Request)
CertificateRequired responds to the request with the CertificateRequired status code.
func Fingerprint ¶
func Fingerprint(cert *x509.Certificate) string
Fingerprint returns the SHA-512 fingerprint of the provided certificate.
func Gone ¶
func Gone(w *ResponseWriter, r *Request)
Gone replies to the request with the Gone status code.
func Input ¶
func Input(w *ResponseWriter, r *Request, prompt string) (string, bool)
Input returns the request query. If no input is provided, it responds with StatusInput.
func NewCertificate ¶
NewCertificate creates and returns a new parsed certificate.
func NotFound ¶
func NotFound(w *ResponseWriter, r *Request)
NotFound replies to the request with the NotFound status code.
func PermanentRedirect ¶
func PermanentRedirect(w *ResponseWriter, r *Request, url string)
PermanentRedirect replies to the request with a permanent redirect to the given URL.
func Redirect ¶
func Redirect(w *ResponseWriter, r *Request, url string)
Redirect replies to the request with a redirect to the given URL.
func SensitiveInput ¶
func SensitiveInput(w *ResponseWriter, r *Request, prompt string) (string, bool)
SensitiveInput returns the request query. If no input is provided, it responds with StatusSensitiveInput.
Types ¶
type CertificateStore ¶
type CertificateStore struct {
// contains filtered or unexported fields
}
CertificateStore maps hostnames to certificates. The zero value of CertificateStore is an empty store ready to use.
func (*CertificateStore) Add ¶
func (c *CertificateStore) Add(hostname string, cert tls.Certificate)
Add adds a certificate for the given hostname to the store. It tries to parse the certificate if it is not already parsed.
func (*CertificateStore) Load ¶
func (c *CertificateStore) Load(path string) error
Load loads certificates from the given path. The path should lead to a directory containing certificates and private keys in the form hostname.crt and hostname.key. For example, the hostname "localhost" would have the corresponding files localhost.crt (certificate) and localhost.key (private key).
func (*CertificateStore) Lookup ¶
func (c *CertificateStore) Lookup(hostname string) (*tls.Certificate, error)
Lookup returns the certificate for the given hostname.
type Client ¶
type Client struct { // KnownHosts is a list of known hosts that the client trusts. KnownHosts KnownHosts // CertificateStore maps hostnames to certificates. // It is used to determine which certificate to use when the server requests // a certificate. CertificateStore CertificateStore // GetCertificate, if not nil, will be called when a server requests a certificate. // The returned certificate will be used when sending the request again. // If the certificate is nil, the request will not be sent again and // the response will be returned. GetCertificate func(hostname string, store *CertificateStore) *tls.Certificate // TrustCertificate, if not nil, will be called to determine whether the // client should trust the given certificate. // If error is not nil, the connection will be aborted. TrustCertificate func(hostname string, cert *x509.Certificate, knownHosts *KnownHosts) error }
Client represents a Gemini client.
var DefaultClient Client
DefaultClient is the default client. It is used by Send.
On the first request, DefaultClient will load the default list of known hosts.
type Dir ¶
type Dir string
Dir implements FS using the native filesystem restricted to a specific directory.
type KnownHosts ¶
type KnownHosts struct {
// contains filtered or unexported fields
}
KnownHosts represents a list of known hosts. The zero value for KnownHosts is an empty list ready to use.
func (*KnownHosts) Add ¶
func (k *KnownHosts) Add(hostname string, cert *x509.Certificate)
Add adds a certificate to the list of known hosts. If KnownHosts was loaded from a file, Add will append to the file.
func (*KnownHosts) AddTemporary ¶
func (k *KnownHosts) AddTemporary(hostname string, cert *x509.Certificate)
AddTemporary adds a certificate to the list of known hosts without writing it to the known hosts file.
func (*KnownHosts) Load ¶
func (k *KnownHosts) Load(path string) error
Load loads the known hosts from the provided path. It creates the path and any of its parent directories if they do not exist. KnownHosts will append to the file whenever a certificate is added.
func (*KnownHosts) LoadDefault ¶
func (k *KnownHosts) LoadDefault() error
LoadDefault loads the known hosts from the default known hosts path, which is $XDG_DATA_HOME/gemini/known_hosts. It creates the path and any of its parent directories if they do not exist. KnownHosts will append to the file whenever a certificate is added.
func (*KnownHosts) Lookup ¶
func (k *KnownHosts) Lookup(hostname string, cert *x509.Certificate) error
Lookup looks for the provided certificate in the list of known hosts. If the hostname is in the list, but the fingerprint differs, Lookup returns ErrCertificateNotTrusted. If the hostname is not in the list, Lookup returns ErrCertificateUnknown. If the certificate is found and the fingerprint matches, error will be nil.
func (*KnownHosts) Parse ¶
func (k *KnownHosts) Parse(r io.Reader)
Parse parses the provided reader and adds the parsed known hosts to the list. Invalid lines are ignored.
func (*KnownHosts) Write ¶
func (k *KnownHosts) Write(w io.Writer)
Write writes the known hosts to the provided io.Writer.
type Line ¶
type Line interface { String() string // contains filtered or unexported methods }
Line represents a line of a Gemini text response.
type LineHeading1 ¶
type LineHeading1 string
A first-level heading line.
func (LineHeading1) String ¶
func (l LineHeading1) String() string
type LineHeading2 ¶
type LineHeading2 string
A second-level heading line.
func (LineHeading2) String ¶
func (l LineHeading2) String() string
type LineHeading3 ¶
type LineHeading3 string
A third-level heading line.
func (LineHeading3) String ¶
func (l LineHeading3) String() string
type LineListItem ¶
type LineListItem string
An unordered list item line.
func (LineListItem) String ¶
func (l LineListItem) String() string
type LinePreformattedText ¶
type LinePreformattedText string
A preformatted text line.
func (LinePreformattedText) String ¶
func (l LinePreformattedText) String() string
type LinePreformattingToggle ¶
type LinePreformattingToggle string
A preformatting toggle line.
func (LinePreformattingToggle) String ¶
func (l LinePreformattingToggle) String() string
type Request ¶
type Request struct { // URL specifies the URL being requested. URL *url.URL // For client requests, Host specifies the host on which the URL is sought. // Host must contain a port. // This field is ignored by the server. Host string // Certificate specifies the TLS certificate to use for the request. // Request certificates take precedence over client certificates. // This field is ignored by the server. Certificate *tls.Certificate // RemoteAddr allows servers and other software to record the network // address that sent the request. // This field is ignored by the client. RemoteAddr net.Addr // TLS allows servers and other software to record information about the TLS // connection on which the request was received. // This field is ignored by the client. TLS tls.ConnectionState }
Request represents a Gemini request.
func NewRequest ¶
NewRequest returns a new request. The host is inferred from the provided URL.
func NewRequestTo ¶
NewRequestTo returns a new request for the provided URL to the provided host. The host must contain a port.
type Responder ¶
type Responder interface { // Respond accepts a Request and constructs a Response. Respond(*ResponseWriter, *Request) }
A Responder responds to a Gemini request.
func FileServer ¶
FileServer takes a filesystem and returns a Responder which uses that filesystem. The returned Responder sanitizes paths before handling them.
type ResponderFunc ¶
type ResponderFunc func(*ResponseWriter, *Request)
ResponderFunc is a wrapper around a bare function that implements Responder.
func (ResponderFunc) Respond ¶
func (f ResponderFunc) Respond(w *ResponseWriter, r *Request)
type Response ¶
type Response struct { // Status represents the response status. Status int // Meta contains more information related to the response status. // For successful responses, Meta should contain the mimetype of the response. // For failure responses, Meta should contain a short description of the failure. // Meta should not be longer than 1024 bytes. Meta string // Body contains the response body. Body []byte // TLS contains information about the TLS connection on which the response // was received. TLS tls.ConnectionState }
Response is a Gemini response.
type ResponseWriter ¶
type ResponseWriter struct {
// contains filtered or unexported fields
}
ResponseWriter is used by a Gemini handler to construct a Gemini response.
func (*ResponseWriter) SetMimetype ¶
func (w *ResponseWriter) SetMimetype(mimetype string)
SetMimetype sets the mimetype that will be written for a successful response. The provided mimetype will only be used if Write is called without calling WriteHeader. If the mimetype is not set, it will default to "text/gemini".
func (*ResponseWriter) Write ¶
func (w *ResponseWriter) Write(b []byte) (int, error)
Write writes the response body. If the response status does not allow for a response body, Write returns ErrBodyNotAllowed.
If WriteHeader has not yet been called, Write calls WriteHeader(StatusSuccess, mimetype) where mimetype is the mimetype set in SetMimetype. If no mimetype is set, a default of "text/gemini" will be used.
func (*ResponseWriter) WriteHeader ¶
func (w *ResponseWriter) WriteHeader(status int, meta string)
WriteHeader writes the response header. If the header has already been written, WriteHeader does nothing.
Meta contains more information related to the response status. For successful responses, Meta should contain the mimetype of the response. For failure responses, Meta should contain a short description of the failure. Meta should not be longer than 1024 bytes.
type ServeMux ¶
type ServeMux struct {
// contains filtered or unexported fields
}
ServeMux is a Gemini request multiplexer. It matches the URL of each incoming request against a list of registered patterns and calls the handler for the pattern that most closely matches the URL.
Patterns name fixed, rooted paths, like "/favicon.ico", or rooted subtrees, like "/images/" (note the trailing slash). Longer patterns take precedence over shorter ones, so that if there are handlers registered for both "/images/" and "/images/thumbnails/", the latter handler will be called for paths beginning "/images/thumbnails/" and the former will receive requests for any other paths in the "/images/" subtree.
Note that since a pattern ending in a slash names a rooted subtree, the pattern "/" matches all paths not matched by other registered patterns, not just the URL with Path == "/".
If a subtree has been registered and a request is received naming the subtree root without its trailing slash, ServeMux redirects that request to the subtree root (adding the trailing slash). This behavior can be overridden with a separate registration for the path without the trailing slash. For example, registering "/images/" causes ServeMux to redirect a request for "/images" to "/images/", unless "/images" has been registered separately.
ServeMux also takes care of sanitizing the URL request path and redirecting any request containing . or .. elements or repeated slashes to an equivalent, cleaner URL.
func (*ServeMux) Handle ¶
Handle registers the responder for the given pattern. If a responder already exists for pattern, Handle panics.
func (*ServeMux) HandleFunc ¶
func (mux *ServeMux) HandleFunc(pattern string, responder func(*ResponseWriter, *Request))
HandleFunc registers the responder function for the given pattern.
func (*ServeMux) Respond ¶
func (mux *ServeMux) Respond(w *ResponseWriter, r *Request)
Respond dispatches the request to the responder whose pattern most closely matches the request URL.
type Server ¶
type Server struct { // Addr specifies the address that the server should listen on. // If Addr is empty, the server will listen on the address ":1965". Addr string // CertificateStore contains the certificates used by the server. CertificateStore CertificateStore // GetCertificate, if not nil, will be called to retrieve the certificate // to use for a given hostname. // If the certificate is nil, the connection will be aborted. GetCertificate func(hostname string, store *CertificateStore) *tls.Certificate // contains filtered or unexported fields }
Server is a Gemini server.
func (*Server) ListenAndServe ¶
ListenAndServe listens for requests at the server's configured address.
func (*Server) Register ¶
Register registers a responder for the given pattern. Patterns must be in the form of scheme://hostname (e.g. gemini://example.com). If no scheme is specified, a default scheme of gemini:// is assumed. Wildcard patterns are supported (e.g. *.example.com).
func (*Server) RegisterFunc ¶
func (s *Server) RegisterFunc(pattern string, responder func(*ResponseWriter, *Request))
RegisterFunc registers a responder function for the given pattern.