mirror of
https://code.rocketnine.space/tslocum/twins.git
synced 2024-11-27 11:38:14 +01:00
Add cache attribute
This commit is contained in:
parent
e8af67c813
commit
8d02c6d779
7 changed files with 63 additions and 39 deletions
|
@ -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
|
||||
|
|
|
@ -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
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
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=
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
var contentType string
|
||||
if strings.HasSuffix(filePath, ".html") && strings.HasSuffix(filePath, ".htm") {
|
||||
contentType = "text/html; charset=utf-8"
|
||||
} else if strings.HasSuffix(filePath, ".txt") && strings.HasSuffix(filePath, ".text") {
|
||||
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 {
|
||||
contentType = kind.MIME.Value
|
||||
}
|
||||
}
|
||||
if contentType == "" {
|
||||
contentType = geminiType
|
||||
}
|
||||
size := int64(-1)
|
||||
info, err := file.Stat()
|
||||
if err == nil {
|
||||
size = info.Size()
|
||||
}
|
||||
var mimeType string
|
||||
if strings.HasSuffix(filePath, ".html") && strings.HasSuffix(filePath, ".htm") {
|
||||
mimeType = "text/html; charset=utf-8"
|
||||
} else if strings.HasSuffix(filePath, ".txt") && strings.HasSuffix(filePath, ".text") {
|
||||
mimeType = "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
|
||||
}
|
||||
}
|
||||
if mimeType == "" {
|
||||
mimeType = "text/gemini; charset=utf-8"
|
||||
}
|
||||
var meta string
|
||||
if !config.DisableSize && size >= 0 {
|
||||
meta = fmt.Sprintf("%s; size=%d", mimeType, size)
|
||||
} else {
|
||||
meta = mimeType
|
||||
}
|
||||
writeHeader(c, statusSuccess, meta)
|
||||
writeSuccess(c, serve, contentType, size)
|
||||
|
||||
// Write body
|
||||
c.Write(buf[:n])
|
||||
|
|
31
server.go
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…
Reference in a new issue