1 // Copyright 2010 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Package mime implements parts of the MIME spec.
15 mimeTypes sync.Map // map[string]string; ".Z" => "application/x-compress"
16 mimeTypesLower sync.Map // map[string]string; ".z" => "application/x-compress"
18 // extensions maps from MIME type to list of lowercase file
19 // extensions: "image/jpeg" => [".jpg", ".jpeg"]
20 extensionsMu sync.Mutex // Guards stores (but not loads) on extensions.
21 extensions sync.Map // map[string][]string; slice values are append-only.
24 func clearSyncMap(m *sync.Map) {
25 m.Range(func(k, _ interface{}) bool {
31 // setMimeTypes is used by initMime's non-test path, and by tests.
32 func setMimeTypes(lowerExt, mixExt map[string]string) {
33 clearSyncMap(&mimeTypes)
34 clearSyncMap(&mimeTypesLower)
35 clearSyncMap(&extensions)
37 for k, v := range lowerExt {
38 mimeTypesLower.Store(k, v)
40 for k, v := range mixExt {
45 defer extensionsMu.Unlock()
46 for k, v := range lowerExt {
47 justType, _, err := ParseMediaType(v)
52 if ei, ok := extensions.Load(k); ok {
55 extensions.Store(justType, append(exts, k))
59 var builtinTypesLower = map[string]string{
60 ".css": "text/css; charset=utf-8",
62 ".htm": "text/html; charset=utf-8",
63 ".html": "text/html; charset=utf-8",
65 ".js": "application/x-javascript",
66 ".pdf": "application/pdf",
68 ".svg": "image/svg+xml",
69 ".xml": "text/xml; charset=utf-8",
72 var once sync.Once // guards initMime
74 var testInitMime, osInitMime func()
77 if fn := testInitMime; fn != nil {
80 setMimeTypes(builtinTypesLower, builtinTypesLower)
85 // TypeByExtension returns the MIME type associated with the file extension ext.
86 // The extension ext should begin with a leading dot, as in ".html".
87 // When ext has no associated type, TypeByExtension returns "".
89 // Extensions are looked up first case-sensitively, then case-insensitively.
91 // The built-in table is small but on unix it is augmented by the local
92 // system's mime.types file(s) if available under one or more of these
96 // /etc/apache2/mime.types
97 // /etc/apache/mime.types
99 // On Windows, MIME types are extracted from the registry.
101 // Text types have the charset parameter set to "utf-8" by default.
102 func TypeByExtension(ext string) string {
105 // Case-sensitive lookup.
106 if v, ok := mimeTypes.Load(ext); ok {
110 // Case-insensitive lookup.
111 // Optimistically assume a short ASCII extension and be
112 // allocation-free in that case.
115 const utf8RuneSelf = 0x80 // from utf8 package, but not importing it.
116 for i := 0; i < len(ext); i++ {
118 if c >= utf8RuneSelf {
120 si, _ := mimeTypesLower.Load(strings.ToLower(ext))
124 if 'A' <= c && c <= 'Z' {
125 lower = append(lower, c+('a'-'A'))
127 lower = append(lower, c)
130 si, _ := mimeTypesLower.Load(string(lower))
135 // ExtensionsByType returns the extensions known to be associated with the MIME
136 // type typ. The returned extensions will each begin with a leading dot, as in
137 // ".html". When typ has no associated extensions, ExtensionsByType returns an
139 func ExtensionsByType(typ string) ([]string, error) {
140 justType, _, err := ParseMediaType(typ)
146 s, ok := extensions.Load(justType)
150 return append([]string{}, s.([]string)...), nil
153 // AddExtensionType sets the MIME type associated with
154 // the extension ext to typ. The extension should begin with
155 // a leading dot, as in ".html".
156 func AddExtensionType(ext, typ string) error {
157 if !strings.HasPrefix(ext, ".") {
158 return fmt.Errorf("mime: extension %q missing leading dot", ext)
161 return setExtensionType(ext, typ)
164 func setExtensionType(extension, mimeType string) error {
165 justType, param, err := ParseMediaType(mimeType)
169 if strings.HasPrefix(mimeType, "text/") && param["charset"] == "" {
170 param["charset"] = "utf-8"
171 mimeType = FormatMediaType(mimeType, param)
173 extLower := strings.ToLower(extension)
175 mimeTypes.Store(extension, mimeType)
176 mimeTypesLower.Store(extLower, mimeType)
179 defer extensionsMu.Unlock()
181 if ei, ok := extensions.Load(justType); ok {
184 for _, v := range exts {
189 extensions.Store(justType, append(exts, extLower))