go-gemini/gemini.go

144 lines
4.1 KiB
Go
Raw Normal View History

2020-10-24 19:15:32 +00:00
package gemini
2020-09-25 23:09:49 +00:00
2020-09-26 20:52:14 +00:00
import (
"crypto/tls"
2020-09-26 20:52:14 +00:00
"crypto/x509"
2020-10-14 00:10:04 +00:00
"errors"
2020-09-26 20:52:14 +00:00
"sync"
"time"
2020-09-26 20:52:14 +00:00
)
2020-09-25 23:09:49 +00:00
// Status codes.
type Status int
2020-09-25 23:09:49 +00:00
const (
StatusInput Status = 10
StatusSensitiveInput Status = 11
StatusSuccess Status = 20
StatusRedirect Status = 30
StatusRedirectPermanent Status = 31
StatusTemporaryFailure Status = 40
StatusServerUnavailable Status = 41
StatusCGIError Status = 42
StatusProxyError Status = 43
StatusSlowDown Status = 44
StatusPermanentFailure Status = 50
StatusNotFound Status = 51
StatusGone Status = 52
StatusProxyRequestRefused Status = 53
StatusBadRequest Status = 59
StatusCertificateRequired Status = 60
StatusCertificateNotAuthorized Status = 61
StatusCertificateNotValid Status = 62
2020-09-25 23:09:49 +00:00
)
// Class returns the status class for this status code.
func (s Status) Class() StatusClass {
return StatusClass(s / 10)
}
// StatusMessage returns the status message corresponding to the provided
// status code.
// StatusMessage returns an empty string for input, successs, and redirect
// status codes.
func (s Status) Message() string {
switch s {
case StatusTemporaryFailure:
return "TemporaryFailure"
case StatusServerUnavailable:
return "Server unavailable"
case StatusCGIError:
return "CGI error"
case StatusProxyError:
return "Proxy error"
case StatusSlowDown:
return "Slow down"
case StatusPermanentFailure:
return "PermanentFailure"
case StatusNotFound:
return "Not found"
case StatusGone:
return "Gone"
case StatusProxyRequestRefused:
return "Proxy request refused"
case StatusBadRequest:
return "Bad request"
case StatusCertificateRequired:
return "Certificate required"
case StatusCertificateNotAuthorized:
return "Certificate not authorized"
case StatusCertificateNotValid:
return "Certificate not valid"
}
return ""
}
2020-09-25 23:09:49 +00:00
// Status code categories.
type StatusClass int
2020-09-25 23:09:49 +00:00
const (
StatusClassInput StatusClass = 1
StatusClassSuccess StatusClass = 2
StatusClassRedirect StatusClass = 3
StatusClassTemporaryFailure StatusClass = 4
StatusClassPermanentFailure StatusClass = 5
StatusClassCertificateRequired StatusClass = 6
2020-09-25 23:09:49 +00:00
)
2020-10-14 00:10:04 +00:00
// Errors.
var (
2020-10-24 19:15:32 +00:00
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")
2020-10-14 00:10:04 +00:00
)
2020-09-26 20:52:14 +00:00
// DefaultClient is the default client. It is used by Send.
//
// On the first request, DefaultClient will load the default list of known hosts.
2020-10-14 00:10:04 +00:00
var DefaultClient Client
2020-09-26 20:52:14 +00:00
2020-09-28 02:15:36 +00:00
var (
crlf = []byte("\r\n")
)
2020-09-26 20:52:14 +00:00
func init() {
2020-09-28 02:18:21 +00:00
DefaultClient.TrustCertificate = func(hostname string, cert *x509.Certificate, knownHosts *KnownHosts) error {
// Load the hosts only once. This is so that the hosts don't have to be loaded
// for those using their own clients.
setupDefaultClientOnce.Do(setupDefaultClient)
return knownHosts.Lookup(hostname, cert)
2020-09-26 20:52:14 +00:00
}
2020-10-12 20:34:52 +00:00
DefaultClient.GetCertificate = func(hostname string, store *CertificateStore) *tls.Certificate {
// If the certificate is in the store, return it
2020-10-12 03:48:18 +00:00
if cert, err := store.Lookup(hostname); err == nil {
return cert
}
// Otherwise, generate a certificate
duration := time.Hour
cert, err := NewCertificate(hostname, duration)
if err != nil {
return nil
}
// Store and return the certificate
2020-10-12 03:48:18 +00:00
store.Add(hostname, cert)
return &cert
}
2020-09-26 20:52:14 +00:00
}
var setupDefaultClientOnce sync.Once
func setupDefaultClient() {
DefaultClient.KnownHosts.LoadDefault()
2020-09-26 20:52:14 +00:00
}
// Send sends a Gemini request and returns a Gemini response.
//
// Send is a wrapper around DefaultClient.Send.
func Send(req *Request) (*Response, error) {
return DefaultClient.Send(req)
}