all repos — flounder @ d8647e6a087a23309305a5a0c7075c7e7ddf9ed4

A small site builder for the Gemini protocol

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}