copy: improve error detection from closed pipes

If we get io.ErrClosedPipe in pushWriter.Write, there are three possible
scenarios:

- The request has failed, we need to attempt a reset, so we can expect a
  new pipe incoming on pipeC.
- The request has failed, we don't need to attempt a reset, so we can
  expect an incoming error on errC.
- Something else externally has called Close, so we can expect the done
  channel to be closed.

This patch ensures that we block for as long as possible (while still
handling each of the above cases, so we avoid hanging), to make sure
that we properly return an appropriate error message each time.

Signed-off-by: Justin Chadwell <me@jedevc.com>
This commit is contained in:
Justin Chadwell 2023-04-13 15:49:44 +01:00
parent 91a50f70b7
commit 9d7641ff3e

View File

@ -420,9 +420,22 @@ func (pw *pushWriter) Write(p []byte) (n int, err error) {
// if the pipe is closed, we might have the original error on the error // if the pipe is closed, we might have the original error on the error
// channel - so we should try and get it // channel - so we should try and get it
select { select {
case err2 := <-pw.errC: case <-pw.done:
err = err2 case err = <-pw.errC:
default: case p := <-pw.pipeC:
pw.pipe.CloseWithError(content.ErrReset)
pw.pipe = p
// If content has already been written, the bytes
// cannot be written again and the caller must reset
status, err := pw.tracker.GetStatus(pw.ref)
if err != nil {
return 0, err
}
status.Offset = 0
status.UpdatedAt = time.Now()
pw.tracker.SetStatus(pw.ref, status)
return 0, content.ErrReset
} }
} }
status.Offset += int64(n) status.Offset += int64(n)