diff options
| author | Adnan Maolood <[email protected]> | 2020-10-24 15:15:32 -0400 |
|---|---|---|
| committer | Adnan Maolood <[email protected]> | 2020-10-24 15:15:32 -0400 |
| commit | f0345f349594b3bb93a45827c7f99688ce73ae34 (patch) | |
| tree | a1065b08973167e4a23ce6d91c9df3ab06209a41 /gemini.go | |
| parent | Add Certificate helper function (diff) | |
| download | go-gemini-f0345f349594b3bb93a45827c7f99688ce73ae34.tar.xz go-gemini-f0345f349594b3bb93a45827c7f99688ce73ae34.zip | |
Rename repository to go-gemini
Diffstat (limited to 'gemini.go')
| -rw-r--r-- | gemini.go | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/gemini.go b/gemini.go new file mode 100644 index 0000000..f03fd82 --- /dev/null +++ b/gemini.go @@ -0,0 +1,98 @@ +package gemini + +import ( + "crypto/tls" + "crypto/x509" + "errors" + "sync" + "time" +) + +// Status codes. +const ( + StatusInput = 10 + StatusSensitiveInput = 11 + StatusSuccess = 20 + StatusRedirect = 30 + StatusRedirectPermanent = 31 + StatusTemporaryFailure = 40 + StatusServerUnavailable = 41 + StatusCGIError = 42 + StatusProxyError = 43 + StatusSlowDown = 44 + StatusPermanentFailure = 50 + StatusNotFound = 51 + StatusGone = 52 + StatusProxyRequestRefused = 53 + StatusBadRequest = 59 + StatusCertificateRequired = 60 + StatusCertificateNotAuthorized = 61 + StatusCertificateNotValid = 62 +) + +// Status code categories. +const ( + StatusClassInput = 1 + StatusClassSuccess = 2 + StatusClassRedirect = 3 + StatusClassTemporaryFailure = 4 + StatusClassPermanentFailure = 5 + StatusClassCertificateRequired = 6 +) + +// Errors. +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") +) + +// DefaultClient is the default client. It is used by Send. +// +// On the first request, DefaultClient will load the default list of known hosts. +var DefaultClient Client + +var ( + crlf = []byte("\r\n") +) + +func init() { + 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) + } + DefaultClient.GetCertificate = func(hostname string, store *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 := NewCertificate(hostname, duration) + if err != nil { + return nil + } + // Store and return the certificate + store.Add(hostname, cert) + return &cert + } +} + +var setupDefaultClientOnce sync.Once + +func setupDefaultClient() { + DefaultClient.KnownHosts.LoadDefault() +} + +// 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) +} |