diff options
Diffstat (limited to 'libgo/go/mime/type.go')
-rw-r--r-- | libgo/go/mime/type.go | 84 |
1 files changed, 64 insertions, 20 deletions
diff --git a/libgo/go/mime/type.go b/libgo/go/mime/type.go index 00cff263ba..ffda1f0ce5 100644 --- a/libgo/go/mime/type.go +++ b/libgo/go/mime/type.go @@ -11,26 +11,41 @@ import ( "sync" ) -var mimeTypes = map[string]string{ - ".css": "text/css; charset=utf-8", - ".gif": "image/gif", - ".htm": "text/html; charset=utf-8", - ".html": "text/html; charset=utf-8", - ".jpg": "image/jpeg", - ".js": "application/x-javascript", - ".pdf": "application/pdf", - ".png": "image/png", - ".xml": "text/xml; charset=utf-8", -} +var ( + mimeLock sync.RWMutex + mimeTypesLower = map[string]string{ + ".css": "text/css; charset=utf-8", + ".gif": "image/gif", + ".htm": "text/html; charset=utf-8", + ".html": "text/html; charset=utf-8", + ".jpg": "image/jpeg", + ".js": "application/x-javascript", + ".pdf": "application/pdf", + ".png": "image/png", + ".xml": "text/xml; charset=utf-8", + } + mimeTypes = clone(mimeTypesLower) +) -var mimeLock sync.RWMutex +func clone(m map[string]string) map[string]string { + m2 := make(map[string]string, len(m)) + for k, v := range m { + m2[k] = v + if strings.ToLower(k) != k { + panic("keys in mimeTypesLower must be lowercase") + } + } + return m2 +} -var once sync.Once +var once sync.Once // guards initMime // TypeByExtension returns the MIME type associated with the file extension ext. // The extension ext should begin with a leading dot, as in ".html". // When ext has no associated type, TypeByExtension returns "". // +// Extensions are looked up first case-sensitively, then case-insensitively. +// // The built-in table is small but on unix it is augmented by the local // system's mime.types file(s) if available under one or more of these // names: @@ -39,23 +54,49 @@ var once sync.Once // /etc/apache2/mime.types // /etc/apache/mime.types // -// Windows system mime types are extracted from registry. +// On Windows, MIME types are extracted from the registry. // // Text types have the charset parameter set to "utf-8" by default. func TypeByExtension(ext string) string { once.Do(initMime) mimeLock.RLock() - typename := mimeTypes[ext] - mimeLock.RUnlock() - return typename + defer mimeLock.RUnlock() + + // Case-sensitive lookup. + v := mimeTypes[ext] + if v != "" { + return v + } + + // Case-insensitive lookup. + // Optimistically assume a short ASCII extension and be + // allocation-free in that case. + var buf [10]byte + lower := buf[:0] + const utf8RuneSelf = 0x80 // from utf8 package, but not importing it. + for i := 0; i < len(ext); i++ { + c := ext[i] + if c >= utf8RuneSelf { + // Slow path. + return mimeTypesLower[strings.ToLower(ext)] + } + if 'A' <= c && c <= 'Z' { + lower = append(lower, c+('a'-'A')) + } else { + lower = append(lower, c) + } + } + // The conversion from []byte to string doesn't allocate in + // a map lookup. + return mimeTypesLower[string(lower)] } // AddExtensionType sets the MIME type associated with -// the extension ext to typ. The extension should begin with +// the extension ext to typ. The extension should begin with // a leading dot, as in ".html". func AddExtensionType(ext, typ string) error { - if ext == "" || ext[0] != '.' { - return fmt.Errorf(`mime: extension "%s" misses dot`, ext) + if !strings.HasPrefix(ext, ".") { + return fmt.Errorf(`mime: extension %q misses dot`, ext) } once.Do(initMime) return setExtensionType(ext, typ) @@ -70,8 +111,11 @@ func setExtensionType(extension, mimeType string) error { param["charset"] = "utf-8" mimeType = FormatMediaType(mimeType, param) } + extLower := strings.ToLower(extension) + mimeLock.Lock() mimeTypes[extension] = mimeType + mimeTypesLower[extLower] = mimeType mimeLock.Unlock() return nil } |