github.com/guyezi/gofrontend@v0.0.0-20200228202240-7a62a49e62c0/libgo/go/mime/type.go (about) 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. 4 5 // Package mime implements parts of the MIME spec. 6 package mime 7 8 import ( 9 "fmt" 10 "strings" 11 "sync" 12 ) 13 14 var ( 15 mimeTypes sync.Map // map[string]string; ".Z" => "application/x-compress" 16 mimeTypesLower sync.Map // map[string]string; ".z" => "application/x-compress" 17 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. 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 // 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) 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": "text/javascript; charset=utf-8", 67 ".mjs": "text/javascript; charset=utf-8", 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 // guards initMime 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 // TypeByExtension returns the MIME type associated with the file extension ext. 90 // The extension ext should begin with a leading dot, as in ".html". 91 // When ext has no associated type, TypeByExtension returns "". 92 // 93 // Extensions are looked up first case-sensitively, then case-insensitively. 94 // 95 // The built-in table is small but on unix it is augmented by the local 96 // system's mime.types file(s) if available under one or more of these 97 // names: 98 // 99 // /etc/mime.types 100 // /etc/apache2/mime.types 101 // /etc/apache/mime.types 102 // 103 // On Windows, MIME types are extracted from the registry. 104 // 105 // Text types have the charset parameter set to "utf-8" by default. 106 func TypeByExtension(ext string) string { 107 once.Do(initMime) 108 109 // Case-sensitive lookup. 110 if v, ok := mimeTypes.Load(ext); ok { 111 return v.(string) 112 } 113 114 // Case-insensitive lookup. 115 // Optimistically assume a short ASCII extension and be 116 // allocation-free in that case. 117 var buf [10]byte 118 lower := buf[:0] 119 const utf8RuneSelf = 0x80 // from utf8 package, but not importing it. 120 for i := 0; i < len(ext); i++ { 121 c := ext[i] 122 if c >= utf8RuneSelf { 123 // Slow path. 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 // ExtensionsByType returns the extensions known to be associated with the MIME 140 // type typ. The returned extensions will each begin with a leading dot, as in 141 // ".html". When typ has no associated extensions, ExtensionsByType returns an 142 // nil slice. 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 // AddExtensionType sets the MIME type associated with 158 // the extension ext to typ. The extension should begin with 159 // a leading dot, as in ".html". 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 }