diff --git a/CONFIGURATION.md b/CONFIGURATION.md index c264fad..35e188d 100644 --- a/CONFIGURATION.md +++ b/CONFIGURATION.md @@ -18,6 +18,11 @@ Address to listen for connections on in the format of `interface:port`. `:1965` +## Types + +Content types may be defined by file extension. When a type is not defined for +the requested file extension, content type is detected automatically. + ## Hosts Hosts are defined by their hostname followed by one or more paths to serve. @@ -181,6 +186,10 @@ References: # Address to listen on listen: :1965 +# Custom content types +types: + .json: application/json; charset=UTF-8 + # Hosts and paths to serve hosts: default: # Default host configuration diff --git a/config.go b/config.go index 018ddc4..87a6e8e 100644 --- a/config.go +++ b/config.go @@ -68,6 +68,8 @@ type hostConfig struct { type serverConfig struct { Listen string + Types map[string]string + Hosts map[string]*hostConfig DisableSize bool @@ -108,6 +110,10 @@ func readconfig(configPath string) error { log.Fatal("listen address must be specified") } + if config.Types == nil { + config.Types = make(map[string]string) + } + if config.SaneEOL { newLine = "\n" } else { @@ -126,6 +132,20 @@ func readconfig(configPath string) error { } } + // Default content types + if config.Types[".htm"] == "" { + config.Types[".htm"] = htmlType + } + if config.Types[".html"] == "" { + config.Types[".html"] = htmlType + } + if config.Types[".gmi"] == "" { + config.Types[".gmi"] = geminiType + } + if config.Types[".gemini"] == "" { + config.Types[".gemini"] = geminiType + } + defaultHost := config.Hosts["default"] delete(config.Hosts, "default") diff --git a/serve_file.go b/serve_file.go index 2771d22..9dd738b 100644 --- a/serve_file.go +++ b/serve_file.go @@ -8,7 +8,6 @@ import ( "os" "path/filepath" "sort" - "strings" "github.com/h2non/filetype" ) @@ -98,33 +97,35 @@ func serveFile(c net.Conn, serve *pathConfig, filePath string) { file, _ := os.Open(filePath) defer file.Close() - // Read file header - buf := make([]byte, 261) - 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 { + // Read content type + var ( + buf = make([]byte, 261) + n int + ) + contentType := config.Types[filepath.Ext(filePath)] + if contentType == "" { + n, _ = file.Read(buf) + kind, err := filetype.Match(buf[:n]) + if err == nil && kind != filetype.Unknown && kind.MIME.Value != "" { contentType = kind.MIME.Value + } else { + contentType = plainType } } - if contentType == "" { - contentType = geminiType - } + + // Read file size size := int64(-1) info, err := file.Stat() if err == nil { size = info.Size() } + + // Write response header writeSuccess(c, serve, contentType, size) - // Write body - c.Write(buf[:n]) + // Write file contents + if n > 0 { + c.Write(buf[:n]) + } io.Copy(c, file) } diff --git a/server.go b/server.go index 28426c0..6cda9af 100644 --- a/server.go +++ b/server.go @@ -24,7 +24,9 @@ const ( urlMaxLength = 1024 + plainType = "text/plain; charset=utf-8" geminiType = "text/gemini; charset=utf-8" + htmlType = "text/html; charset=utf-8" logTimeFormat = "2006-01-02 15:04:05" )