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