Hash Reader

reader-hash-reader.svg
图 1: Hash Reader 示意图

hash.Reader 在读出数据时,同步计算内容的 MD5 值,并存储在 etag 中;并且在内容读取完毕后,计算 SHA256 对内容进行校验。

Read

Read 方法中,首先读取原始内容(L2),然后根据是否配置了 Hash 算法,计算 Hash 值并写入(L4 - L6)。如果内容读取完毕,且设置了 Hash 算法,计算校验值,并与内容存储时的 SHA256 比对(L8 - L17)。

  1. func (r *Reader) Read(p []byte) (int, error) {
  2. n, err := r.src.Read(p)
  3. r.bytesRead += int64(n)
  4. if r.sha256 != nil {
  5. r.sha256.Write(p[:n])
  6. }
  7. if err == io.EOF { // Verify content SHA256, if set.
  8. if r.sha256 != nil {
  9. if sum := r.sha256.Sum(nil); !bytes.Equal(r.contentSHA256, sum) {
  10. return n, SHA256Mismatch{
  11. ExpectedSHA256: hex.EncodeToString(r.contentSHA256),
  12. CalculatedSHA256: hex.EncodeToString(sum),
  13. }
  14. }
  15. }
  16. }
  17. if err != nil && err != io.EOF {
  18. if v, ok := err.(etag.VerifyError); ok {
  19. return n, BadDigest{
  20. ExpectedMD5: v.Expected.String(),
  21. CalculatedMD5: v.Computed.String(),
  22. }
  23. }
  24. }
  25. return n, err
  26. }

etag.Reader

hash.Reader 中指向的 Reader 中必然存在 etag.Reader 及其等价。etag.Reader 用于保存内容的 MD5 值。定义为

  1. type Reader struct {
  2. src io.Reader
  3. md5 hash.Hash
  4. checksum ETag
  5. readN int64
  6. }

创建方法非常简单

  1. func NewReader(r io.Reader, etag ETag) *Reader {
  2. if er, ok := r.(*Reader); ok {
  3. if er.readN == 0 && Equal(etag, er.checksum) {
  4. return er
  5. }
  6. }
  7. return &Reader{
  8. src: r,
  9. md5: md5.New(),
  10. checksum: etag,
  11. }
  12. }

其 Read 方法实现如下所示

  1. func (r *Reader) Read(p []byte) (int, error) {
  2. n, err := r.src.Read(p)
  3. r.readN += int64(n)
  4. r.md5.Write(p[:n])
  5. if err == io.EOF && len(r.checksum) != 0 {
  6. if etag := r.ETag(); !Equal(etag, r.checksum) {
  7. return n, VerifyError{
  8. Expected: r.checksum,
  9. Computed: etag,
  10. }
  11. }
  12. }
  13. return n, err
  14. }

ETag() 方法计算内容的 MD5 值

  1. func (r *Reader) ETag() ETag {
  2. sum := r.md5.Sum(nil)
  3. return ETag(sum)
  4. }