Source file src/pkg/mime/type.go
1
2
3
4
5
6 package mime
7
8 import (
9 "fmt"
10 "strings"
11 "sync"
12 )
13
14 var (
15 mimeTypes sync.Map
16 mimeTypesLower sync.Map
17
18
19
20 extensionsMu sync.Mutex
21 extensions sync.Map
22 )
23
24 func clearSyncMap(m *sync.Map) {
25 m.Range(func(k, _ interface{}) bool {
26 m.Delete(k)
27 return true
28 })
29 }
30
31
32 func setMimeTypes(lowerExt, mixExt map[string]string) {
33 clearSyncMap(&mimeTypes)
34 clearSyncMap(&mimeTypesLower)
35 clearSyncMap(&extensions)
36
37 for k, v := range lowerExt {
38 mimeTypesLower.Store(k, v)
39 }
40 for k, v := range mixExt {
41 mimeTypes.Store(k, v)
42 }
43
44 extensionsMu.Lock()
45 defer extensionsMu.Unlock()
46 for k, v := range lowerExt {
47 justType, _, err := ParseMediaType(v)
48 if err != nil {
49 panic(err)
50 }
51 var exts []string
52 if ei, ok := extensions.Load(k); ok {
53 exts = ei.([]string)
54 }
55 extensions.Store(justType, append(exts, k))
56 }
57 }
58
59 var builtinTypesLower = map[string]string{
60 ".css": "text/css; charset=utf-8",
61 ".gif": "image/gif",
62 ".htm": "text/html; charset=utf-8",
63 ".html": "text/html; charset=utf-8",
64 ".jpeg": "image/jpeg",
65 ".jpg": "image/jpeg",
66 ".js": "application/javascript",
67 ".mjs": "application/javascript",
68 ".pdf": "application/pdf",
69 ".png": "image/png",
70 ".svg": "image/svg+xml",
71 ".wasm": "application/wasm",
72 ".webp": "image/webp",
73 ".xml": "text/xml; charset=utf-8",
74 }
75
76 var once sync.Once
77
78 var testInitMime, osInitMime func()
79
80 func initMime() {
81 if fn := testInitMime; fn != nil {
82 fn()
83 } else {
84 setMimeTypes(builtinTypesLower, builtinTypesLower)
85 osInitMime()
86 }
87 }
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106 func TypeByExtension(ext string) string {
107 once.Do(initMime)
108
109
110 if v, ok := mimeTypes.Load(ext); ok {
111 return v.(string)
112 }
113
114
115
116
117 var buf [10]byte
118 lower := buf[:0]
119 const utf8RuneSelf = 0x80
120 for i := 0; i < len(ext); i++ {
121 c := ext[i]
122 if c >= utf8RuneSelf {
123
124 si, _ := mimeTypesLower.Load(strings.ToLower(ext))
125 s, _ := si.(string)
126 return s
127 }
128 if 'A' <= c && c <= 'Z' {
129 lower = append(lower, c+('a'-'A'))
130 } else {
131 lower = append(lower, c)
132 }
133 }
134 si, _ := mimeTypesLower.Load(string(lower))
135 s, _ := si.(string)
136 return s
137 }
138
139
140
141
142
143 func ExtensionsByType(typ string) ([]string, error) {
144 justType, _, err := ParseMediaType(typ)
145 if err != nil {
146 return nil, err
147 }
148
149 once.Do(initMime)
150 s, ok := extensions.Load(justType)
151 if !ok {
152 return nil, nil
153 }
154 return append([]string{}, s.([]string)...), 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