Add SaneEOL configuration option

This commit is contained in:
Trevor Slocum 2020-11-05 12:57:28 -08:00
parent 0b744eba7e
commit e3aeb18053
6 changed files with 41 additions and 14 deletions

View file

@ -119,16 +119,31 @@ specified address or path.
A `Root` attribute must also be specified to use `FastCGI`.
## End-of-line indicator
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.
In anticipation of an improvement to the Gemini specification, administrators
may configure twins to send standard `\n` (LF) line endings by setting
`SaneEOL` to `true`.
References:
[1](https://lists.orbitalfox.eu/archives/gemini/2019/000131.html)
[2](https://lists.orbitalfox.eu/archives/gemini/2020/000756.html)
[3](https://lists.orbitalfox.eu/archives/gemini/2020/001339.html)
[4](https://lists.orbitalfox.eu/archives/gemini/2020/003065.html)
# Example config.yaml
```yaml
# Address to listen on
listen: :1965
# TLS certificates
certificates:
-
# Hosts and paths to serve
hosts:
gemini.rocks:

View file

@ -12,7 +12,7 @@ This page is also available at [gemini://twins.rocketnine.space](gemini://twins.
## Features
- Serve static files
- Directory listing (when enabled)
- List files and directories (when enabled)
- Reverse proxy requests
- TCP
- [FastCGI](https://en.wikipedia.org/wiki/FastCGI)

View file

@ -59,6 +59,8 @@ type serverConfig struct {
DisableSize bool
SaneEOL bool
hostname string
port int
fcgiPools map[string]gofast.ConnFactory
@ -91,6 +93,12 @@ func readconfig(configPath string) error {
log.Fatal("listen address must be specified")
}
if config.SaneEOL {
newLine = "\n"
} else {
newLine = "\r\n"
}
split := strings.Split(config.Listen, ":")
if len(split) != 2 {
config.hostname = config.Listen

View file

@ -52,7 +52,7 @@ func serveDirList(c net.Conn, request *url.URL, dirPath string) {
writeHeader(c, statusSuccess, "text/gemini; charset=utf-8")
fmt.Fprintf(c, "# %s\r\n", request.Path)
fmt.Fprintf(c, "# %s%s", request.Path, newLine)
if numDirs == 1 {
c.Write([]byte("1 directory"))
} else {
@ -64,10 +64,10 @@ func serveDirList(c net.Conn, request *url.URL, dirPath string) {
} else {
fmt.Fprintf(c, "%d files", numFiles)
}
c.Write([]byte("\r\n\n"))
c.Write([]byte(newLine + newLine))
if request.Path != "/" {
c.Write([]byte("=> ../ ../\r\n\r\n"))
c.Write([]byte("=> ../ ../" + newLine + newLine))
}
for _, info := range files {
@ -78,10 +78,10 @@ func serveDirList(c net.Conn, request *url.URL, dirPath string) {
filePath += "/"
}
c.Write([]byte("=> " + fileName + " " + filePath + "\r\n"))
c.Write([]byte("=> " + fileName + " " + filePath + newLine))
if info.IsDir() || info.Mode()&os.ModeSymlink != 0 {
c.Write([]byte("\r\n"))
c.Write([]byte(newLine))
continue
}
@ -89,7 +89,7 @@ func serveDirList(c net.Conn, request *url.URL, dirPath string) {
if !info.ModTime().IsZero() {
modified = info.ModTime().Format("2006-01-02 3:04 PM")
}
c.Write([]byte(modified + " - " + formatFileSize(info.Size()) + "\r\n\r\n"))
c.Write([]byte(modified + " - " + formatFileSize(info.Size()) + newLine + newLine))
}
}

View file

@ -28,7 +28,7 @@ func serveProxy(c net.Conn, request *url.URL, proxyURL string) {
// Forward request
proxy.Write([]byte(request.String()))
proxy.Write([]byte("\r\n"))
proxy.Write([]byte(newLine))
// Forward response
io.Copy(c, proxy)

View file

@ -47,8 +47,10 @@ const (
var slashesRegexp = regexp.MustCompile(`[^\\]\/`)
var newLine = "\r\n"
func writeHeader(c net.Conn, code int, meta string) {
fmt.Fprintf(c, "%d %s\r\n", code, meta)
fmt.Fprintf(c, "%d %s%s", code, meta, newLine)
if verbose {
log.Printf("< %d %s\n", code, meta)
@ -203,7 +205,9 @@ func servePath(c *tls.Conn, request *url.URL, serve *pathConfig) {
func serveConn(c *tls.Conn) {
var requestData string
scanner := bufio.NewScanner(c)
if !config.SaneEOL {
scanner.Split(scanCRLF)
}
if scanner.Scan() {
requestData = scanner.Text()
}