flounder

A simple gemini site builder
Log | Files | Refs | README | LICENSE

commit 1a867bb9b9fbf406d431b32799bd280c17d4a20a
parent f603adbbef0f6f987fedc941cbc34ce95caabffe
Author: alex wennerberg <alex@alexwennerberg.com>
Date:   Wed,  6 Jan 2021 21:54:05 -0800

WIP proxy server

Diffstat:
Mgmi2html.go | 22+++++++++++++++++++---
Mgo.sum | 4++++
Mhttp.go | 2++
Aproxy.go | 123+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 148 insertions(+), 3 deletions(-)

diff --git a/gmi2html.go b/gmi2html.go @@ -3,6 +3,7 @@ package main import ( "fmt" "html" + "net/url" "strings" "git.sr.ht/~adnano/go-gemini" @@ -25,12 +26,23 @@ func textToHTML(text gemini.Text) string { switch l.(type) { case gemini.LineLink: link := l.(gemini.LineLink) - url := html.EscapeString(link.URL) + urlstring := html.EscapeString(link.URL) + // u = ctx.URL.ResolveReference(u) ? + u, err := url.Parse(urlstring) + if err != nil { + continue + } + if u.Scheme == "gemini" { + u.Path = fmt.Sprintf("/%s%s", u.Host, u.Path) + u.Scheme = "" + u.Host = "proxy." + c.Host + urlstring = html.EscapeString(u.String()) + } name := html.EscapeString(link.Name) if name == "" { - name = url + name = urlstring } - fmt.Fprintf(&b, "<p><a href='%s'>%s</a></p>\n", url, name) + fmt.Fprintf(&b, "<p><a href='%s'>%s</a></p>\n", urlstring, name) case gemini.LinePreformattingToggle: pre = !pre if pre { @@ -79,3 +91,7 @@ func textToHTML(text gemini.Text) string { } return b.String() } + +// Convert a Gemini link to a proxy link +func proxyLink() { +} diff --git a/go.sum b/go.sum @@ -10,6 +10,10 @@ git.sr.ht/~adnano/go-gemini v0.1.6-0.20201105050458-53390dad6bb1 h1:WlUXNngKn9+K git.sr.ht/~adnano/go-gemini v0.1.6-0.20201105050458-53390dad6bb1/go.mod h1:If1VxEWcZDrRt5FeAFnGTcM2Ud1E3BXs3VJ5rnZWKq0= git.sr.ht/~adnano/go-gemini v0.1.8 h1:93DxDNXB0bjnfDhZewf+QsEopfuOMh/I4v7ujoJ6WIs= git.sr.ht/~adnano/go-gemini v0.1.8/go.mod h1:If1VxEWcZDrRt5FeAFnGTcM2Ud1E3BXs3VJ5rnZWKq0= +git.sr.ht/~adnano/go-gemini v0.1.10 h1:enuYuY2pC+1BsP1GE73wLIyhXc4r+Ryx6TUubynmSgo= +git.sr.ht/~adnano/go-gemini v0.1.10/go.mod h1:If1VxEWcZDrRt5FeAFnGTcM2Ud1E3BXs3VJ5rnZWKq0= +git.sr.ht/~sircmpwn/getopt v0.0.0-20201218204720-9961a9c6298f h1:f5axCdaRzGDCihN3o1Lq0ydn0VlkhY+11G0JOyY5qss= +git.sr.ht/~sircmpwn/getopt v0.0.0-20201218204720-9961a9c6298f/go.mod h1:wMEGFFFNuPos7vHmWXfszqImLppbc0wEhh6JBfJIUgw= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= diff --git a/http.go b/http.go @@ -750,6 +750,8 @@ func runHTTPServer() { wrapped := handlers.CustomLoggingHandler(log.Writer(), handlers.RecoveryHandler()(serveMux), logFormatter) // handle user files based on subdomain + // also routes to proxy + serveMux.HandleFunc("proxy."+hostname+"/", proxyGemini) // eg. proxy.flounder.online serveMux.HandleFunc("/", userFile) // login+register functions srv := &http.Server{ diff --git a/proxy.go b/proxy.go @@ -0,0 +1,123 @@ +// Copied from https://git.sr.ht/~sircmpwn/kineto/tree/master/item/main.go +package main + +import ( + "fmt" + "html/template" + "io" + "mime" + "net/http" + "net/url" + "strings" + "time" + + "git.sr.ht/~adnano/go-gemini" +) + +func proxyGemini(w http.ResponseWriter, r *http.Request) { + if r.Method != "GET" { + w.WriteHeader(http.StatusMethodNotAllowed) + w.Write([]byte("404 Not found")) + return + } + path := strings.SplitN(r.URL.Path, "/", 3) + req := gemini.Request{} + var err error + req.Host = path[1] + if len(path) > 2 { + req.URL, err = url.Parse(fmt.Sprintf("gemini://%s/%s", path[1], path[2])) + } else { + req.URL, err = url.Parse(fmt.Sprintf("gemini://%s", path[1])) + } + client := gemini.Client{ + Timeout: 60 * time.Second, + InsecureSkipTrust: true, + } + fmt.Println(req) + + if h := (url.URL{Host: req.Host}); h.Port() == "" { + req.Host += ":1965" + } + + resp, err := client.Do(&req) + if err != nil { + w.WriteHeader(http.StatusBadGateway) + fmt.Fprintf(w, "Gateway error: %v", err) + return + } + defer resp.Body.Close() + + switch resp.Status { + case 10, 11: + // TODO accept input + w.WriteHeader(http.StatusInternalServerError) + return + case 20: + break // OK + case 30, 31: + to, err := url.Parse(resp.Meta) + if err != nil { + w.WriteHeader(http.StatusBadGateway) + w.Write([]byte(fmt.Sprintf("Gateway error: bad redirect %v", err))) + } + next := req.URL.ResolveReference(to) + if next.Scheme != "gemini" { + w.WriteHeader(http.StatusOK) + w.Write([]byte(fmt.Sprintf("This page is redirecting you to %s", next.String()))) + return + } + next.Host = r.URL.Host + next.Scheme = r.URL.Scheme + w.Header().Add("Location", next.String()) + w.WriteHeader(http.StatusFound) + w.Write([]byte("Redirecting to " + next.String())) + return + case 40, 41, 42, 43, 44: + w.WriteHeader(http.StatusServiceUnavailable) + fmt.Fprintf(w, "The remote server returned %d: %s", resp.Status, resp.Meta) + return + case 50, 51: + w.WriteHeader(http.StatusNotFound) + fmt.Fprintf(w, "The remote server returned %d: %s", resp.Status, resp.Meta) + return + case 52, 53, 59: + w.WriteHeader(http.StatusServiceUnavailable) + fmt.Fprintf(w, "The remote server returned %d: %s", resp.Status, resp.Meta) + return + default: + w.WriteHeader(http.StatusNotImplemented) + fmt.Fprintf(w, "Proxy does not understand Gemini response status %d", resp.Status) + return + } + + m, _, err := mime.ParseMediaType(resp.Meta) + if err != nil { + w.WriteHeader(http.StatusBadGateway) + w.Write([]byte(fmt.Sprintf("Gateway error: %d %s: %v", + resp.Status, resp.Meta, err))) + return + } + + if m != "text/gemini" { + w.Header().Add("Content-Type", resp.Meta) + io.Copy(w, resp.Body) + return + } + + w.Header().Add("Content-Type", "text/html") + + htmlString := textToHTML(gemini.ParseText(resp.Body)) + data := struct { + SiteBody template.HTML + Favicon string + PageTitle string + URI string + }{template.HTML(htmlString), "", req.URL.String(), req.URL.String()} + + err = t.ExecuteTemplate(w, "user_page.html", data) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + fmt.Fprintf(w, "%v", err) + return + } +}