Browse Source

Add cache attribute

master
Trevor Slocum 2 years ago
parent
commit
8d02c6d779
  1. 16
      CONFIGURATION.md
  2. 3
      config.go
  3. 2
      go.mod
  4. 4
      go.sum
  5. 4
      serve_command.go
  6. 34
      serve_file.go
  7. 31
      server.go

16
CONFIGURATION.md

@ -65,7 +65,8 @@ and the private key file at `certs/live/$DOMAIN/privkey.pem` to twins.
### DisableSize
The size of the response body is included in the media type header by default.
Set this option to `true` to disable this feature.
Set this option to `true` to disable this feature. See [PROPOSALS.md](https://gitlab.com/tslocum/twins/blob/master/PROPOSALS.md)
for more information.
### Path
@ -95,6 +96,12 @@ argument to the command, otherwise user input is passed via standard input.
Any number of attributes may be defined for a path.
##### Cache
Cache duration (in seconds). Set to `0` to disable caching entirely. This is an
out-of-spec feature. See [PROPOSALS.md](https://gitlab.com/tslocum/twins/blob/master/PROPOSALS.md)
for more information.
##### ListDirectory
Directory listing may be enabled by adding `listdirectory: true`.
@ -124,9 +131,8 @@ A `Root` attribute must also be specified to use `FastCGI`.
The Gemini protocol requires `\r\n` (CRLF) as the end-of-line indicator. This
convention is carried over from protocol specifications **first written in the
1970s**. This requirement is antithetic to the spirit of Gemini (to improve
upon the Finger and Gopher protocols) because it unnecessarily tacks on ancient
baggage. This baggage has caused (and continues to cause) increased complexity in
client and server implementations, which naturally gives rise to more bugs.
upon the Finger and Gopher protocols), increasing the complexity of client and
server implementations unnecessarily.
In anticipation of an improvement to the Gemini specification, administrators
may configure twins to send standard `\n` (LF) line endings by setting
@ -157,6 +163,7 @@ hosts:
-
path: /sites
root: /home/geminirocks/data
cache: 604800 # Cache for 1 week
listdirectory: true
-
path: ^/(help|info)$
@ -167,6 +174,7 @@ hosts:
-
path: ^/cmd-example$
command: uname -a
cache: 0 # Do not cache
-
path: /
root: /home/geminirocks/data/home

3
config.go

@ -37,6 +37,9 @@ type pathConfig struct {
// Content type
Type string
// Cache duration
Cache int64 `default:"-1965"`
// FastCGI server address
FastCGI string

2
go.mod

@ -6,6 +6,6 @@ require (
github.com/h2non/filetype v1.1.0
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/yookoala/gofast v0.4.1-0.20201013050739-975113c54107
golang.org/x/tools v0.0.0-20201104193857-22bd85271a8b // indirect
golang.org/x/tools v0.0.0-20201110030525-169ad6d6ecb2 // indirect
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
)

4
go.sum

@ -34,8 +34,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200908211811-12e1bf57a112/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201104193857-22bd85271a8b h1:ILx+nYAUTq4JtXxrXKQ82j7nruEwlXRfUn+kxtsnElg=
golang.org/x/tools v0.0.0-20201104193857-22bd85271a8b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201110030525-169ad6d6ecb2 h1:5GmCe1Mc5HsGGl6E0kOVQRzVp+AgZf4Ffw4DadiVpd4=
golang.org/x/tools v0.0.0-20201110030525-169ad6d6ecb2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

4
serve_command.go

@ -9,7 +9,7 @@ import (
"strings"
)
func serveCommand(c net.Conn, request *url.URL, command []string) {
func serveCommand(c net.Conn, serve *pathConfig, request *url.URL, command []string) {
var args []string
if len(command) > 0 {
args = command[1:]
@ -34,7 +34,7 @@ func serveCommand(c net.Conn, request *url.URL, command []string) {
return
}
writeHeader(c, statusSuccess, "text/gemini; charset=utf-8")
writeSuccess(c, serve, geminiType, int64(buf.Len()))
c.Write(buf.Bytes())
if verbose {

34
serve_file.go

@ -13,7 +13,7 @@ import (
"github.com/h2non/filetype"
)
func serveDirList(c net.Conn, request *url.URL, dirPath string) {
func serveDirList(c net.Conn, serve *pathConfig, request *url.URL, dirPath string) {
var (
files []os.FileInfo
numDirs int
@ -50,7 +50,7 @@ func serveDirList(c net.Conn, request *url.URL, dirPath string) {
return i < j
})
writeHeader(c, statusSuccess, "text/gemini; charset=utf-8")
writeSuccess(c, serve, geminiType, -1)
fmt.Fprintf(c, "# %s%s", request.Path, newLine)
if numDirs == 1 {
@ -93,7 +93,7 @@ func serveDirList(c net.Conn, request *url.URL, dirPath string) {
}
}
func serveFile(c net.Conn, filePath string) {
func serveFile(c net.Conn, serve *pathConfig, filePath string) {
// Open file
file, _ := os.Open(filePath)
defer file.Close()
@ -103,32 +103,26 @@ func serveFile(c net.Conn, filePath string) {
n, _ := file.Read(buf)
// Write response header
size := int64(-1)
info, err := file.Stat()
if err == nil {
size = info.Size()
}
var mimeType string
var contentType string
if strings.HasSuffix(filePath, ".html") && strings.HasSuffix(filePath, ".htm") {
mimeType = "text/html; charset=utf-8"
contentType = "text/html; charset=utf-8"
} else if strings.HasSuffix(filePath, ".txt") && strings.HasSuffix(filePath, ".text") {
mimeType = "text/plain; charset=utf-8"
contentType = "text/plain; charset=utf-8"
} else if !strings.HasSuffix(filePath, ".gmi") && !strings.HasSuffix(filePath, ".gemini") {
kind, _ := filetype.Match(buf[:n])
if kind != filetype.Unknown {
mimeType = kind.MIME.Value
contentType = kind.MIME.Value
}
}
if mimeType == "" {
mimeType = "text/gemini; charset=utf-8"
if contentType == "" {
contentType = geminiType
}
var meta string
if !config.DisableSize && size >= 0 {
meta = fmt.Sprintf("%s; size=%d", mimeType, size)
} else {
meta = mimeType
size := int64(-1)
info, err := file.Stat()
if err == nil {
size = info.Size()
}
writeHeader(c, statusSuccess, meta)
writeSuccess(c, serve, contentType, size)
// Write body
c.Write(buf[:n])

31
server.go

@ -22,6 +22,8 @@ const (
readTimeout = 30 * time.Second
urlMaxLength = 1024
geminiType = "text/gemini; charset=utf-8"
)
const (
@ -74,6 +76,23 @@ func writeStatus(c net.Conn, code int) {
writeHeader(c, code, meta)
}
func writeSuccess(c net.Conn, serve *pathConfig, contentType string, size int64) {
meta := contentType
if serve.Type != "" {
meta = serve.Type
}
if !config.DisableSize && size >= 0 {
meta += fmt.Sprintf("; size=%d", size)
}
if serve.Cache != -1965 {
meta += fmt.Sprintf("; cache=%d", serve.Cache)
}
writeHeader(c, statusSuccess, meta)
}
func scanCRLF(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
@ -138,11 +157,11 @@ func servePath(c *tls.Conn, request *url.URL, serve *pathConfig) {
return
}
contentType := "text/gemini; charset=utf-8"
contentType := geminiType
if serve.Type != "" {
contentType = serve.Type
}
writeHeader(c, statusSuccess, contentType)
writeSuccess(c, serve, contentType, -1)
serveFastCGI(c, config.fcgiPools[serve.FastCGI], request, filePath)
return
@ -151,11 +170,11 @@ func servePath(c *tls.Conn, request *url.URL, serve *pathConfig) {
if requireInput {
newCommand := replaceWithUserInput(serve.cmd, request)
if newCommand != nil {
serveCommand(c, request, newCommand)
serveCommand(c, serve, request, newCommand)
return
}
}
serveCommand(c, request, serve.cmd)
serveCommand(c, serve, request, serve.cmd)
return
}
@ -183,7 +202,7 @@ func servePath(c *tls.Conn, request *url.URL, serve *pathConfig) {
_, err := os.Stat(path.Join(filePath, "index.gemini"))
if err != nil {
if serve.ListDirectory {
serveDirList(c, request, filePath)
serveDirList(c, serve, request, filePath)
return
}
writeStatus(c, statusNotFound)
@ -199,7 +218,7 @@ func servePath(c *tls.Conn, request *url.URL, serve *pathConfig) {
return
}
serveFile(c, filePath)
serveFile(c, serve, filePath)
}
func serveConn(c *tls.Conn) {

Loading…
Cancel
Save