From ab2790d9dedd13e527e13d8de18fefdae7051992 Mon Sep 17 00:00:00 2001
From: Ivan Andreev <ivandeex@gmail.com>
Date: Sat, 3 Oct 2020 02:23:34 +0300
Subject: [PATCH] Fix http2 stream reset between restic and rest backends #3014

---
 internal/backend/rest/rest.go | 27 ++++++++++++++++++++++++++-
 1 file changed, 26 insertions(+), 1 deletion(-)

diff --git a/internal/backend/rest/rest.go b/internal/backend/rest/rest.go
index 4dc271f38..a294cb35f 100644
--- a/internal/backend/rest/rest.go
+++ b/internal/backend/rest/rest.go
@@ -173,7 +173,32 @@ func (b *Backend) IsNotExist(err error) bool {
 // Load runs fn with a reader that yields the contents of the file at h at the
 // given offset.
 func (b *Backend) Load(ctx context.Context, h restic.Handle, length int, offset int64, fn func(rd io.Reader) error) error {
-	return backend.DefaultLoad(ctx, h, length, offset, b.openReader, fn)
+	r, err := b.openReader(ctx, h, length, offset)
+	if err != nil {
+		return err
+	}
+	err = fn(r)
+	if err != nil {
+		_ = r.Close() // ignore error here
+		return err
+	}
+
+	// Note: readerat.ReadAt() (the fn) uses io.ReadFull() that doesn't
+	// wait for EOF after reading body. Due to HTTP/2 stream multiplexing
+	// and goroutine timings the EOF frame arrives from server (eg. rclone)
+	// with a delay after reading body. Immediate close might trigger
+	// HTTP/2 stream reset resulting in the *stream closed* error on server,
+	// so we wait for EOF before closing body.
+	var buf [1]byte
+	_, err = r.Read(buf[:])
+	if err == io.EOF {
+		err = nil
+	}
+
+	if e := r.Close(); err == nil {
+		err = e
+	}
+	return err
 }
 
 func (b *Backend) openReader(ctx context.Context, h restic.Handle, length int, offset int64) (io.ReadCloser, error) {