Source file
src/mime/type.go
Documentation: mime
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 ".jpg": "image/jpeg",
65 ".js": "application/x-javascript",
66 ".pdf": "application/pdf",
67 ".png": "image/png",
68 ".svg": "image/svg+xml",
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 func TypeByExtension(ext string) string {
103 once.Do(initMime)
104
105
106 if v, ok := mimeTypes.Load(ext); ok {
107 return v.(string)
108 }
109
110
111
112
113 var buf [10]byte
114 lower := buf[:0]
115 const utf8RuneSelf = 0x80
116 for i := 0; i < len(ext); i++ {
117 c := ext[i]
118 if c >= utf8RuneSelf {
119
120 si, _ := mimeTypesLower.Load(strings.ToLower(ext))
121 s, _ := si.(string)
122 return s
123 }
124 if 'A' <= c && c <= 'Z' {
125 lower = append(lower, c+('a'-'A'))
126 } else {
127 lower = append(lower, c)
128 }
129 }
130 si, _ := mimeTypesLower.Load(string(lower))
131 s, _ := si.(string)
132 return s
133 }
134
135
136
137
138
139 func ExtensionsByType(typ string) ([]string, error) {
140 justType, _, err := ParseMediaType(typ)
141 if err != nil {
142 return nil, err
143 }
144
145 once.Do(initMime)
146 s, ok := extensions.Load(justType)
147 if !ok {
148 return nil, nil
149 }
150 return append([]string{}, s.([]string)...), nil
151 }
152
153
154
155
156 func AddExtensionType(ext, typ string) error {
157 if !strings.HasPrefix(ext, ".") {
158 return fmt.Errorf("mime: extension %q missing leading dot", ext)
159 }
160 once.Do(initMime)
161 return setExtensionType(ext, typ)
162 }
163
164 func setExtensionType(extension, mimeType string) error {
165 justType, param, err := ParseMediaType(mimeType)
166 if err != nil {
167 return err
168 }
169 if strings.HasPrefix(mimeType, "text/") && param["charset"] == "" {
170 param["charset"] = "utf-8"
171 mimeType = FormatMediaType(mimeType, param)
172 }
173 extLower := strings.ToLower(extension)
174
175 mimeTypes.Store(extension, mimeType)
176 mimeTypesLower.Store(extLower, mimeType)
177
178 extensionsMu.Lock()
179 defer extensionsMu.Unlock()
180 var exts []string
181 if ei, ok := extensions.Load(justType); ok {
182 exts = ei.([]string)
183 }
184 for _, v := range exts {
185 if v == extLower {
186 return nil
187 }
188 }
189 extensions.Store(justType, append(exts, extLower))
190 return nil
191 }
192
View as plain text