From 1e0f8e0aaeaf1bd2ee39c02e922238b641bcf88b Mon Sep 17 00:00:00 2001 From: tjp Date: Mon, 13 Nov 2023 07:25:39 -0700 Subject: refactor contribs to work with a Protocol interface --- internal/filetypes.go | 57 ++++++++++++++++++++++++++++++++++++++++++++++ internal/types/protocol.go | 19 ++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 internal/filetypes.go create mode 100644 internal/types/protocol.go (limited to 'internal') diff --git a/internal/filetypes.go b/internal/filetypes.go new file mode 100644 index 0000000..6824ffc --- /dev/null +++ b/internal/filetypes.go @@ -0,0 +1,57 @@ +package internal + +import ( + "mime" + "os" + "strings" + "unicode/utf8" +) + +func MediaType(fpath string) string { + if strings.HasSuffix(fpath, ".gmi") { + // This may not be present in the listings searched by mime.TypeByExtension, + // so provide a dedicated fast path for it here. + return "text/gemini" + } + + slashIdx := strings.LastIndex(fpath, "/") + dotIdx := strings.LastIndex(fpath[slashIdx+1:], ".") + if dotIdx == -1 { + return "application/octet-stream" + } + ext := fpath[slashIdx+1+dotIdx:] + + mtype := mime.TypeByExtension(ext) + if mtype == "" { + if ContentsAreText(fpath) { + return "text/plain" + } + return "application/octet-stream" + } + return mtype +} + +func ContentsAreText(fpath string) bool { + f, err := os.Open(fpath) + if err != nil { + return false + } + defer func() { _ = f.Close() }() + + var buf [1024]byte + n, err := f.Read(buf[:]) + if err != nil { + return false + } + + for i, c := range string(buf[:n]) { + if i+utf8.UTFMax > n { + // incomplete last char + break + } + if c == 0xFFFD || c < ' ' && c != '\n' && c != '\t' && c != '\f' { + return false + } + } + return true +} diff --git a/internal/types/protocol.go b/internal/types/protocol.go new file mode 100644 index 0000000..7166d8f --- /dev/null +++ b/internal/types/protocol.go @@ -0,0 +1,19 @@ +package types + +import ( + "io" + "net/url" +) + +type ServerProtocol interface { + TemporaryRedirect(*url.URL) *Response + PermanentRedirect(*url.URL) *Response + + TemporaryServerError(error) *Response + PermanentServerError(error) *Response + CGIFailure(error) *Response + + Success(filename string, body io.Reader) *Response + + ParseResponse(io.Reader) (*Response, error) +} -- cgit v1.2.3