10// ParseHeaderFields parses only the header fields in "fields" from the complete
11// header buffer "header". It uses "scratch" as temporary space, which can be
12// reused across calls, potentially saving lots of unneeded allocations when only a
13// few headers are needed and/or many messages are parsed.
14func ParseHeaderFields(header []byte, scratch []byte, fields [][]byte) (textproto.MIMEHeader, error) {
15 // todo: should not use mail.ReadMessage, it allocates a bufio.Reader. should implement header parsing ourselves.
17 // Gather the raw lines for the fields, with continuations, without the other
18 // headers. Put them in a byte slice and only parse those headers. For now, use
19 // mail.ReadMessage without letting it do allocations for all headers.
21 var keepcontinuation bool
23 if header[0] == ' ' || header[0] == '\t' {
25 i := bytes.IndexByte(header, '\n')
32 scratch = append(scratch, header[:i]...)
37 i := bytes.IndexByte(header, ':')
38 if i < 0 || i > 0 && (header[i-1] == ' ' || header[i-1] == '\t') {
39 i = bytes.IndexByte(header, '\n')
44 keepcontinuation = false
48 keepcontinuation = false
49 for _, f := range fields {
50 if bytes.EqualFold(k, f) {
51 keepcontinuation = true
55 i = bytes.IndexByte(header, '\n')
62 scratch = append(scratch, header[:i]...)
67 if len(scratch) == 0 {
71 scratch = append(scratch, "\r\n"...)
73 msg, err := mail.ReadMessage(bytes.NewReader(scratch))
75 return nil, fmt.Errorf("reading message header")
77 return textproto.MIMEHeader(msg.Header), nil