diff options
| author | Drew DeVault <[email protected]> | 2020-11-08 10:24:27 -0500 |
|---|---|---|
| committer | Drew DeVault <[email protected]> | 2020-11-08 10:27:29 -0500 |
| commit | 6531cf3930635dfcc94052b477c6351498552169 (patch) | |
| tree | 75a9f063848c3213206215a05e4fd5e001509f46 /main.go | |
| parent | Fix response status switch statement (diff) | |
| download | capybara-6531cf3930635dfcc94052b477c6351498552169.tar.xz capybara-6531cf3930635dfcc94052b477c6351498552169.zip | |
Implement input and redirect responses
Diffstat (limited to 'main.go')
| -rw-r--r-- | main.go | 96 |
1 files changed, 93 insertions, 3 deletions
@@ -215,6 +215,33 @@ var gemtextPage = template.Must(template. </details> `)) +var inputPage = template.Must(template. + New("input"). + Funcs(template.FuncMap{ + "safeCSS": func(s string) template.CSS { + return template.CSS(s) + }, + }). + Parse(`<!doctype html> +<html> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1" /> +{{- if .CSS }} +<style> +{{.CSS | safeCSS}} +</style> +{{- end }} +<title>{{.Prompt}}</title> +<form> + <label for="input">{{.Prompt}}</label> + {{ if .Secret }} + <input type="password" id="input" name="q" /> + {{ else }} + <input type="text" id="input" name="q" /> + {{ end }} +</form> +`)) + // TODO: let user customize this const defaultCSS = `html { font-family: sans-serif; @@ -306,6 +333,27 @@ dl dt:not(:first-child) { color: #333399; } } + +label { + display: block; + font-weight: bold; + margin-bottom: 0.5rem; +} + +input { + display: block; + border: 1px solid #888; + padding: .375rem; + line-height: 1.25rem; + transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out; + width: 100%; +} + +input:focus { + outline: 0; + border-color: #80bdff; + box-shadow: 0 0 0 0.2rem rgba(0,123,255,.25); +} ` type GemtextContext struct { @@ -319,6 +367,13 @@ type GemtextContext struct { Root *url.URL } +type InputContext struct { + CSS string + Prompt string + Secret bool + URL *url.URL +} + type GemtextHeading struct { Level int Text string @@ -344,11 +399,41 @@ func proxyGemini(req gemini.Request, external bool, root *url.URL, defer resp.Body.Close() switch resp.Status { + case 10, 11: + w.Header().Add("Content-Type", "text/html") + err = inputPage.Execute(w, &InputContext{ + CSS: defaultCSS, + Prompt: resp.Meta, + Secret: resp.Status == 11, + URL: req.URL, + }) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(fmt.Sprintf("%v", err))) + } + return case 20: break // OK case 30, 31: - w.WriteHeader(http.StatusNotImplemented) - fmt.Fprintf(w, "This URL redirects to %s", resp.Meta) + 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 + if external { + next.Path = fmt.Sprintf("/x/%s/%s", next.Host, next.Path) + } + 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) @@ -372,7 +457,8 @@ func proxyGemini(req gemini.Request, external bool, root *url.URL, m, _, err := mime.ParseMediaType(resp.Meta) if err != nil { w.WriteHeader(http.StatusBadGateway) - fmt.Fprintf(w, "Gateway error: %v", err) + w.Write([]byte(fmt.Sprintf("Gateway error: %d %s: %v", + resp.Status, resp.Meta, err))) return } @@ -456,6 +542,10 @@ func main() { req.URL.Host = root.Host req.URL.Path = r.URL.Path req.Host = root.Host + q := r.URL.Query() + if x, ok := q["q"]; ok { + req.URL.RawQuery = x[0] + } proxyGemini(req, false, root, w, r) })) |