Source file
src/mime/type.go
1
2
3
4
5
6 package mime
7
8 import (
9 "fmt"
10 "slices"
11 "strings"
12 "sync"
13 )
14
15 var (
16 mimeTypes sync.Map
17 mimeTypesLower sync.Map
18
19
20
21 extensionsMu sync.Mutex
22 extensions sync.Map
23 )
24
25
26 func setMimeTypes(lowerExt, mixExt map[string]string) {
27 mimeTypes.Clear()
28 mimeTypesLower.Clear()
29 extensions.Clear()
30
31 for k, v := range lowerExt {
32 mimeTypesLower.Store(k, v)
33 }
34 for k, v := range mixExt {
35 mimeTypes.Store(k, v)
36 }
37
38 extensionsMu.Lock()
39 defer extensionsMu.Unlock()
40 for k, v := range lowerExt {
41 justType, _, err := ParseMediaType(v)
42 if err != nil {
43 panic(err)
44 }
45 var exts []string
46 if ei, ok := extensions.Load(justType); ok {
47 exts = ei.([]string)
48 }
49 extensions.Store(justType, append(exts, k))
50 }
51 }
52
53
54
55
56
57
58
59
60
61
62
63
64 var builtinTypesLower = map[string]string{
65 ".ai": "application/postscript",
66 ".apk": "application/vnd.android.package-archive",
67 ".apng": "image/apng",
68 ".avif": "image/avif",
69 ".bin": "application/octet-stream",
70 ".bmp": "image/bmp",
71 ".com": "application/octet-stream",
72 ".css": "text/css; charset=utf-8",
73 ".csv": "text/csv; charset=utf-8",
74 ".doc": "application/msword",
75 ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
76 ".ehtml": "text/html; charset=utf-8",
77 ".eml": "message/rfc822",
78 ".eps": "application/postscript",
79 ".exe": "application/octet-stream",
80 ".flac": "audio/flac",
81 ".gif": "image/gif",
82 ".gz": "application/gzip",
83 ".htm": "text/html; charset=utf-8",
84 ".html": "text/html; charset=utf-8",
85 ".ico": "image/vnd.microsoft.icon",
86 ".ics": "text/calendar; charset=utf-8",
87 ".jfif": "image/jpeg",
88 ".jpeg": "image/jpeg",
89 ".jpg": "image/jpeg",
90 ".js": "text/javascript; charset=utf-8",
91 ".json": "application/json",
92 ".m4a": "audio/mp4",
93 ".mjs": "text/javascript; charset=utf-8",
94 ".mp3": "audio/mpeg",
95 ".mp4": "video/mp4",
96 ".oga": "audio/ogg",
97 ".ogg": "audio/ogg",
98 ".ogv": "video/ogg",
99 ".opus": "audio/ogg",
100 ".pdf": "application/pdf",
101 ".pjp": "image/jpeg",
102 ".pjpeg": "image/jpeg",
103 ".png": "image/png",
104 ".ppt": "application/vnd.ms-powerpoint",
105 ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
106 ".ps": "application/postscript",
107 ".rdf": "application/rdf+xml",
108 ".rtf": "application/rtf",
109 ".shtml": "text/html; charset=utf-8",
110 ".svg": "image/svg+xml",
111 ".text": "text/plain; charset=utf-8",
112 ".tif": "image/tiff",
113 ".tiff": "image/tiff",
114 ".txt": "text/plain; charset=utf-8",
115 ".vtt": "text/vtt; charset=utf-8",
116 ".wasm": "application/wasm",
117 ".wav": "audio/wav",
118 ".webm": "audio/webm",
119 ".webp": "image/webp",
120 ".xbl": "text/xml; charset=utf-8",
121 ".xbm": "image/x-xbitmap",
122 ".xht": "application/xhtml+xml",
123 ".xhtml": "application/xhtml+xml",
124 ".xls": "application/vnd.ms-excel",
125 ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
126 ".xml": "text/xml; charset=utf-8",
127 ".xsl": "text/xml; charset=utf-8",
128 ".zip": "application/zip",
129 }
130
131 var once sync.Once
132
133 var testInitMime, osInitMime func()
134
135 func initMime() {
136 if fn := testInitMime; fn != nil {
137 fn()
138 } else {
139 setMimeTypes(builtinTypesLower, builtinTypesLower)
140 osInitMime()
141 }
142 }
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164 func TypeByExtension(ext string) string {
165 once.Do(initMime)
166
167
168 if v, ok := mimeTypes.Load(ext); ok {
169 return v.(string)
170 }
171
172
173
174
175 var buf [10]byte
176 lower := buf[:0]
177 const utf8RuneSelf = 0x80
178 for i := 0; i < len(ext); i++ {
179 c := ext[i]
180 if c >= utf8RuneSelf {
181
182 si, _ := mimeTypesLower.Load(strings.ToLower(ext))
183 s, _ := si.(string)
184 return s
185 }
186 if 'A' <= c && c <= 'Z' {
187 lower = append(lower, c+('a'-'A'))
188 } else {
189 lower = append(lower, c)
190 }
191 }
192 si, _ := mimeTypesLower.Load(string(lower))
193 s, _ := si.(string)
194 return s
195 }
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214 func ExtensionsByType(typ string) ([]string, error) {
215 justType, _, err := ParseMediaType(typ)
216 if err != nil {
217 return nil, err
218 }
219
220 once.Do(initMime)
221 s, ok := extensions.Load(justType)
222 if !ok {
223 return nil, nil
224 }
225 ret := append([]string(nil), s.([]string)...)
226 slices.Sort(ret)
227 return ret, nil
228 }
229
230
231
232
233 func AddExtensionType(ext, typ string) error {
234 if !strings.HasPrefix(ext, ".") {
235 return fmt.Errorf("mime: extension %q missing leading dot", ext)
236 }
237 once.Do(initMime)
238 return setExtensionType(ext, typ)
239 }
240
241 func setExtensionType(extension, mimeType string) error {
242 justType, param, err := ParseMediaType(mimeType)
243 if err != nil {
244 return err
245 }
246 if strings.HasPrefix(mimeType, "text/") && param["charset"] == "" {
247 param["charset"] = "utf-8"
248 mimeType = FormatMediaType(mimeType, param)
249 }
250 extLower := strings.ToLower(extension)
251
252 mimeTypes.Store(extension, mimeType)
253 mimeTypesLower.Store(extLower, mimeType)
254
255 extensionsMu.Lock()
256 defer extensionsMu.Unlock()
257 var exts []string
258 if ei, ok := extensions.Load(justType); ok {
259 exts = ei.([]string)
260 }
261 for _, v := range exts {
262 if v == extLower {
263 return nil
264 }
265 }
266 extensions.Store(justType, append(exts, extLower))
267 return nil
268 }
269
View as plain text