aboutsummaryrefslogtreecommitdiffstats
path: root/libgemini.go
diff options
context:
space:
mode:
authorFeuerfuchs <git@feuerfuchs.dev>2019-11-26 13:13:02 +0100
committerFeuerfuchs <git@feuerfuchs.dev>2019-11-26 13:13:02 +0100
commit93115f804220c31c2aa10f123560fb11135f06d8 (patch)
treea3c12f68c4263f3e34b1e03f12e7962aab9cfbf5 /libgemini.go
parentFix title on startpage (diff)
downloadgopherproxy-93115f804220c31c2aa10f123560fb11135f06d8.tar.gz
gopherproxy-93115f804220c31c2aa10f123560fb11135f06d8.tar.bz2
gopherproxy-93115f804220c31c2aa10f123560fb11135f06d8.zip
Add IPv6 support, general restructuring
Diffstat (limited to 'libgemini.go')
-rw-r--r--libgemini.go153
1 files changed, 0 insertions, 153 deletions
diff --git a/libgemini.go b/libgemini.go
deleted file mode 100644
index 94744ba..0000000
--- a/libgemini.go
+++ /dev/null
@@ -1,153 +0,0 @@
1package gopherproxy
2
3import (
4 "bufio"
5 "crypto/tls"
6 "errors"
7 "io"
8 "mime"
9 "net/url"
10 "regexp"
11 "strconv"
12 "strings"
13)
14
15const (
16 CRLF = "\r\n"
17)
18
19const (
20 STATUS_INPUT = 10
21 STATUS_SUCCESS = 20
22 STATUS_SUCCESS_CERT = 21
23 STATUS_REDIRECT_TEMP = 30
24 STATUS_REDIRECT_PERM = 31
25 STATUS_TEMP_FAILURE = 40
26 STATUS_SERVER_UNAVAILABLE = 41
27 STATUS_CGI_ERROR = 42
28 STATUS_PROXY_ERROR = 43
29 STATUS_SLOW_DOWN = 44
30 STATUS_PERM_FAILURE = 50
31 STATUS_NOT_FOUND = 51
32 STATUS_GONE = 52
33 STATUS_PROXY_REFUSED = 53
34 STATUS_BAD_REQUEST = 59
35 STATUS_CLIENT_CERT_EXPIRED = 60
36 STATUS_TRANSIENT_CERT_REQUEST = 61
37 STATUS_AUTH_CERT_REQUIRED = 62
38 STATUS_CERT_REJECTED = 63
39 STATUS_FUTURE_CERT_REJECTED = 64
40 STATUS_EXPIRED_CERT_REJECTED = 65
41)
42
43const (
44 MIME_GEMINI = "text/gemini"
45 DEFAULT_MIME = MIME_GEMINI
46 DEFAULT_CHARSET = "utf-8"
47)
48
49var (
50 HeaderPattern = regexp.MustCompile("^(\\d\\d)[ \\t]+(.*)$")
51 GeminiLinkPattern = regexp.MustCompile("^=>[ \\t]*([^ \\t]+)(?:[ \\t]+(.*))?$")
52)
53
54type GeminiHeader struct {
55 Status int
56 Meta string
57}
58
59type GeminiResponse struct {
60 Header *GeminiHeader
61 Body io.Reader
62}
63
64func GeminiGet(uri string) (*GeminiResponse, error) {
65 u, err := url.Parse(uri)
66 if err != nil {
67 return nil, err
68 }
69
70 if u.Scheme != "gemini" {
71 return nil, errors.New("invalid scheme for uri")
72 }
73
74 var (
75 host string
76 port int
77 )
78
79 hostport := strings.Split(u.Host, ":")
80 if len(hostport) == 2 {
81 host = hostport[0]
82 n, err := strconv.ParseInt(hostport[1], 10, 32)
83 if err != nil {
84 return nil, err
85 }
86 port = int(n)
87 } else {
88 host, port = hostport[0], 1965
89 }
90
91 conn, err := tls.Dial("tcp", host+":"+strconv.Itoa(port), &tls.Config{
92 MinVersion: tls.VersionTLS12,
93 InsecureSkipVerify: true,
94 })
95 if err != nil {
96 return nil, err
97 }
98
99 _, err = conn.Write([]byte(u.String() + CRLF))
100 if err != nil {
101 conn.Close()
102 return nil, err
103 }
104
105 reader := bufio.NewReader(conn)
106
107 line, _, err := reader.ReadLine()
108 if err != nil {
109 conn.Close()
110 return nil, err
111 }
112
113 header, err := ParseGeminiHeader(string(line))
114 if err != nil {
115 conn.Close()
116 return nil, err
117 }
118
119 return &GeminiResponse{
120 Header: header,
121 Body: reader,
122 }, nil
123}
124
125func ParseGeminiHeader(line string) (header *GeminiHeader, err error) {
126 matches := HeaderPattern.FindStringSubmatch(line)
127
128 status, err := strconv.Atoi(matches[1])
129 if err != nil {
130 return nil, err
131 }
132
133 meta := matches[2]
134
135 if int(status/10) == 2 {
136 mediaType, params, err := mime.ParseMediaType(meta)
137
138 if err != nil {
139 meta = DEFAULT_MIME + ";charset=" + DEFAULT_CHARSET
140 } else if strings.HasPrefix(mediaType, "text/") {
141 if _, ok := params["charset"]; !ok {
142 meta += ";charset=" + DEFAULT_CHARSET
143 }
144 }
145 }
146
147 header = &GeminiHeader{
148 Status: status,
149 Meta: meta,
150 }
151
152 return
153}