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.Readermd5 hash.Hashchecksum ETagreadN 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)}
