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 var builtinTypesLower = map[string]string{
54 ".avif": "image/avif",
55 ".css": "text/css; charset=utf-8",
56 ".gif": "image/gif",
57 ".htm": "text/html; charset=utf-8",
58 ".html": "text/html; charset=utf-8",
59 ".jpeg": "image/jpeg",
60 ".jpg": "image/jpeg",
61 ".js": "text/javascript; charset=utf-8",
62 ".json": "application/json",
63 ".mjs": "text/javascript; charset=utf-8",
64 ".pdf": "application/pdf",
65 ".png": "image/png",
66 ".svg": "image/svg+xml",
67 ".wasm": "application/wasm",
68 ".webp": "image/webp",
69 ".xml": "text/xml; charset=utf-8",
70 }
71
72 var once sync.Once
73
74 var testInitMime, osInitMime func()
75
76 func initMime() {
77 if fn := testInitMime; fn != nil {
78 fn()
79 } else {
80 setMimeTypes(builtinTypesLower, builtinTypesLower)
81 osInitMime()
82 }
83 }
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104 func TypeByExtension(ext string) string {
105 once.Do(initMime)
106
107
108 if v, ok := mimeTypes.Load(ext); ok {
109 return v.(string)
110 }
111
112
113
114
115 var buf [10]byte
116 lower := buf[:0]
117 const utf8RuneSelf = 0x80
118 for i := 0; i < len(ext); i++ {
119 c := ext[i]
120 if c >= utf8RuneSelf {
121
122 si, _ := mimeTypesLower.Load(strings.ToLower(ext))
123 s, _ := si.(string)
124 return s
125 }
126 if 'A' <= c && c <= 'Z' {
127 lower = append(lower, c+('a'-'A'))
128 } else {
129 lower = append(lower, c)
130 }
131 }
132 si, _ := mimeTypesLower.Load(string(lower))
133 s, _ := si.(string)
134 return s
135 }
136
137
138
139
140
141 func ExtensionsByType(typ string) ([]string, error) {
142 justType, _, err := ParseMediaType(typ)
143 if err != nil {
144 return nil, err
145 }
146
147 once.Do(initMime)
148 s, ok := extensions.Load(justType)
149 if !ok {
150 return nil, nil
151 }
152 ret := append([]string(nil), s.([]string)...)
153 slices.Sort(ret)
154 return ret, nil
155 }
156
157
158
159
160 func AddExtensionType(ext, typ string) error {
161 if !strings.HasPrefix(ext, ".") {
162 return fmt.Errorf("mime: extension %q missing leading dot", ext)
163 }
164 once.Do(initMime)
165 return setExtensionType(ext, typ)
166 }
167
168 func setExtensionType(extension, mimeType string) error {
169 justType, param, err := ParseMediaType(mimeType)
170 if err != nil {
171 return err
172 }
173 if strings.HasPrefix(mimeType, "text/") && param["charset"] == "" {
174 param["charset"] = "utf-8"
175 mimeType = FormatMediaType(mimeType, param)
176 }
177 extLower := strings.ToLower(extension)
178
179 mimeTypes.Store(extension, mimeType)
180 mimeTypesLower.Store(extLower, mimeType)
181
182 extensionsMu.Lock()
183 defer extensionsMu.Unlock()
184 var exts []string
185 if ei, ok := extensions.Load(justType); ok {
186 exts = ei.([]string)
187 }
188 for _, v := range exts {
189 if v == extLower {
190 return nil
191 }
192 }
193 extensions.Store(justType, append(exts, extLower))
194 return nil
195 }
196
View as plain text