forked from tslocum/twins
1
0
Fork 0

Support custom content types

This commit is contained in:
Trevor Slocum 2020-11-17 11:31:22 -08:00
parent 3cb855acfa
commit cac10df2f6
4 changed files with 51 additions and 19 deletions

View File

@ -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

View File

@ -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")

View File

@ -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)
}

View File

@ -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"
)