aboutsummaryrefslogtreecommitdiff
path: root/gemini.go
blob: 3c137c1d7a4f7791162461d660e1fa740990f9c5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package gmi

import (
	"crypto/tls"
	"crypto/x509"
	"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
)

// 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)
}