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 5package textproto 6 7import ( 8 "bufio" 9 "bytes" 10 "errors" 11 "fmt" 12 "io" 13 "math" 14 "strconv" 15 "strings" 16 "sync" 17 _ "unsafe" // for linkname 18) 19 20// TODO: This should be a distinguishable error (ErrMessageTooLarge) 21// to allow mime/multipart to detect it. 22var errMessageTooLarge = errors.New("message too large") 23 24// A Reader implements convenience methods for reading requests 25// or responses from a text protocol network connection. 26type Reader struct { 27 R *bufio.Reader 28 dot *dotReader 29 buf []byte // a re-usable buffer for readContinuedLineSlice 30} 31 32// NewReader returns a new [Reader] reading from r. 33// 34// To avoid denial of service attacks, the provided [bufio.Reader] 35// should be reading from an [io.LimitReader] or similar Reader to bound 36// the size of responses. 37func NewReader(r *bufio.Reader) *Reader { 38 return &Reader{R: r} 39} 40 41// ReadLine reads a single line from r, 42// eliding the final \n or \r\n from the returned string. 43func (r *Reader) ReadLine() (string, error) { 44 line, err := r.readLineSlice(-1) 45 return string(line), err 46} 47 48// ReadLineBytes is like [Reader.ReadLine] but returns a []byte instead of a string. 49func (r *Reader) ReadLineBytes() ([]byte, error) { 50 line, err := r.readLineSlice(-1) 51 if line != nil { 52 line = bytes.Clone(line) 53 } 54 return line, err 55} 56 57// readLineSlice reads a single line from r, 58// up to lim bytes long (or unlimited if lim is less than 0), 59// eliding the final \r or \r\n from the returned string. 60func (r *Reader) readLineSlice(lim int64) ([]byte, error) { 61 r.closeDot() 62 var line []byte 63 for { 64 l, more, err := r.R.ReadLine() 65 if err != nil { 66 return nil, err 67 } 68 if lim >= 0 && int64(len(line))+int64(len(l)) > lim { 69 return nil, errMessageTooLarge 70 } 71 // Avoid the copy if the first call produced a full line. 72 if line == nil && !more { 73 return l, nil 74 } 75 line = append(line, l...) 76 if !more { 77 break 78 } 79 } 80 return line, nil 81} 82 83// ReadContinuedLine reads a possibly continued line from r, 84// eliding the final trailing ASCII white space. 85// Lines after the first are considered continuations if they 86// begin with a space or tab character. In the returned data, 87// continuation lines are separated from the previous line 88// only by a single space: the newline and leading white space 89// are removed. 90// 91// For example, consider this input: 92// 93// Line 1 94// continued... 95// Line 2 96// 97// The first call to ReadContinuedLine will return "Line 1 continued..." 98// and the second will return "Line 2". 99// 100// Empty lines are never continued. 101func (r *Reader) ReadContinuedLine() (string, error) { 102 line, err := r.readContinuedLineSlice(-1, noValidation) 103 return string(line), err 104} 105 106// trim returns s with leading and trailing spaces and tabs removed. 107// It does not assume Unicode or UTF-8. 108func trim(s []byte) []byte { 109 i := 0 110 for i < len(s) && (s[i] == ' ' || s[i] == '\t') { 111 i++ 112 } 113 n := len(s) 114 for n > i && (s[n-1] == ' ' || s[n-1] == '\t') { 115 n-- 116 } 117 return s[i:n] 118} 119 120// ReadContinuedLineBytes is like [Reader.ReadContinuedLine] but 121// returns a []byte instead of a string. 122func (r *Reader) ReadContinuedLineBytes() ([]byte, error) { 123 line, err := r.readContinuedLineSlice(-1, noValidation) 124 if line != nil { 125 line = bytes.Clone(line) 126 } 127 return line, err 128} 129 130// readContinuedLineSlice reads continued lines from the reader buffer, 131// returning a byte slice with all lines. The validateFirstLine function 132// is run on the first read line, and if it returns an error then this 133// error is returned from readContinuedLineSlice. 134// It reads up to lim bytes of data (or unlimited if lim is less than 0). 135func (r *Reader) readContinuedLineSlice(lim int64, validateFirstLine func([]byte) error) ([]byte, error) { 136 if validateFirstLine == nil { 137 return nil, fmt.Errorf("missing validateFirstLine func") 138 } 139 140 // Read the first line. 141 line, err := r.readLineSlice(lim) 142 if err != nil { 143 return nil, err 144 } 145 if len(line) == 0 { // blank line - no continuation 146 return line, nil 147 } 148 149 if err := validateFirstLine(line); err != nil { 150 return nil, err 151 } 152 153 // Optimistically assume that we have started to buffer the next line 154 // and it starts with an ASCII letter (the next header key), or a blank 155 // line, so we can avoid copying that buffered data around in memory 156 // and skipping over non-existent whitespace. 157 if r.R.Buffered() > 1 { 158 peek, _ := r.R.Peek(2) 159 if len(peek) > 0 && (isASCIILetter(peek[0]) || peek[0] == '\n') || 160 len(peek) == 2 && peek[0] == '\r' && peek[1] == '\n' { 161 return trim(line), nil 162 } 163 } 164 165 // ReadByte or the next readLineSlice will flush the read buffer; 166 // copy the slice into buf. 167 r.buf = append(r.buf[:0], trim(line)...) 168 169 if lim < 0 { 170 lim = math.MaxInt64 171 } 172 lim -= int64(len(r.buf)) 173 174 // Read continuation lines. 175 for r.skipSpace() > 0 { 176 r.buf = append(r.buf, ' ') 177 if int64(len(r.buf)) >= lim { 178 return nil, errMessageTooLarge 179 } 180 line, err := r.readLineSlice(lim - int64(len(r.buf))) 181 if err != nil { 182 break 183 } 184 r.buf = append(r.buf, trim(line)...) 185 } 186 return r.buf, nil 187} 188 189// skipSpace skips R over all spaces and returns the number of bytes skipped. 190func (r *Reader) skipSpace() int { 191 n := 0 192 for { 193 c, err := r.R.ReadByte() 194 if err != nil { 195 // Bufio will keep err until next read. 196 break 197 } 198 if c != ' ' && c != '\t' { 199 r.R.UnreadByte() 200 break 201 } 202 n++ 203 } 204 return n 205} 206 207func (r *Reader) readCodeLine(expectCode int) (code int, continued bool, message string, err error) { 208 line, err := r.ReadLine() 209 if err != nil { 210 return 211 } 212 return parseCodeLine(line, expectCode) 213} 214 215func parseCodeLine(line string, expectCode int) (code int, continued bool, message string, err error) { 216 if len(line) < 4 || line[3] != ' ' && line[3] != '-' { 217 err = ProtocolError("short response: " + line) 218 return 219 } 220 continued = line[3] == '-' 221 code, err = strconv.Atoi(line[0:3]) 222 if err != nil || code < 100 { 223 err = ProtocolError("invalid response code: " + line) 224 return 225 } 226 message = line[4:] 227 if 1 <= expectCode && expectCode < 10 && code/100 != expectCode || 228 10 <= expectCode && expectCode < 100 && code/10 != expectCode || 229 100 <= expectCode && expectCode < 1000 && code != expectCode { 230 err = &Error{code, message} 231 } 232 return 233} 234 235// ReadCodeLine reads a response code line of the form 236// 237// code message 238// 239// where code is a three-digit status code and the message 240// extends to the rest of the line. An example of such a line is: 241// 242// 220 plan9.bell-labs.com ESMTP 243// 244// If the prefix of the status does not match the digits in expectCode, 245// ReadCodeLine returns with err set to &Error{code, message}. 246// For example, if expectCode is 31, an error will be returned if 247// the status is not in the range [310,319]. 248// 249// If the response is multi-line, ReadCodeLine returns an error. 250// 251// An expectCode <= 0 disables the check of the status code. 252func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err error) { 253 code, continued, message, err := r.readCodeLine(expectCode) 254 if err == nil && continued { 255 err = ProtocolError("unexpected multi-line response: " + message) 256 } 257 return 258} 259 260// ReadResponse reads a multi-line response of the form: 261// 262// code-message line 1 263// code-message line 2 264// ... 265// code message line n 266// 267// where code is a three-digit status code. The first line starts with the 268// code and a hyphen. The response is terminated by a line that starts 269// with the same code followed by a space. Each line in message is 270// separated by a newline (\n). 271// 272// See page 36 of RFC 959 (https://www.ietf.org/rfc/rfc959.txt) for 273// details of another form of response accepted: 274// 275// code-message line 1 276// message line 2 277// ... 278// code message line n 279// 280// If the prefix of the status does not match the digits in expectCode, 281// ReadResponse returns with err set to &Error{code, message}. 282// For example, if expectCode is 31, an error will be returned if 283// the status is not in the range [310,319]. 284// 285// An expectCode <= 0 disables the check of the status code. 286func (r *Reader) ReadResponse(expectCode int) (code int, message string, err error) { 287 code, continued, message, err := r.readCodeLine(expectCode) 288 multi := continued 289 for continued { 290 line, err := r.ReadLine() 291 if err != nil { 292 return 0, "", err 293 } 294 295 var code2 int 296 var moreMessage string 297 code2, continued, moreMessage, err = parseCodeLine(line, 0) 298 if err != nil || code2 != code { 299 message += "\n" + strings.TrimRight(line, "\r\n") 300 continued = true 301 continue 302 } 303 message += "\n" + moreMessage 304 } 305 if err != nil && multi && message != "" { 306 // replace one line error message with all lines (full message) 307 err = &Error{code, message} 308 } 309 return 310} 311 312// DotReader returns a new [Reader] that satisfies Reads using the 313// decoded text of a dot-encoded block read from r. 314// The returned Reader is only valid until the next call 315// to a method on r. 316// 317// Dot encoding is a common framing used for data blocks 318// in text protocols such as SMTP. The data consists of a sequence 319// of lines, each of which ends in "\r\n". The sequence itself 320// ends at a line containing just a dot: ".\r\n". Lines beginning 321// with a dot are escaped with an additional dot to avoid 322// looking like the end of the sequence. 323// 324// The decoded form returned by the Reader's Read method 325// rewrites the "\r\n" line endings into the simpler "\n", 326// removes leading dot escapes if present, and stops with error [io.EOF] 327// after consuming (and discarding) the end-of-sequence line. 328func (r *Reader) DotReader() io.Reader { 329 r.closeDot() 330 r.dot = &dotReader{r: r} 331 return r.dot 332} 333 334type dotReader struct { 335 r *Reader 336 state int 337} 338 339// Read satisfies reads by decoding dot-encoded data read from d.r. 340func (d *dotReader) Read(b []byte) (n int, err error) { 341 // Run data through a simple state machine to 342 // elide leading dots, rewrite trailing \r\n into \n, 343 // and detect ending .\r\n line. 344 const ( 345 stateBeginLine = iota // beginning of line; initial state; must be zero 346 stateDot // read . at beginning of line 347 stateDotCR // read .\r at beginning of line 348 stateCR // read \r (possibly at end of line) 349 stateData // reading data in middle of line 350 stateEOF // reached .\r\n end marker line 351 ) 352 br := d.r.R 353 for n < len(b) && d.state != stateEOF { 354 var c byte 355 c, err = br.ReadByte() 356 if err != nil { 357 if err == io.EOF { 358 err = io.ErrUnexpectedEOF 359 } 360 break 361 } 362 switch d.state { 363 case stateBeginLine: 364 if c == '.' { 365 d.state = stateDot 366 continue 367 } 368 if c == '\r' { 369 d.state = stateCR 370 continue 371 } 372 d.state = stateData 373 374 case stateDot: 375 if c == '\r' { 376 d.state = stateDotCR 377 continue 378 } 379 if c == '\n' { 380 d.state = stateEOF 381 continue 382 } 383 d.state = stateData 384 385 case stateDotCR: 386 if c == '\n' { 387 d.state = stateEOF 388 continue 389 } 390 // Not part of .\r\n. 391 // Consume leading dot and emit saved \r. 392 br.UnreadByte() 393 c = '\r' 394 d.state = stateData 395 396 case stateCR: 397 if c == '\n' { 398 d.state = stateBeginLine 399 break 400 } 401 // Not part of \r\n. Emit saved \r 402 br.UnreadByte() 403 c = '\r' 404 d.state = stateData 405 406 case stateData: 407 if c == '\r' { 408 d.state = stateCR 409 continue 410 } 411 if c == '\n' { 412 d.state = stateBeginLine 413 } 414 } 415 b[n] = c 416 n++ 417 } 418 if err == nil && d.state == stateEOF { 419 err = io.EOF 420 } 421 if err != nil && d.r.dot == d { 422 d.r.dot = nil 423 } 424 return 425} 426 427// closeDot drains the current DotReader if any, 428// making sure that it reads until the ending dot line. 429func (r *Reader) closeDot() { 430 if r.dot == nil { 431 return 432 } 433 buf := make([]byte, 128) 434 for r.dot != nil { 435 // When Read reaches EOF or an error, 436 // it will set r.dot == nil. 437 r.dot.Read(buf) 438 } 439} 440 441// ReadDotBytes reads a dot-encoding and returns the decoded data. 442// 443// See the documentation for the [Reader.DotReader] method for details about dot-encoding. 444func (r *Reader) ReadDotBytes() ([]byte, error) { 445 return io.ReadAll(r.DotReader()) 446} 447 448// ReadDotLines reads a dot-encoding and returns a slice 449// containing the decoded lines, with the final \r\n or \n elided from each. 450// 451// See the documentation for the [Reader.DotReader] method for details about dot-encoding. 452func (r *Reader) ReadDotLines() ([]string, error) { 453 // We could use ReadDotBytes and then Split it, 454 // but reading a line at a time avoids needing a 455 // large contiguous block of memory and is simpler. 456 var v []string 457 var err error 458 for { 459 var line string 460 line, err = r.ReadLine() 461 if err != nil { 462 if err == io.EOF { 463 err = io.ErrUnexpectedEOF 464 } 465 break 466 } 467 468 // Dot by itself marks end; otherwise cut one dot. 469 if len(line) > 0 && line[0] == '.' { 470 if len(line) == 1 { 471 break 472 } 473 line = line[1:] 474 } 475 v = append(v, line) 476 } 477 return v, err 478} 479 480var colon = []byte(":") 481 482// ReadMIMEHeader reads a MIME-style header from r. 483// The header is a sequence of possibly continued Key: Value lines 484// ending in a blank line. 485// The returned map m maps [CanonicalMIMEHeaderKey](key) to a 486// sequence of values in the same order encountered in the input. 487// 488// For example, consider this input: 489// 490// My-Key: Value 1 491// Long-Key: Even 492// Longer Value 493// My-Key: Value 2 494// 495// Given that input, ReadMIMEHeader returns the map: 496// 497// map[string][]string{ 498// "My-Key": {"Value 1", "Value 2"}, 499// "Long-Key": {"Even Longer Value"}, 500// } 501func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) { 502 return readMIMEHeader(r, math.MaxInt64, math.MaxInt64) 503} 504 505// readMIMEHeader is accessed from mime/multipart. 506//go:linkname readMIMEHeader 507 508// readMIMEHeader is a version of ReadMIMEHeader which takes a limit on the header size. 509// It is called by the mime/multipart package. 510func readMIMEHeader(r *Reader, maxMemory, maxHeaders int64) (MIMEHeader, error) { 511 // Avoid lots of small slice allocations later by allocating one 512 // large one ahead of time which we'll cut up into smaller 513 // slices. If this isn't big enough later, we allocate small ones. 514 var strs []string 515 hint := r.upcomingHeaderKeys() 516 if hint > 0 { 517 if hint > 1000 { 518 hint = 1000 // set a cap to avoid overallocation 519 } 520 strs = make([]string, hint) 521 } 522 523 m := make(MIMEHeader, hint) 524 525 // Account for 400 bytes of overhead for the MIMEHeader, plus 200 bytes per entry. 526 // Benchmarking map creation as of go1.20, a one-entry MIMEHeader is 416 bytes and large 527 // MIMEHeaders average about 200 bytes per entry. 528 maxMemory -= 400 529 const mapEntryOverhead = 200 530 531 // The first line cannot start with a leading space. 532 if buf, err := r.R.Peek(1); err == nil && (buf[0] == ' ' || buf[0] == '\t') { 533 const errorLimit = 80 // arbitrary limit on how much of the line we'll quote 534 line, err := r.readLineSlice(errorLimit) 535 if err != nil { 536 return m, err 537 } 538 return m, ProtocolError("malformed MIME header initial line: " + string(line)) 539 } 540 541 for { 542 kv, err := r.readContinuedLineSlice(maxMemory, mustHaveFieldNameColon) 543 if len(kv) == 0 { 544 return m, err 545 } 546 547 // Key ends at first colon. 548 k, v, ok := bytes.Cut(kv, colon) 549 if !ok { 550 return m, ProtocolError("malformed MIME header line: " + string(kv)) 551 } 552 key, ok := canonicalMIMEHeaderKey(k) 553 if !ok { 554 return m, ProtocolError("malformed MIME header line: " + string(kv)) 555 } 556 for _, c := range v { 557 if !validHeaderValueByte(c) { 558 return m, ProtocolError("malformed MIME header line: " + string(kv)) 559 } 560 } 561 562 maxHeaders-- 563 if maxHeaders < 0 { 564 return nil, errMessageTooLarge 565 } 566 567 // Skip initial spaces in value. 568 value := string(bytes.TrimLeft(v, " \t")) 569 570 vv := m[key] 571 if vv == nil { 572 maxMemory -= int64(len(key)) 573 maxMemory -= mapEntryOverhead 574 } 575 maxMemory -= int64(len(value)) 576 if maxMemory < 0 { 577 return m, errMessageTooLarge 578 } 579 if vv == nil && len(strs) > 0 { 580 // More than likely this will be a single-element key. 581 // Most headers aren't multi-valued. 582 // Set the capacity on strs[0] to 1, so any future append 583 // won't extend the slice into the other strings. 584 vv, strs = strs[:1:1], strs[1:] 585 vv[0] = value 586 m[key] = vv 587 } else { 588 m[key] = append(vv, value) 589 } 590 591 if err != nil { 592 return m, err 593 } 594 } 595} 596 597// noValidation is a no-op validation func for readContinuedLineSlice 598// that permits any lines. 599func noValidation(_ []byte) error { return nil } 600 601// mustHaveFieldNameColon ensures that, per RFC 7230, the 602// field-name is on a single line, so the first line must 603// contain a colon. 604func mustHaveFieldNameColon(line []byte) error { 605 if bytes.IndexByte(line, ':') < 0 { 606 return ProtocolError(fmt.Sprintf("malformed MIME header: missing colon: %q", line)) 607 } 608 return nil 609} 610 611var nl = []byte("\n") 612 613// upcomingHeaderKeys returns an approximation of the number of keys 614// that will be in this header. If it gets confused, it returns 0. 615func (r *Reader) upcomingHeaderKeys() (n int) { 616 // Try to determine the 'hint' size. 617 r.R.Peek(1) // force a buffer load if empty 618 s := r.R.Buffered() 619 if s == 0 { 620 return 621 } 622 peek, _ := r.R.Peek(s) 623 for len(peek) > 0 && n < 1000 { 624 var line []byte 625 line, peek, _ = bytes.Cut(peek, nl) 626 if len(line) == 0 || (len(line) == 1 && line[0] == '\r') { 627 // Blank line separating headers from the body. 628 break 629 } 630 if line[0] == ' ' || line[0] == '\t' { 631 // Folded continuation of the previous line. 632 continue 633 } 634 n++ 635 } 636 return n 637} 638 639// CanonicalMIMEHeaderKey returns the canonical format of the 640// MIME header key s. The canonicalization converts the first 641// letter and any letter following a hyphen to upper case; 642// the rest are converted to lowercase. For example, the 643// canonical key for "accept-encoding" is "Accept-Encoding". 644// MIME header keys are assumed to be ASCII only. 645// If s contains a space or invalid header field bytes, it is 646// returned without modifications. 647func CanonicalMIMEHeaderKey(s string) string { 648 // Quick check for canonical encoding. 649 upper := true 650 for i := 0; i < len(s); i++ { 651 c := s[i] 652 if !validHeaderFieldByte(c) { 653 return s 654 } 655 if upper && 'a' <= c && c <= 'z' { 656 s, _ = canonicalMIMEHeaderKey([]byte(s)) 657 return s 658 } 659 if !upper && 'A' <= c && c <= 'Z' { 660 s, _ = canonicalMIMEHeaderKey([]byte(s)) 661 return s 662 } 663 upper = c == '-' 664 } 665 return s 666} 667 668const toLower = 'a' - 'A' 669 670// validHeaderFieldByte reports whether c is a valid byte in a header 671// field name. RFC 7230 says: 672// 673// header-field = field-name ":" OWS field-value OWS 674// field-name = token 675// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / 676// "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA 677// token = 1*tchar 678func validHeaderFieldByte(c byte) bool { 679 // mask is a 128-bit bitmap with 1s for allowed bytes, 680 // so that the byte c can be tested with a shift and an and. 681 // If c >= 128, then 1<<c and 1<<(c-64) will both be zero, 682 // and this function will return false. 683 const mask = 0 | 684 (1<<(10)-1)<<'0' | 685 (1<<(26)-1)<<'a' | 686 (1<<(26)-1)<<'A' | 687 1<<'!' | 688 1<<'#' | 689 1<<'$' | 690 1<<'%' | 691 1<<'&' | 692 1<<'\'' | 693 1<<'*' | 694 1<<'+' | 695 1<<'-' | 696 1<<'.' | 697 1<<'^' | 698 1<<'_' | 699 1<<'`' | 700 1<<'|' | 701 1<<'~' 702 return ((uint64(1)<<c)&(mask&(1<<64-1)) | 703 (uint64(1)<<(c-64))&(mask>>64)) != 0 704} 705 706// validHeaderValueByte reports whether c is a valid byte in a header 707// field value. RFC 7230 says: 708// 709// field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] 710// field-vchar = VCHAR / obs-text 711// obs-text = %x80-FF 712// 713// RFC 5234 says: 714// 715// HTAB = %x09 716// SP = %x20 717// VCHAR = %x21-7E 718func validHeaderValueByte(c byte) bool { 719 // mask is a 128-bit bitmap with 1s for allowed bytes, 720 // so that the byte c can be tested with a shift and an and. 721 // If c >= 128, then 1<<c and 1<<(c-64) will both be zero. 722 // Since this is the obs-text range, we invert the mask to 723 // create a bitmap with 1s for disallowed bytes. 724 const mask = 0 | 725 (1<<(0x7f-0x21)-1)<<0x21 | // VCHAR: %x21-7E 726 1<<0x20 | // SP: %x20 727 1<<0x09 // HTAB: %x09 728 return ((uint64(1)<<c)&^(mask&(1<<64-1)) | 729 (uint64(1)<<(c-64))&^(mask>>64)) == 0 730} 731 732// canonicalMIMEHeaderKey is like CanonicalMIMEHeaderKey but is 733// allowed to mutate the provided byte slice before returning the 734// string. 735// 736// For invalid inputs (if a contains spaces or non-token bytes), a 737// is unchanged and a string copy is returned. 738// 739// ok is true if the header key contains only valid characters and spaces. 740// ReadMIMEHeader accepts header keys containing spaces, but does not 741// canonicalize them. 742func canonicalMIMEHeaderKey(a []byte) (_ string, ok bool) { 743 if len(a) == 0 { 744 return "", false 745 } 746 747 // See if a looks like a header key. If not, return it unchanged. 748 noCanon := false 749 for _, c := range a { 750 if validHeaderFieldByte(c) { 751 continue 752 } 753 // Don't canonicalize. 754 if c == ' ' { 755 // We accept invalid headers with a space before the 756 // colon, but must not canonicalize them. 757 // See https://go.dev/issue/34540. 758 noCanon = true 759 continue 760 } 761 return string(a), false 762 } 763 if noCanon { 764 return string(a), true 765 } 766 767 upper := true 768 for i, c := range a { 769 // Canonicalize: first letter upper case 770 // and upper case after each dash. 771 // (Host, User-Agent, If-Modified-Since). 772 // MIME headers are ASCII only, so no Unicode issues. 773 if upper && 'a' <= c && c <= 'z' { 774 c -= toLower 775 } else if !upper && 'A' <= c && c <= 'Z' { 776 c += toLower 777 } 778 a[i] = c 779 upper = c == '-' // for next time 780 } 781 commonHeaderOnce.Do(initCommonHeader) 782 // The compiler recognizes m[string(byteSlice)] as a special 783 // case, so a copy of a's bytes into a new string does not 784 // happen in this map lookup: 785 if v := commonHeader[string(a)]; v != "" { 786 return v, true 787 } 788 return string(a), true 789} 790 791// commonHeader interns common header strings. 792var commonHeader map[string]string 793 794var commonHeaderOnce sync.Once 795 796func initCommonHeader() { 797 commonHeader = make(map[string]string) 798 for _, v := range []string{ 799 "Accept", 800 "Accept-Charset", 801 "Accept-Encoding", 802 "Accept-Language", 803 "Accept-Ranges", 804 "Cache-Control", 805 "Cc", 806 "Connection", 807 "Content-Id", 808 "Content-Language", 809 "Content-Length", 810 "Content-Transfer-Encoding", 811 "Content-Type", 812 "Cookie", 813 "Date", 814 "Dkim-Signature", 815 "Etag", 816 "Expires", 817 "From", 818 "Host", 819 "If-Modified-Since", 820 "If-None-Match", 821 "In-Reply-To", 822 "Last-Modified", 823 "Location", 824 "Message-Id", 825 "Mime-Version", 826 "Pragma", 827 "Received", 828 "Return-Path", 829 "Server", 830 "Set-Cookie", 831 "Subject", 832 "To", 833 "User-Agent", 834 "Via", 835 "X-Forwarded-For", 836 "X-Imforwards", 837 "X-Powered-By", 838 } { 839 commonHeader[v] = v 840 } 841} 842