英文:
Go (lang) parsing an email header and keeping order
问题
我正在使用Go语言的net/mail
库,一切都很好,但是我想传入一个原始的电子邮件并保持头部的顺序。这很重要,因为传递消息的邮件服务器会按顺序添加它们的头部。如果没有顺序,很难知道谁在何时接收了什么以及每个服务器添加了哪些头部。
net/mail
库将头部存储在一个映射中,根据定义,映射没有顺序的概念。这似乎是一个奇怪的选择,因为头部的顺序仅基于电子邮件中的顺序,但事实确实如此。
有人有任何关于如何保留头部读取顺序的建议吗?
谢谢
英文:
I'm using net/mail
library in Go, everything is great, however I want to pass in an original email and keep the order of the headers. This is important because the mail servers that pass the message on each add their headers in an order. Without order, its hard to know who received what, when and what headers each server added.
The net/mail
library stores the headers in a map, which by definition has no concept of order. Seems a strange choice as header order is based only on order in the email, but it is the case.
Anyone got any suggestions as to how I can retain order the headers were read?
Thanks
答案1
得分: 1
net/mail
包使用net/textproto
包来解析邮件头(参见ReadMessage()
)。具体来说,它使用[ReadMIMEHeader()
][2]来解析头部,其文档如下:
> 返回的映射m将CanonicalMIMEHeaderKey(key)映射到与输入中遇到的相同顺序的值序列。
如果你想要的话,可以[查看完整的源代码][3],但基本的过程如下:
headers = make(map[string][]string)
for {
key, value := readNextHeader()
if key == "" {
return headers // 头部结束
}
if headers[key] == nil {
headers[key] = []string{value}
} else {
headers[key] = append(headers[key], value)
}
}
确实,邮件中头部的原始顺序会丢失,但我不知道有任何真正需要保留这个顺序的情况。丢失的是多值头部的顺序。切片确保它们与电子邮件中出现的顺序相同。
你可以使用一个简单的程序循环遍历头部并比较值来验证这一点(例如Playground中的这个程序)。
然而,匹配Received
和Received-SPF
头部要复杂一些,因为:
- 并非每个
Received
头部都有对应的Received-SPF
头部; Received-SPF
头部可能不会出现在Received
头部之上;这是[RFC推荐但并非强制要求的][4](此外,许多程序甚至不遵循RFC,所以这也不能保证)。
因此,你需要解析头部的值并基于此进行匹配,或者使用net/textproto
包来更低级地访问头部。你可以使用[ReadMIMEHeader()
][3]的源代码作为起点。
1: https://github.com/golang/go/blob/master/src/net/mail/message.go#L53)
[2]: https://golang.org/pkg/net/textproto/#Reader.ReadMIMEHeader
[3]: https://github.com/golang/go/blob/master/src/net/textproto/reader.go#L468
[4]: http://www.openspf.org/RFC_4408#header-field
英文:
The net/mail
package uses the net/textproto
package to parse the headers
(see ReadMessage()
). Specifically, it uses [ReadMIMEHeader()
][2] for
the headers, which is documented as:
> The returned map m maps CanonicalMIMEHeaderKey(key) to a sequence of values
> in the same order encountered in the input.
You can [view the full source][3] if you want, but the basic process is:
headers = make(map[string][]string)
for {
key, value := readNextHeader()
if key == "" {
return headers // End of headers
}
if headers[key] == nil {
headers[key] = []string{value}
} else {
headers[key] = append(headers[key], value)
}
}
It's true that the original order of the headers as they appeared in the message
is lost, but I'm not aware of any scenario where this truly matters. What
isn't lost is the order of the multi-values headers. The slice ensures they're
in the same order as they appeared in the email.
You can verify this with a simple program which loops over the headers and
compares the values (such as this one in the
Playground).
However, matching Received
and Received-SPF
headers is a bit more complex,
as:
- not every
Received
header may have a correspondingReceived-SPF
header; - the
Received-SPF
header may not appear above theReceived
header; this is
[recommended but not mandated by the RFC][4] (besides, many programs don't
even follow the RFC, so this wouldn't be a guarantee anyway).
So you'll either need to parse the value of the headers and match them based on
that, or use the net/textproto
package for more low-level access to the
headers. You can use the source of [ReadMIMEHeader()
][3] as a starting point.
1: https://github.com/golang/go/blob/master/src/net/mail/message.go#L53)
[2]: https://golang.org/pkg/net/textproto/#Reader.ReadMIMEHeader
[3]: https://github.com/golang/go/blob/master/src/net/textproto/reader.go#L468
[4]: http://www.openspf.org/RFC_4408#header-field
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论