aboutsummaryrefslogtreecommitdiffstats
path: root/internal/port/gemini.go
diff options
context:
space:
mode:
authorFeuerfuchs <git@feuerfuchs.dev>2020-05-18 12:12:43 +0200
committerFeuerfuchs <git@feuerfuchs.dev>2020-05-18 12:12:43 +0200
commit4bf44b16562335b3d09b6df0150521bb5b5f776f (patch)
tree576723e6dc9f9db48d0892f7ec354a11b973aef4 /internal/port/gemini.go
parentAdded 2 more glyphs (diff)
downloadgopherproxy-4bf44b16562335b3d09b6df0150521bb5b5f776f.tar.gz
gopherproxy-4bf44b16562335b3d09b6df0150521bb5b5f776f.tar.bz2
gopherproxy-4bf44b16562335b3d09b6df0150521bb5b5f776f.zip
WIP: Refactoring
Diffstat (limited to 'internal/port/gemini.go')
-rw-r--r--internal/port/gemini.go205
1 files changed, 205 insertions, 0 deletions
diff --git a/internal/port/gemini.go b/internal/port/gemini.go
new file mode 100644
index 0000000..f9b0b97
--- /dev/null
+++ b/internal/port/gemini.go
@@ -0,0 +1,205 @@
1package port
2
3import (
4 "bytes"
5 "fmt"
6 "html/template"
7 "io"
8 "log"
9 "mime"
10 "net/http"
11 "net/url"
12 "regexp"
13 "strings"
14
15 "golang.org/x/net/html/charset"
16 "golang.org/x/text/transform"
17
18 "git.vulpes.one/Feuerfuchs/port/port/libgemini"
19
20 "github.com/temoto/robotstxt"
21)
22
23var (
24 TermEscapeSGRPattern = regexp.MustCompile("\\[\\d+(;\\d+)*m")
25)
26
27func resolveURI(uri string, baseURL *url.URL) (resolvedURI string) {
28 if strings.HasPrefix(uri, "//") {
29 resolvedURI = "/gemini/" + strings.TrimPrefix(uri, "//")
30 } else if strings.HasPrefix(uri, "gemini://") {
31 resolvedURI = "/gemini/" + strings.TrimPrefix(uri, "gemini://")
32 } else if strings.HasPrefix(uri, "gopher://") {
33 resolvedURI = "/gopher/" + strings.TrimPrefix(uri, "gopher://")
34 } else {
35 url, err := url.Parse(uri)
36 if err != nil {
37 return ""
38 }
39 adjustedURI := baseURL.ResolveReference(url)
40 path := adjustedURI.Path
41 if !strings.HasPrefix(path, "/") {
42 path = "/" + path
43 }
44 if adjustedURI.Scheme == "gemini" {
45 resolvedURI = "/gemini/" + adjustedURI.Host + path
46 } else if adjustedURI.Scheme == "gopher" {
47 resolvedURI = "/gopher/" + adjustedURI.Host + path
48 } else {
49 resolvedURI = adjustedURI.String()
50 }
51 }
52
53 return
54}
55
56func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, assetList AssetList, robotsdebug bool) http.HandlerFunc {
57 return func(w http.ResponseWriter, req *http.Request) {
58 agent := req.UserAgent()
59 path := strings.TrimPrefix(req.URL.Path, "/gemini/")
60
61 if robotsdata != nil && robotsdebug && !robotsdata.TestAgent(path, agent) {
62 log.Printf("UserAgent %s ignored robots.txt", agent)
63 }
64
65 parts := strings.Split(path, "/")
66 hostport := parts[0]
67
68 if len(hostport) == 0 {
69 http.Redirect(w, req, "/", http.StatusFound)
70 return
71 }
72
73 title := hostport
74
75 var qs string
76
77 if req.URL.RawQuery != "" {
78 qs = fmt.Sprintf("?%s", url.QueryEscape(req.URL.RawQuery))
79 }
80
81 uri, err := url.QueryUnescape(strings.Join(parts[1:], "/"))
82 if err != nil {
83 if e := tpl.Execute(w, TemplateVariables{
84 Title: title,
85 URI: hostport,
86 Assets: assetList,
87 RawText: fmt.Sprintf("Error: %s", err),
88 Error: true,
89 Protocol: "gemini",
90 }); e != nil {
91 log.Println("Template error: " + e.Error())
92 log.Println(err.Error())
93 }
94 return
95 }
96
97 if uri != "" {
98 title = fmt.Sprintf("%s/%s", hostport, uri)
99 }
100
101 res, err := libgemini.Get(
102 fmt.Sprintf(
103 "gemini://%s/%s%s",
104 hostport,
105 uri,
106 qs,
107 ),
108 )
109
110 if err != nil {
111 if e := tpl.Execute(w, TemplateVariables{
112 Title: title,
113 URI: fmt.Sprintf("%s/%s", hostport, uri),
114 Assets: assetList,
115 RawText: fmt.Sprintf("Error: %s", err),
116 Error: true,
117 Protocol: "gemini",
118 }); e != nil {
119 log.Println("Template error: " + e.Error())
120 log.Println(err.Error())
121 }
122 return
123 }
124
125 if int(res.Header.Status/10) == 3 {
126 baseURL, err := url.Parse(fmt.Sprintf(
127 "gemini://%s/%s",
128 hostport,
129 uri,
130 ))
131 if err != nil {
132 if e := tpl.Execute(w, TemplateVariables{
133 Title: title,
134 URI: fmt.Sprintf("%s/%s", hostport, uri),
135 Assets: assetList,
136 RawText: fmt.Sprintf("Error: %s", err),
137 Error: true,
138 Protocol: "gemini",
139 }); e != nil {
140 log.Println("Template error: " + e.Error())
141 log.Println(err.Error())
142 }
143 return
144 }
145
146 http.Redirect(w, req, resolveURI(res.Header.Meta, baseURL), http.StatusFound)
147 return
148 }
149
150 if int(res.Header.Status/10) != 2 {
151 if err := tpl.Execute(w, TemplateVariables{
152 Title: title,
153 URI: fmt.Sprintf("%s/%s", hostport, uri),
154 Assets: assetList,
155 RawText: fmt.Sprintf("Error %d: %s", res.Header.Status, res.Header.Meta),
156 Error: true,
157 Protocol: "gemini",
158 }); err != nil {
159 log.Println("Template error: " + err.Error())
160 }
161 return
162 }
163
164 if strings.HasPrefix(res.Header.Meta, "text/") {
165 buf := new(bytes.Buffer)
166
167 _, params, err := mime.ParseMediaType(res.Header.Meta)
168 if err != nil {
169 buf.ReadFrom(res.Body)
170 } else {
171 encoding, _ := charset.Lookup(params["charset"])
172 readbuf := new(bytes.Buffer)
173 readbuf.ReadFrom(res.Body)
174
175 writer := transform.NewWriter(buf, encoding.NewDecoder())
176 writer.Write(readbuf.Bytes())
177 writer.Close()
178 }
179
180 var (
181 rawText string
182 items []Item
183 )
184
185 if strings.HasPrefix(res.Header.Meta, libgemini.MIME_GEMINI) {
186 items = parseGeminiDocument(buf, uri, hostport)
187 } else {
188 rawText = buf.String()
189 }
190
191 if err := tpl.Execute(w, TemplateVariables{
192 Title: title,
193 URI: fmt.Sprintf("%s/%s", hostport, uri),
194 Assets: assetList,
195 Lines: items,
196 RawText: rawText,
197 Protocol: "gemini",
198 }); err != nil {
199 log.Println("Template error: " + err.Error())
200 }
201 } else {
202 io.Copy(w, res.Body)
203 }
204 }
205}