// Copyright 2010 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package mimeimport ()// FormatMediaType serializes mediatype t and the parameters// param as a media type conforming to RFC 2045 and RFC 2616.// The type and parameter names are written in lower-case.// When any of the arguments result in a standard violation then// FormatMediaType returns the empty string.func ( string, map[string]string) string {varstrings.Builderif , , := strings.Cut(, "/"); ! {if !isToken() {return"" } .WriteString(strings.ToLower()) } else {if !isToken() || !isToken() {return"" } .WriteString(strings.ToLower()) .WriteByte('/') .WriteString(strings.ToLower()) } := make([]string, 0, len())for := range { = append(, ) }slices.Sort()for , := range { := [] .WriteByte(';') .WriteByte(' ')if !isToken() {return"" } .WriteString(strings.ToLower()) := needsEncoding()if {// RFC 2231 section 4 .WriteByte('*') } .WriteByte('=')if { .WriteString("utf-8''") := 0for := 0; < len(); ++ { := []// {RFC 2231 section 7} // attribute-char := <any (US-ASCII) CHAR except SPACE, CTLs, "*", "'", "%", or tspecials>if <= ' ' || >= 0x7F || == '*' || == '\'' || == '%' ||isTSpecial(rune()) { .WriteString([:]) = + 1 .WriteByte('%') .WriteByte(upperhex[>>4]) .WriteByte(upperhex[&0x0F]) } } .WriteString([:])continue }ifisToken() { .WriteString()continue } .WriteByte('"') := 0for := 0; < len(); ++ { := []if == '"' || == '\\' { .WriteString([:]) = .WriteByte('\\') } } .WriteString([:]) .WriteByte('"') }return .String()}func checkMediaTypeDisposition( string) error { , := consumeToken()if == "" {returnerrors.New("mime: no media type") }if == "" {returnnil }if !strings.HasPrefix(, "/") {returnerrors.New("mime: expected slash after first token") } , := consumeToken([1:])if == "" {returnerrors.New("mime: expected token after slash") }if != "" {returnerrors.New("mime: unexpected content after media subtype") }returnnil}// ErrInvalidMediaParameter is returned by [ParseMediaType] if// the media type value was found but there was an error parsing// the optional parametersvarErrInvalidMediaParameter = errors.New("mime: invalid media parameter")// ParseMediaType parses a media type value and any optional// parameters, per RFC 1521. Media types are the values in// Content-Type and Content-Disposition headers (RFC 2183).// On success, ParseMediaType returns the media type converted// to lowercase and trimmed of white space and a non-nil map.// If there is an error parsing the optional parameter,// the media type will be returned along with the error// [ErrInvalidMediaParameter].// The returned map, params, maps from the lowercase// attribute to the attribute value with its case preserved.func ( string) ( string, map[string]string, error) { , , := strings.Cut(, ";") = strings.TrimSpace(strings.ToLower()) = checkMediaTypeDisposition()if != nil {return"", nil, } = make(map[string]string)// Map of base parameter name -> parameter name -> value // for parameters containing a '*' character. // Lazily initialized.varmap[string]map[string]string = [len():]forlen() > 0 { = strings.TrimLeftFunc(, unicode.IsSpace)iflen() == 0 {break } , , := consumeMediaParam()if == "" {ifstrings.TrimSpace() == ";" {// Ignore trailing semicolons. // Not an error.break }// Parse error.return , nil, ErrInvalidMediaParameter } := if , , := strings.Cut(, "*"); {if == nil { = make(map[string]map[string]string) }varboolif , = []; ! { [] = make(map[string]string) = [] } }if , := []; && != {// Duplicate parameter names are incorrect, but we allow them if they are equal.return"", nil, errors.New("mime: duplicate parameter name") } [] = = }// Stitch together any continuations or things with stars // (i.e. RFC 2231 things with stars: "foo*0" or "foo*")varstrings.Builderfor , := range { := + "*"if , := []; {if , := decode2231Enc(); { [] = }continue } .Reset() := falsefor := 0; ; ++ { := fmt.Sprintf("%s*%d", , )if , := []; { = true .WriteString()continue } := + "*" , := []if ! {break } = trueif == 0 {if , := decode2231Enc(); { .WriteString() } } else { , := percentHexUnescape() .WriteString() } }if { [] = .String() } }return}func decode2231Enc( string) (string, bool) { := strings.SplitN(, "'", 3)iflen() != 3 {return"", false }// TODO: ignoring lang in sv[1] for now. If anybody needs it we'll // need to decide how to expose it in the API. But I'm not sure // anybody uses it in practice. := strings.ToLower([0])iflen() == 0 {return"", false }if != "us-ascii" && != "utf-8" {// TODO: unsupported encodingreturn"", false } , := percentHexUnescape([2])if != nil {return"", false }return , true}func isNotTokenChar( rune) bool {return !isTokenChar()}// consumeToken consumes a token from the beginning of provided// string, per RFC 2045 section 5.1 (referenced from 2183), and return// the token consumed and the rest of the string. Returns ("", v) on// failure to consume at least one character.func consumeToken( string) (, string) { := strings.IndexFunc(, isNotTokenChar)if == -1 {return , "" }if == 0 {return"", }return [0:], [:]}// consumeValue consumes a "value" per RFC 2045, where a value is// either a 'token' or a 'quoted-string'. On success, consumeValue// returns the value consumed (and de-quoted/escaped, if a// quoted-string) and the rest of the string. On failure, returns// ("", v).func consumeValue( string) (, string) {if == "" {return }if [0] != '"' {returnconsumeToken() }// parse a quoted-string := new(strings.Builder)for := 1; < len(); ++ { := []if == '"' {return .String(), [+1:] }// When MSIE sends a full file path (in "intranet mode"), it does not // escape backslashes: "C:\dev\go\foo.txt", not "C:\\dev\\go\\foo.txt". // // No known MIME generators emit unnecessary backslash escapes // for simple token characters like numbers and letters. // // If we see an unnecessary backslash escape, assume it is from MSIE // and intended as a literal backslash. This makes Go servers deal better // with MSIE without affecting the way they handle conforming MIME // generators.if == '\\' && +1 < len() && isTSpecial(rune([+1])) { .WriteByte([+1]) ++continue }if == '\r' || == '\n' {return"", } .WriteByte([]) }// Did not find end quote.return"", }func consumeMediaParam( string) (, , string) { = strings.TrimLeftFunc(, unicode.IsSpace)if !strings.HasPrefix(, ";") {return"", "", } = [1:] // consume semicolon = strings.TrimLeftFunc(, unicode.IsSpace) , = consumeToken() = strings.ToLower()if == "" {return"", "", } = strings.TrimLeftFunc(, unicode.IsSpace)if !strings.HasPrefix(, "=") {return"", "", } = [1:] // consume equals sign = strings.TrimLeftFunc(, unicode.IsSpace) , := consumeValue()if == "" && == {return"", "", } = return , , }func percentHexUnescape( string) (string, error) {// Count %, check that they're well-formed. := 0for := 0; < len(); {if [] != '%' { ++continue } ++if +2 >= len() || !ishex([+1]) || !ishex([+2]) { = [:]iflen() > 3 { = [0:3] }return"", fmt.Errorf("mime: bogus characters after %%: %q", ) } += 3 }if == 0 {return , nil } := make([]byte, len()-2*) := 0for := 0; < len(); {switch [] {case'%': [] = unhex([+1])<<4 | unhex([+2]) ++ += 3default: [] = [] ++ ++ } }returnstring(), nil}func ishex( byte) bool {switch {case'0' <= && <= '9':returntruecase'a' <= && <= 'f':returntruecase'A' <= && <= 'F':returntrue }returnfalse}func unhex( byte) byte {switch {case'0' <= && <= '9':return - '0'case'a' <= && <= 'f':return - 'a' + 10case'A' <= && <= 'F':return - 'A' + 10 }return0}
The pages are generated with Goldsv0.7.0-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.