nandi/oripublic Fork 0
76d62ac0da1600e1c208017584c3fa22e54feaa3
Commits
Clone
git clone https://git.rickub.com/nandi/ori.git
git clone ssh://git@rickub.com/nandi/ori.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

forked from bots-garden/ori

raw.go · 80 lines · 2.2 KBGo Blame HistoryRaw
✨ Workspace panel, selectors, previews, desktop app, sandbox template, resizable file tree, light/dark theme 76d62ac k33g yesterday1package files
2
3import (
4 "mime"
5 "net/http"
6 "os"
7 "path/filepath"
8 "strings"
9)
10
11// imageTypes pins the Content-Type of the image formats the preview pane
12// renders, independently of the host's mime database (which may lack
13// entries such as .webp or .avif, or spell .ico differently).
14var imageTypes = map[string]string{
15 ".avif": "image/avif",
16 ".bmp": "image/bmp",
17 ".gif": "image/gif",
18 ".ico": "image/x-icon",
19 ".jpeg": "image/jpeg",
20 ".jpg": "image/jpeg",
21 ".png": "image/png",
22 ".svg": "image/svg+xml",
23 ".webp": "image/webp",
24}
25
26// ContentTypeFor returns the Content-Type the raw endpoint serves a file
27// under: pinned image types first, then the host mime database, and a
28// content sniff of the first bytes as a last resort.
29//
30// Example:
31//
32// files.ContentTypeFor("logo.webp", nil) // "image/webp"
33func ContentTypeFor(path string, head []byte) string {
34 ext := strings.ToLower(filepath.Ext(path))
35 if typ, ok := imageTypes[ext]; ok {
36 return typ
37 }
38 if typ := mime.TypeByExtension(ext); typ != "" {
39 return typ
40 }
41 return http.DetectContentType(head)
42}
43
44// rawFile streams a file's bytes as-is, for the browser to render (images)
45// or download. Unlike readFile it has no size cap and no text requirement:
46// http.ServeContent streams and honours Range requests.
47func (s *Service) rawFile(w http.ResponseWriter, r *http.Request) {
48 if r.URL.Query().Get("path") == "" {
49 writeError(w, http.StatusBadRequest, "path query parameter is required")
50 return
51 }
52 path := s.resolve(r.URL.Query().Get("path"))
53 info, err := os.Stat(path)
54 if err != nil {
55 writeFSError(w, err)
56 return
57 }
58 if info.IsDir() {
59 writeError(w, http.StatusBadRequest, "path is a directory")
60 return
61 }
62 file, err := os.Open(path)
63 if err != nil {
64 writeFSError(w, err)
65 return
66 }
67 // Read-only handle: a Close error carries no information the client
68 // could act on, and the response headers are already committed.
69 defer func() { _ = file.Close() }()
70
71 head := make([]byte, 512)
72 n, _ := file.Read(head)
73 if _, err := file.Seek(0, 0); err != nil {
74 writeFSError(w, err)
75 return
76 }
77 w.Header().Set("Content-Type", ContentTypeFor(path, head[:n]))
78 w.Header().Set("X-Content-Type-Options", "nosniff")
79 http.ServeContent(w, r, info.Name(), info.ModTime(), file)
80}