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`. 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 # Example config.yaml
```yaml ```yaml
# Address to listen on # Address to listen on
listen: :1965 listen: :1965
# TLS certificates
certificates:
-
# Hosts and paths to serve # Hosts and paths to serve
hosts: hosts:
gemini.rocks: gemini.rocks:

View file

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

View file

@ -59,6 +59,8 @@ type serverConfig struct {
DisableSize bool DisableSize bool
SaneEOL bool
hostname string hostname string
port int port int
fcgiPools map[string]gofast.ConnFactory fcgiPools map[string]gofast.ConnFactory
@ -91,6 +93,12 @@ func readconfig(configPath string) error {
log.Fatal("listen address must be specified") log.Fatal("listen address must be specified")
} }
if config.SaneEOL {
newLine = "\n"
} else {
newLine = "\r\n"
}
split := strings.Split(config.Listen, ":") split := strings.Split(config.Listen, ":")
if len(split) != 2 { if len(split) != 2 {
config.hostname = config.Listen 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") 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 { if numDirs == 1 {
c.Write([]byte("1 directory")) c.Write([]byte("1 directory"))
} else { } else {
@ -64,10 +64,10 @@ func serveDirList(c net.Conn, request *url.URL, dirPath string) {
} else { } else {
fmt.Fprintf(c, "%d files", numFiles) fmt.Fprintf(c, "%d files", numFiles)
} }
c.Write([]byte("\r\n\n")) c.Write([]byte(newLine + newLine))
if request.Path != "/" { if request.Path != "/" {
c.Write([]byte("=> ../ ../\r\n\r\n")) c.Write([]byte("=> ../ ../" + newLine + newLine))
} }
for _, info := range files { for _, info := range files {
@ -78,10 +78,10 @@ func serveDirList(c net.Conn, request *url.URL, dirPath string) {
filePath += "/" filePath += "/"
} }
c.Write([]byte("=> " + fileName + " " + filePath + "\r\n")) c.Write([]byte("=> " + fileName + " " + filePath + newLine))
if info.IsDir() || info.Mode()&os.ModeSymlink != 0 { if info.IsDir() || info.Mode()&os.ModeSymlink != 0 {
c.Write([]byte("\r\n")) c.Write([]byte(newLine))
continue continue
} }
@ -89,7 +89,7 @@ func serveDirList(c net.Conn, request *url.URL, dirPath string) {
if !info.ModTime().IsZero() { if !info.ModTime().IsZero() {
modified = info.ModTime().Format("2006-01-02 3:04 PM") 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 // Forward request
proxy.Write([]byte(request.String())) proxy.Write([]byte(request.String()))
proxy.Write([]byte("\r\n")) proxy.Write([]byte(newLine))
// Forward response // Forward response
io.Copy(c, proxy) io.Copy(c, proxy)

View file

@ -47,8 +47,10 @@ const (
var slashesRegexp = regexp.MustCompile(`[^\\]\/`) var slashesRegexp = regexp.MustCompile(`[^\\]\/`)
var newLine = "\r\n"
func writeHeader(c net.Conn, code int, meta string) { 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 { if verbose {
log.Printf("< %d %s\n", code, meta) 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) { func serveConn(c *tls.Conn) {
var requestData string var requestData string
scanner := bufio.NewScanner(c) scanner := bufio.NewScanner(c)
scanner.Split(scanCRLF) if !config.SaneEOL {
scanner.Split(scanCRLF)
}
if scanner.Scan() { if scanner.Scan() {
requestData = scanner.Text() requestData = scanner.Text()
} }