gopkg.in/docker/docker.v23@v23.0.11/registry/resumable/resumablerequestreader_test.go (about)

     1  package resumable // import "github.com/docker/docker/registry/resumable"
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  
    12  	"gotest.tools/v3/assert"
    13  	is "gotest.tools/v3/assert/cmp"
    14  )
    15  
    16  func TestResumableRequestHeaderSimpleErrors(t *testing.T) {
    17  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    18  		fmt.Fprintln(w, "Hello, world !")
    19  	}))
    20  	defer ts.Close()
    21  
    22  	client := &http.Client{}
    23  
    24  	var req *http.Request
    25  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
    26  	assert.NilError(t, err)
    27  
    28  	resreq := &requestReader{}
    29  	_, err = resreq.Read([]byte{})
    30  	assert.Check(t, is.Error(err, "client and request can't be nil"))
    31  
    32  	resreq = &requestReader{
    33  		client:    client,
    34  		request:   req,
    35  		totalSize: -1,
    36  	}
    37  	_, err = resreq.Read([]byte{})
    38  	assert.Check(t, is.Error(err, "failed to auto detect content length"))
    39  }
    40  
    41  // Not too much failures, bails out after some wait
    42  func TestResumableRequestHeaderNotTooMuchFailures(t *testing.T) {
    43  	client := &http.Client{}
    44  
    45  	var badReq *http.Request
    46  	badReq, err := http.NewRequest(http.MethodGet, "I'm not an url", nil)
    47  	assert.NilError(t, err)
    48  
    49  	resreq := &requestReader{
    50  		client:       client,
    51  		request:      badReq,
    52  		failures:     0,
    53  		maxFailures:  2,
    54  		waitDuration: 10 * time.Millisecond,
    55  	}
    56  	read, err := resreq.Read([]byte{})
    57  	assert.NilError(t, err)
    58  	assert.Check(t, is.Equal(0, read))
    59  }
    60  
    61  // Too much failures, returns the error
    62  func TestResumableRequestHeaderTooMuchFailures(t *testing.T) {
    63  	client := &http.Client{}
    64  
    65  	var badReq *http.Request
    66  	badReq, err := http.NewRequest(http.MethodGet, "I'm not an url", nil)
    67  	assert.NilError(t, err)
    68  
    69  	resreq := &requestReader{
    70  		client:      client,
    71  		request:     badReq,
    72  		failures:    0,
    73  		maxFailures: 1,
    74  	}
    75  	defer resreq.Close()
    76  
    77  	read, err := resreq.Read([]byte{})
    78  	assert.Assert(t, err != nil)
    79  	assert.Check(t, is.ErrorContains(err, "unsupported protocol scheme"))
    80  	assert.Check(t, is.ErrorContains(err, "I%27m%20not%20an%20url"))
    81  	assert.Check(t, is.Equal(0, read))
    82  }
    83  
    84  type errorReaderCloser struct{}
    85  
    86  func (errorReaderCloser) Close() error { return nil }
    87  
    88  func (errorReaderCloser) Read(p []byte) (n int, err error) {
    89  	return 0, fmt.Errorf("An error occurred")
    90  }
    91  
    92  // If an unknown error is encountered, return 0, nil and log it
    93  func TestResumableRequestReaderWithReadError(t *testing.T) {
    94  	var req *http.Request
    95  	req, err := http.NewRequest(http.MethodGet, "", nil)
    96  	assert.NilError(t, err)
    97  
    98  	client := &http.Client{}
    99  
   100  	response := &http.Response{
   101  		Status:        "500 Internal Server",
   102  		StatusCode:    http.StatusInternalServerError,
   103  		ContentLength: 0,
   104  		Close:         true,
   105  		Body:          errorReaderCloser{},
   106  	}
   107  
   108  	resreq := &requestReader{
   109  		client:          client,
   110  		request:         req,
   111  		currentResponse: response,
   112  		lastRange:       1,
   113  		totalSize:       1,
   114  	}
   115  	defer resreq.Close()
   116  
   117  	buf := make([]byte, 1)
   118  	read, err := resreq.Read(buf)
   119  	assert.NilError(t, err)
   120  
   121  	assert.Check(t, is.Equal(0, read))
   122  }
   123  
   124  func TestResumableRequestReaderWithEOFWith416Response(t *testing.T) {
   125  	var req *http.Request
   126  	req, err := http.NewRequest(http.MethodGet, "", nil)
   127  	assert.NilError(t, err)
   128  
   129  	client := &http.Client{}
   130  
   131  	response := &http.Response{
   132  		Status:        "416 Requested Range Not Satisfiable",
   133  		StatusCode:    http.StatusRequestedRangeNotSatisfiable,
   134  		ContentLength: 0,
   135  		Close:         true,
   136  		Body:          io.NopCloser(strings.NewReader("")),
   137  	}
   138  
   139  	resreq := &requestReader{
   140  		client:          client,
   141  		request:         req,
   142  		currentResponse: response,
   143  		lastRange:       1,
   144  		totalSize:       1,
   145  	}
   146  	defer resreq.Close()
   147  
   148  	buf := make([]byte, 1)
   149  	_, err = resreq.Read(buf)
   150  	assert.Check(t, is.Error(err, io.EOF.Error()))
   151  }
   152  
   153  func TestResumableRequestReaderWithServerDoesntSupportByteRanges(t *testing.T) {
   154  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   155  		if r.Header.Get("Range") == "" {
   156  			t.Fatalf("Expected a Range HTTP header, got nothing")
   157  		}
   158  	}))
   159  	defer ts.Close()
   160  
   161  	var req *http.Request
   162  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
   163  	assert.NilError(t, err)
   164  
   165  	client := &http.Client{}
   166  
   167  	resreq := &requestReader{
   168  		client:    client,
   169  		request:   req,
   170  		lastRange: 1,
   171  	}
   172  	defer resreq.Close()
   173  
   174  	buf := make([]byte, 2)
   175  	_, err = resreq.Read(buf)
   176  	assert.Check(t, is.Error(err, "the server doesn't support byte ranges"))
   177  }
   178  
   179  func TestResumableRequestReaderWithZeroTotalSize(t *testing.T) {
   180  	srvtxt := "some response text data"
   181  
   182  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   183  		fmt.Fprintln(w, srvtxt)
   184  	}))
   185  	defer ts.Close()
   186  
   187  	var req *http.Request
   188  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
   189  	assert.NilError(t, err)
   190  
   191  	client := &http.Client{}
   192  	retries := uint32(5)
   193  
   194  	resreq := NewRequestReader(client, req, retries, 0)
   195  	defer resreq.Close()
   196  
   197  	data, err := io.ReadAll(resreq)
   198  	assert.NilError(t, err)
   199  
   200  	resstr := strings.TrimSuffix(string(data), "\n")
   201  	assert.Check(t, is.Equal(srvtxt, resstr))
   202  }
   203  
   204  func TestResumableRequestReader(t *testing.T) {
   205  	srvtxt := "some response text data"
   206  
   207  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   208  		fmt.Fprintln(w, srvtxt)
   209  	}))
   210  	defer ts.Close()
   211  
   212  	var req *http.Request
   213  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
   214  	assert.NilError(t, err)
   215  
   216  	client := &http.Client{}
   217  	retries := uint32(5)
   218  	imgSize := int64(len(srvtxt))
   219  
   220  	resreq := NewRequestReader(client, req, retries, imgSize)
   221  	defer resreq.Close()
   222  
   223  	data, err := io.ReadAll(resreq)
   224  	assert.NilError(t, err)
   225  
   226  	resstr := strings.TrimSuffix(string(data), "\n")
   227  	assert.Check(t, is.Equal(srvtxt, resstr))
   228  }
   229  
   230  func TestResumableRequestReaderWithInitialResponse(t *testing.T) {
   231  	srvtxt := "some response text data"
   232  
   233  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   234  		fmt.Fprintln(w, srvtxt)
   235  	}))
   236  	defer ts.Close()
   237  
   238  	var req *http.Request
   239  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
   240  	assert.NilError(t, err)
   241  
   242  	client := &http.Client{}
   243  	retries := uint32(5)
   244  	imgSize := int64(len(srvtxt))
   245  
   246  	res, err := client.Do(req)
   247  	assert.NilError(t, err)
   248  
   249  	resreq := NewRequestReaderWithInitialResponse(client, req, retries, imgSize, res)
   250  	defer resreq.Close()
   251  
   252  	data, err := io.ReadAll(resreq)
   253  	assert.NilError(t, err)
   254  
   255  	resstr := strings.TrimSuffix(string(data), "\n")
   256  	assert.Check(t, is.Equal(srvtxt, resstr))
   257  }