You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: retry_reader.go
    path of file: ./repos/azure-sdk-for-go/sdk/storage/azblob/blob
    
    the code of the file until where you have to start completion: //go:build go1.18
// +build go1.18

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

package blob

import (
	"context"
	"io"
	"net"
	"strings"
	"sync"

	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
)

// HTTPGetter is a function type that refers to a method that performs an HTTP GET operation.
type httpGetter func(ctx context.Context, i httpGetterInfo) (io.ReadCloser, error)

// HTTPGetterInfo is passed to an HTTPGetter function passing it parameters
// that should be used to make an HTTP GET request.
type httpGetterInfo struct {
	Range HTTPRange

	// ETag specifies the resource's etag that should be used when creating
	// the HTTP GET request's If-Match header
	ETag *azcore.ETag
}

// RetryReaderOptions con
    
func (s *RetryReader) Read(p []byte) (n int, err error) {
	for try := int32(0); ; try++ {
		//fmt.Println(try)       // Comment out for debugging.
		if s.countWasBounded && s.info.Range.Count == CountToEnd {
			// User specified an original count and the remaining bytes are 0, return 0, EOF
			return 0, io.EOF
		}

		s.responseMu.Lock()
		resp := s.response
		s.responseMu.Unlock()
		if resp == nil { // We don't have a response stream to read from, try to get one.
			newResponse, err := s.getter(s.ctx, s.info)
			if err != nil {
				return 0, err
			}
			// Successful GET; this is the network stream we'll read from.
			s.setResponse(newResponse)
			resp = newResponse
		}
		n, err := resp.Read(p) // Read from the stream (this will return non-nil err if forceRetry is called, from another goroutine, while it is running)

		// Injection mechanism for testing.
		if s.retryReaderOptions.doInjectError && try == s.retryReaderOptions.doInjectErrorRound {
			if s.retryReaderOptions.injectedError != nil {
				err = s.retryReaderOptions.injectedError
			} else {
				err = &net.DNSError{IsTemporary: true}
			}
		}

		// We successfully read data or end EOF.
		if err == nil || err == io.EOF {
			s.info.Range.Offset += int64(n) // Increments the start offset in case we need to make a new HTTP request in the future
			if s.info.Range.Count != CountToEnd {
				s.info.Range.Count -= int64(n) // Decrement the count in case we need to make a new HTTP request in the future
			}
			return n, err // Return the return to the caller
		}
		_ = s.Close()

		s.setResponse(nil) // Our stream is no longer good

		// Check the retry count and error code, and decide whether to retry.
		retriesExhausted := try >= s.retryReaderOptions.MaxRetries
		_, isNetError := err.(net.Error)
		isUnexpectedEOF := err == io.ErrUnexpectedEOF
		willRetry := (isNetError || isUnexpectedEOF || s.wasRetryableEarlyClose(err)) && !retriesExhausted

		// Notify, for logging purposes, of any failures
		if s.retryReaderOptions.OnFailedRead != nil {
			failureCount := try + 1 // because try is zero-based
			s.retryReaderOptions.OnFailedRead(failureCount, err, s.info.Range, willRetry)
		}

		if willRetry {
			continue
			// Loop around and try to get and read from new stream.
		}
		return n, err // Not retryable, or retries exhausted, so just return
	}
}