...

Source file src/mime/type.go

Documentation: mime

		 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  	"sort"
		11  	"strings"
		12  	"sync"
		13  )
		14  
		15  var (
		16  	mimeTypes			sync.Map // map[string]string; ".Z" => "application/x-compress"
		17  	mimeTypesLower sync.Map // map[string]string; ".z" => "application/x-compress"
		18  
		19  	// extensions maps from MIME type to list of lowercase file
		20  	// extensions: "image/jpeg" => [".jpg", ".jpeg"]
		21  	extensionsMu sync.Mutex // Guards stores (but not loads) on extensions.
		22  	extensions	 sync.Map	 // map[string][]string; slice values are append-only.
		23  )
		24  
		25  func clearSyncMap(m *sync.Map) {
		26  	m.Range(func(k, _ interface{}) bool {
		27  		m.Delete(k)
		28  		return true
		29  	})
		30  }
		31  
		32  // setMimeTypes is used by initMime's non-test path, and by tests.
		33  func setMimeTypes(lowerExt, mixExt map[string]string) {
		34  	clearSyncMap(&mimeTypes)
		35  	clearSyncMap(&mimeTypesLower)
		36  	clearSyncMap(&extensions)
		37  
		38  	for k, v := range lowerExt {
		39  		mimeTypesLower.Store(k, v)
		40  	}
		41  	for k, v := range mixExt {
		42  		mimeTypes.Store(k, v)
		43  	}
		44  
		45  	extensionsMu.Lock()
		46  	defer extensionsMu.Unlock()
		47  	for k, v := range lowerExt {
		48  		justType, _, err := ParseMediaType(v)
		49  		if err != nil {
		50  			panic(err)
		51  		}
		52  		var exts []string
		53  		if ei, ok := extensions.Load(justType); ok {
		54  			exts = ei.([]string)
		55  		}
		56  		extensions.Store(justType, append(exts, k))
		57  	}
		58  }
		59  
		60  var builtinTypesLower = map[string]string{
		61  	".avif": "image/avif",
		62  	".css":	"text/css; charset=utf-8",
		63  	".gif":	"image/gif",
		64  	".htm":	"text/html; charset=utf-8",
		65  	".html": "text/html; charset=utf-8",
		66  	".jpeg": "image/jpeg",
		67  	".jpg":	"image/jpeg",
		68  	".js":	 "text/javascript; charset=utf-8",
		69  	".json": "application/json",
		70  	".mjs":	"text/javascript; charset=utf-8",
		71  	".pdf":	"application/pdf",
		72  	".png":	"image/png",
		73  	".svg":	"image/svg+xml",
		74  	".wasm": "application/wasm",
		75  	".webp": "image/webp",
		76  	".xml":	"text/xml; charset=utf-8",
		77  }
		78  
		79  var once sync.Once // guards initMime
		80  
		81  var testInitMime, osInitMime func()
		82  
		83  func initMime() {
		84  	if fn := testInitMime; fn != nil {
		85  		fn()
		86  	} else {
		87  		setMimeTypes(builtinTypesLower, builtinTypesLower)
		88  		osInitMime()
		89  	}
		90  }
		91  
		92  // TypeByExtension returns the MIME type associated with the file extension ext.
		93  // The extension ext should begin with a leading dot, as in ".html".
		94  // When ext has no associated type, TypeByExtension returns "".
		95  //
		96  // Extensions are looked up first case-sensitively, then case-insensitively.
		97  //
		98  // The built-in table is small but on unix it is augmented by the local
		99  // system's MIME-info database or mime.types file(s) if available under one or
	 100  // more of these names:
	 101  //
	 102  //	 /usr/local/share/mime/globs2
	 103  //	 /usr/share/mime/globs2
	 104  //	 /etc/mime.types
	 105  //	 /etc/apache2/mime.types
	 106  //	 /etc/apache/mime.types
	 107  //
	 108  // On Windows, MIME types are extracted from the registry.
	 109  //
	 110  // Text types have the charset parameter set to "utf-8" by default.
	 111  func TypeByExtension(ext string) string {
	 112  	once.Do(initMime)
	 113  
	 114  	// Case-sensitive lookup.
	 115  	if v, ok := mimeTypes.Load(ext); ok {
	 116  		return v.(string)
	 117  	}
	 118  
	 119  	// Case-insensitive lookup.
	 120  	// Optimistically assume a short ASCII extension and be
	 121  	// allocation-free in that case.
	 122  	var buf [10]byte
	 123  	lower := buf[:0]
	 124  	const utf8RuneSelf = 0x80 // from utf8 package, but not importing it.
	 125  	for i := 0; i < len(ext); i++ {
	 126  		c := ext[i]
	 127  		if c >= utf8RuneSelf {
	 128  			// Slow path.
	 129  			si, _ := mimeTypesLower.Load(strings.ToLower(ext))
	 130  			s, _ := si.(string)
	 131  			return s
	 132  		}
	 133  		if 'A' <= c && c <= 'Z' {
	 134  			lower = append(lower, c+('a'-'A'))
	 135  		} else {
	 136  			lower = append(lower, c)
	 137  		}
	 138  	}
	 139  	si, _ := mimeTypesLower.Load(string(lower))
	 140  	s, _ := si.(string)
	 141  	return s
	 142  }
	 143  
	 144  // ExtensionsByType returns the extensions known to be associated with the MIME
	 145  // type typ. The returned extensions will each begin with a leading dot, as in
	 146  // ".html". When typ has no associated extensions, ExtensionsByType returns an
	 147  // nil slice.
	 148  func ExtensionsByType(typ string) ([]string, error) {
	 149  	justType, _, err := ParseMediaType(typ)
	 150  	if err != nil {
	 151  		return nil, err
	 152  	}
	 153  
	 154  	once.Do(initMime)
	 155  	s, ok := extensions.Load(justType)
	 156  	if !ok {
	 157  		return nil, nil
	 158  	}
	 159  	ret := append([]string(nil), s.([]string)...)
	 160  	sort.Strings(ret)
	 161  	return ret, nil
	 162  }
	 163  
	 164  // AddExtensionType sets the MIME type associated with
	 165  // the extension ext to typ. The extension should begin with
	 166  // a leading dot, as in ".html".
	 167  func AddExtensionType(ext, typ string) error {
	 168  	if !strings.HasPrefix(ext, ".") {
	 169  		return fmt.Errorf("mime: extension %q missing leading dot", ext)
	 170  	}
	 171  	once.Do(initMime)
	 172  	return setExtensionType(ext, typ)
	 173  }
	 174  
	 175  func setExtensionType(extension, mimeType string) error {
	 176  	justType, param, err := ParseMediaType(mimeType)
	 177  	if err != nil {
	 178  		return err
	 179  	}
	 180  	if strings.HasPrefix(mimeType, "text/") && param["charset"] == "" {
	 181  		param["charset"] = "utf-8"
	 182  		mimeType = FormatMediaType(mimeType, param)
	 183  	}
	 184  	extLower := strings.ToLower(extension)
	 185  
	 186  	mimeTypes.Store(extension, mimeType)
	 187  	mimeTypesLower.Store(extLower, mimeType)
	 188  
	 189  	extensionsMu.Lock()
	 190  	defer extensionsMu.Unlock()
	 191  	var exts []string
	 192  	if ei, ok := extensions.Load(justType); ok {
	 193  		exts = ei.([]string)
	 194  	}
	 195  	for _, v := range exts {
	 196  		if v == extLower {
	 197  			return nil
	 198  		}
	 199  	}
	 200  	extensions.Store(justType, append(exts, extLower))
	 201  	return nil
	 202  }
	 203  

View as plain text