1//go:build go1.16
2// +build go1.16
3
4// Copyright (c) Microsoft Corporation. All rights reserved.
5// Licensed under the MIT License.
6
7package streaming
8
9import (
10	"io"
11
12	"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
13)
14
15type progress struct {
16	rc     io.ReadCloser
17	rsc    io.ReadSeekCloser
18	pr     func(bytesTransferred int64)
19	offset int64
20}
21
22// NopCloser returns a ReadSeekCloser with a no-op close method wrapping the provided io.ReadSeeker.
23func NopCloser(rs io.ReadSeeker) io.ReadSeekCloser {
24	return shared.NopCloser(rs)
25}
26
27// NewRequestProgress adds progress reporting to an HTTP request's body stream.
28func NewRequestProgress(body io.ReadSeekCloser, pr func(bytesTransferred int64)) io.ReadSeekCloser {
29	return &progress{
30		rc:     body,
31		rsc:    body,
32		pr:     pr,
33		offset: 0,
34	}
35}
36
37// NewResponseProgress adds progress reporting to an HTTP response's body stream.
38func NewResponseProgress(body io.ReadCloser, pr func(bytesTransferred int64)) io.ReadCloser {
39	return &progress{
40		rc:     body,
41		rsc:    nil,
42		pr:     pr,
43		offset: 0,
44	}
45}
46
47// Read reads a block of data from an inner stream and reports progress
48func (p *progress) Read(b []byte) (n int, err error) {
49	n, err = p.rc.Read(b)
50	if err != nil && err != io.EOF {
51		return
52	}
53	p.offset += int64(n)
54	// Invokes the user's callback method to report progress
55	p.pr(p.offset)
56	return
57}
58
59// Seek only expects a zero or from beginning.
60func (p *progress) Seek(offset int64, whence int) (int64, error) {
61	// This should only ever be called with offset = 0 and whence = io.SeekStart
62	n, err := p.rsc.Seek(offset, whence)
63	if err == nil {
64		p.offset = int64(n)
65	}
66	return n, err
67}
68
69// requestBodyProgress supports Close but the underlying stream may not; if it does, Close will close it.
70func (p *progress) Close() error {
71	return p.rc.Close()
72}
73