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 <style>...</style> 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 <link rel="stylesheet"...> 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.
This commit is contained in:
parent
edd4fe31f1
commit
988a00f126
2 changed files with 58 additions and 33 deletions
14
README.md
14
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 `<style>`
|
||||
block. By default kineto will serve its built-in style.
|
||||
|
||||
The -e argument is optional and allows you to specify a custom CSS URL. If
|
||||
provided, the style.css given will be treated as a link to be put in the href
|
||||
of a `<link rel="stylesheet"...>` instead of being placed inline with the body
|
||||
in a `<style>` block like with the -s flag. The given stylesheet can be a
|
||||
relative link, for instance `-e /main.css` will serve `main.css` from the root
|
||||
of the proxied Gemini capsule.
|
||||
|
||||
## "kineto"?
|
||||
|
||||
|
|
77
main.go
77
main.go
|
@ -127,10 +127,14 @@ var gemtextPage = template.Must(template.
|
|||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
{{- if .CSS }}
|
||||
{{- if .ExternalCSS }}
|
||||
<link rel="stylesheet" type="text/css" href="{{.CSS | safeCSS}}">
|
||||
{{- else }}
|
||||
<style>
|
||||
{{.CSS | safeCSS}}
|
||||
</style>
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
<title>{{.Title}}</title>
|
||||
<article{{if .Lang}} lang="{{.Lang}}"{{end}}>
|
||||
{{ $ctx := . -}}
|
||||
|
@ -229,10 +233,14 @@ var inputPage = template.Must(template.
|
|||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
{{- if .CSS }}
|
||||
{{- if .ExternalCSS }}
|
||||
<link rel="stylesheet" type="text/css" href="{{.CSS | safeCSS}}">
|
||||
{{- else }}
|
||||
<style>
|
||||
{{.CSS | safeCSS}}
|
||||
</style>
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
<title>{{.Prompt}}</title>
|
||||
<form method="POST">
|
||||
<label for="input">{{.Prompt}}</label>
|
||||
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue