Hash Reader
图 1: Hash Reader 示意图
hash.Reader 在读出数据时,同步计算内容的 MD5 值,并存储在 etag 中;并且在内容读取完毕后,计算 SHA256 对内容进行校验。
Read
Read 方法中,首先读取原始内容(L2),然后根据是否配置了 Hash 算法,计算 Hash 值并写入(L4 - L6)。如果内容读取完毕,且设置了 Hash 算法,计算校验值,并与内容存储时的 SHA256 比对(L8 - L17)。
func (r *Reader) Read(p []byte) (int, error) {
n, err := r.src.Read(p)
r.bytesRead += int64(n)
if r.sha256 != nil {
r.sha256.Write(p[:n])
}
if err == io.EOF { // Verify content SHA256, if set.
if r.sha256 != nil {
if sum := r.sha256.Sum(nil); !bytes.Equal(r.contentSHA256, sum) {
return n, SHA256Mismatch{
ExpectedSHA256: hex.EncodeToString(r.contentSHA256),
CalculatedSHA256: hex.EncodeToString(sum),
}
}
}
}
if err != nil && err != io.EOF {
if v, ok := err.(etag.VerifyError); ok {
return n, BadDigest{
ExpectedMD5: v.Expected.String(),
CalculatedMD5: v.Computed.String(),
}
}
}
return n, err
}
etag.Reader
hash.Reader 中指向的 Reader 中必然存在 etag.Reader 及其等价。etag.Reader 用于保存内容的 MD5 值。定义为
type Reader struct {
src io.Reader
md5 hash.Hash
checksum ETag
readN int64
}
创建方法非常简单
func NewReader(r io.Reader, etag ETag) *Reader {
if er, ok := r.(*Reader); ok {
if er.readN == 0 && Equal(etag, er.checksum) {
return er
}
}
return &Reader{
src: r,
md5: md5.New(),
checksum: etag,
}
}
其 Read 方法实现如下所示
func (r *Reader) Read(p []byte) (int, error) {
n, err := r.src.Read(p)
r.readN += int64(n)
r.md5.Write(p[:n])
if err == io.EOF && len(r.checksum) != 0 {
if etag := r.ETag(); !Equal(etag, r.checksum) {
return n, VerifyError{
Expected: r.checksum,
Computed: etag,
}
}
}
return n, err
}
ETag() 方法计算内容的 MD5 值
func (r *Reader) ETag() ETag {
sum := r.md5.Sum(nil)
return ETag(sum)
}