diff --git a/CONFIGURATION.md b/CONFIGURATION.md index 8830737..5d28f71 100644 --- a/CONFIGURATION.md +++ b/CONFIGURATION.md @@ -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: diff --git a/README.md b/README.md index d95399e..57217c7 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/config.go b/config.go index 38c0665..ca68fc1 100644 --- a/config.go +++ b/config.go @@ -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 diff --git a/serve_file.go b/serve_file.go index 8a260af..5fefd85 100644 --- a/serve_file.go +++ b/serve_file.go @@ -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)) } } diff --git a/serve_proxy.go b/serve_proxy.go index 5d8ab32..8bdae7e 100644 --- a/serve_proxy.go +++ b/serve_proxy.go @@ -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) diff --git a/server.go b/server.go index 372bc0f..58fe699 100644 --- a/server.go +++ b/server.go @@ -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) - scanner.Split(scanCRLF) + if !config.SaneEOL { + scanner.Split(scanCRLF) + } if scanner.Scan() { requestData = scanner.Text() }