From 988a00f1266ca3b9224235c504a1976f06f841cd Mon Sep 17 00:00:00 2001 From: nytpu Date: Mon, 31 May 2021 12:26:59 -0600 Subject: [PATCH] Add -e flag to place a stylesheet externally rather than loading it inline By default, kineto loads a stylesheet given to -s from disk and places it inline with the HTML in a block. This patch adds a -e flag to load a stylesheet externally. When the -e flag is passed with a URI (relative or absolute), the given link is placed in the href of a tag. This helps facilitate caching which can *significantly* reduce request overhead, particularly when the stylesheet is large (>= the size of the page content). The given URI is not validated, and if it is invalid the browser will 404 when requesting it and the page will have no style. --- README.md | 14 +++++++--- main.go | 77 +++++++++++++++++++++++++++++++++---------------------- 2 files changed, 58 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 01197a8..f0851f3 100644 --- a/README.md +++ b/README.md @@ -12,15 +12,23 @@ Geminispace, but it defaults to a specific domain. ``` $ go build -$ ./kineto [-b 127.0.0.1:8080] [-s style.css] gemini://example.org +$ ./kineto [-b 127.0.0.1:8080] [-s style.css] [-e style.css] gemini://example.org ``` The -b argument is optional and allows you to bind to an arbitrary address; by default kineto will bind to `:8080`. You should set up some external reverse proxy like nginx to forward traffic to this port and add TLS. -The -s argument is optional and allows you to specify a custom CSS file. By -default kineto will serve its built-in style. +The -s argument is optional and allows you to specify a custom CSS filename. +The given file will be loaded from the local disk and placed in a ` {{- end }} +{{- end }} {{.Title}} {{ $ctx := . -}} @@ -229,10 +233,14 @@ var inputPage = template.Must(template. {{- if .CSS }} +{{- if .ExternalCSS }} + +{{- else }} {{- end }} +{{- end }} {{.Prompt}}
@@ -359,22 +367,24 @@ input:focus { ` type GemtextContext struct { - CSS string - External bool - Lines []gemini.Line - Pre int - Resp *gemini.Response - Title string - Lang string - URL *url.URL - Root *url.URL + CSS string + ExternalCSS bool + External bool + Lines []gemini.Line + Pre int + Resp *gemini.Response + Title string + Lang string + URL *url.URL + Root *url.URL } type InputContext struct { - CSS string - Prompt string - Secret bool - URL *url.URL + CSS string + ExternalCSS bool + Prompt string + Secret bool + URL *url.URL } type GemtextHeading struct { @@ -383,7 +393,7 @@ type GemtextHeading struct { } func proxyGemini(req gemini.Request, external bool, root *url.URL, - w http.ResponseWriter, r *http.Request, css string) { + w http.ResponseWriter, r *http.Request, css string, externalCSS bool) { ctx, cancel := context.WithTimeout(r.Context(), 20*time.Second) defer cancel() @@ -401,10 +411,11 @@ func proxyGemini(req gemini.Request, external bool, root *url.URL, case 10, 11: w.Header().Add("Content-Type", "text/html") err = inputPage.Execute(w, &InputContext{ - CSS: css, - Prompt: resp.Meta, - Secret: resp.Status == 11, - URL: req.URL, + CSS: css, + ExternalCSS: externalCSS, + Prompt: resp.Meta, + Secret: resp.Status == 11, + URL: req.URL, }) if err != nil { w.WriteHeader(http.StatusInternalServerError) @@ -478,13 +489,14 @@ func proxyGemini(req gemini.Request, external bool, root *url.URL, w.Header().Add("Content-Type", "text/html") gemctx := &GemtextContext{ - CSS: css, - External: external, - Resp: resp, - Title: req.URL.Host + " " + req.URL.Path, - Lang: lang, - URL: req.URL, - Root: root, + CSS: css, + ExternalCSS: externalCSS, + External: external, + Resp: resp, + Title: req.URL.Host + " " + req.URL.Path, + Lang: lang, + URL: req.URL, + Root: root, } var title bool @@ -508,11 +520,12 @@ func proxyGemini(req gemini.Request, external bool, root *url.URL, func main() { var ( - bind string = ":8080" - css string = defaultCSS + bind string = ":8080" + css string = defaultCSS + external bool = false ) - opts, optind, err := getopt.Getopts(os.Args, "b:c:s:") + opts, optind, err := getopt.Getopts(os.Args, "b:c:s:e:") if err != nil { log.Fatal(err) } @@ -521,12 +534,16 @@ func main() { case 'b': bind = opt.Value case 's': + external = false cssContent, err := ioutil.ReadFile(opt.Value) if err == nil { css = string(cssContent) } else { log.Fatalf("Error opening custom CSS from '%s': %v", opt.Value, err) } + case 'e': + external = true + css = opt.Value } } @@ -572,7 +589,7 @@ func main() { req.URL.Host = root.Host req.URL.Path = r.URL.Path req.URL.RawQuery = r.URL.RawQuery - proxyGemini(req, false, root, w, r, css) + proxyGemini(req, false, root, w, r, css, external) })) http.Handle("/x/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -606,7 +623,7 @@ func main() { req.URL.Path = "/" + path[3] req.URL.RawQuery = r.URL.RawQuery log.Printf("%s (external) %s%s", r.Method, r.URL.Host, r.URL.Path) - proxyGemini(req, true, root, w, r, css) + proxyGemini(req, true, root, w, r, css, external) })) log.Printf("HTTP server listening on %s", bind)