proxy.go (view raw)
1// Copied from https://git.sr.ht/~sircmpwn/kineto/tree/master/item/main.go
2package main
3
4import (
5 "fmt"
6 "html/template"
7 "io"
8 "mime"
9 "net/http"
10 "net/url"
11 "strings"
12 "time"
13
14 "git.sr.ht/~adnano/go-gemini"
15)
16
17func proxyGemini(w http.ResponseWriter, r *http.Request) {
18 if r.Method != "GET" {
19 w.WriteHeader(http.StatusMethodNotAllowed)
20 w.Write([]byte("404 Not found"))
21 return
22 }
23 path := strings.SplitN(r.URL.Path, "/", 3)
24 req := gemini.Request{}
25 var err error
26 req.Host = path[1]
27 if len(path) > 2 {
28 req.URL, err = url.Parse(fmt.Sprintf("gemini://%s/%s", path[1], path[2]))
29 } else {
30 req.URL, err = url.Parse(fmt.Sprintf("gemini://%s", path[1]))
31 }
32 client := gemini.Client{
33 Timeout: 60 * time.Second,
34 InsecureSkipTrust: true,
35 }
36 fmt.Println(req)
37
38 if h := (url.URL{Host: req.Host}); h.Port() == "" {
39 req.Host += ":1965"
40 }
41
42 resp, err := client.Do(&req)
43 if err != nil {
44 w.WriteHeader(http.StatusBadGateway)
45 fmt.Fprintf(w, "Gateway error: %v", err)
46 return
47 }
48 defer resp.Body.Close()
49
50 switch resp.Status {
51 case 10, 11:
52 // TODO accept input
53 w.WriteHeader(http.StatusInternalServerError)
54 return
55 case 20:
56 break // OK
57 case 30, 31:
58 to, err := url.Parse(resp.Meta)
59 if err != nil {
60 w.WriteHeader(http.StatusBadGateway)
61 w.Write([]byte(fmt.Sprintf("Gateway error: bad redirect %v", err)))
62 }
63 next := req.URL.ResolveReference(to)
64 if next.Scheme != "gemini" {
65 w.WriteHeader(http.StatusOK)
66 w.Write([]byte(fmt.Sprintf("This page is redirecting you to %s", next.String())))
67 return
68 }
69 next.Host = r.URL.Host
70 next.Scheme = r.URL.Scheme
71 w.Header().Add("Location", next.String())
72 w.WriteHeader(http.StatusFound)
73 w.Write([]byte("Redirecting to " + next.String()))
74 return
75 case 40, 41, 42, 43, 44:
76 w.WriteHeader(http.StatusServiceUnavailable)
77 fmt.Fprintf(w, "The remote server returned %d: %s", resp.Status, resp.Meta)
78 return
79 case 50, 51:
80 w.WriteHeader(http.StatusNotFound)
81 fmt.Fprintf(w, "The remote server returned %d: %s", resp.Status, resp.Meta)
82 return
83 case 52, 53, 59:
84 w.WriteHeader(http.StatusServiceUnavailable)
85 fmt.Fprintf(w, "The remote server returned %d: %s", resp.Status, resp.Meta)
86 return
87 default:
88 w.WriteHeader(http.StatusNotImplemented)
89 fmt.Fprintf(w, "Proxy does not understand Gemini response status %d", resp.Status)
90 return
91 }
92
93 m, _, err := mime.ParseMediaType(resp.Meta)
94 if err != nil {
95 w.WriteHeader(http.StatusBadGateway)
96 w.Write([]byte(fmt.Sprintf("Gateway error: %d %s: %v",
97 resp.Status, resp.Meta, err)))
98 return
99 }
100
101 if m != "text/gemini" {
102 w.Header().Add("Content-Type", resp.Meta)
103 io.Copy(w, resp.Body)
104 return
105 }
106
107 w.Header().Add("Content-Type", "text/html")
108
109 htmlString := textToHTML(gemini.ParseText(resp.Body))
110 data := struct {
111 SiteBody template.HTML
112 Favicon string
113 PageTitle string
114 URI string
115 }{template.HTML(htmlString), "", req.URL.String(), req.URL.String()}
116
117 err = t.ExecuteTemplate(w, "user_page.html", data)
118 if err != nil {
119 w.WriteHeader(http.StatusInternalServerError)
120 fmt.Fprintf(w, "%v", err)
121 return
122 }
123}