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