github.com/openshift-online/ocm-sdk-go@v0.1.473/retry/transport_wrapper_test.go (about)

     1  /*
     2  Copyright (c) 2021 Red Hat, Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8    http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // This file contains tests for request retrying.
    18  
    19  package retry
    20  
    21  import (
    22  	"context"
    23  	"crypto/tls"
    24  	"io"
    25  	"net"
    26  	"net/http"
    27  	"net/url"
    28  	"strings"
    29  	"time"
    30  
    31  	"golang.org/x/net/http2"
    32  
    33  	. "github.com/onsi/ginkgo/v2/dsl/core"             // nolint
    34  	. "github.com/onsi/gomega"                         // nolint
    35  	. "github.com/openshift-online/ocm-sdk-go/testing" // nolint
    36  )
    37  
    38  var _ = Describe("Creation", func() {
    39  	var ctx context.Context
    40  
    41  	BeforeEach(func() {
    42  		ctx = context.Background()
    43  	})
    44  
    45  	It("Can't be created without a logger", func() {
    46  		wrapper, err := NewTransportWrapper().
    47  			Build(ctx)
    48  		Expect(err).To(HaveOccurred())
    49  		Expect(wrapper).To(BeNil())
    50  		message := err.Error()
    51  		Expect(message).To(ContainSubstring("logger"))
    52  		Expect(message).To(ContainSubstring("mandatory"))
    53  	})
    54  
    55  	It("Can be created with positive retry limit", func() {
    56  		wrapper, err := NewTransportWrapper().
    57  			Logger(logger).
    58  			Limit(10).
    59  			Build(ctx)
    60  		Expect(err).ToNot(HaveOccurred())
    61  		Expect(wrapper).ToNot(BeNil())
    62  		err = wrapper.Close()
    63  		Expect(err).ToNot(HaveOccurred())
    64  	})
    65  
    66  	It("Can be created with zero retry limit", func() {
    67  		wrapper, err := NewTransportWrapper().
    68  			Logger(logger).
    69  			Limit(0).
    70  			Build(ctx)
    71  		Expect(err).ToNot(HaveOccurred())
    72  		Expect(wrapper).ToNot(BeNil())
    73  		err = wrapper.Close()
    74  		Expect(err).ToNot(HaveOccurred())
    75  	})
    76  
    77  	It("Can't be created with negative retry limit", func() {
    78  		wrapper, err := NewTransportWrapper().
    79  			Logger(logger).
    80  			Limit(-1).
    81  			Build(ctx)
    82  		Expect(err).To(HaveOccurred())
    83  		Expect(wrapper).To(BeNil())
    84  		message := err.Error()
    85  		Expect(message).To(ContainSubstring("limit"))
    86  		Expect(message).To(ContainSubstring("-1"))
    87  		Expect(message).To(ContainSubstring("greater or equal than zero"))
    88  	})
    89  
    90  	It("Can be created with positive retry interval", func() {
    91  		wrapper, err := NewTransportWrapper().
    92  			Logger(logger).
    93  			Interval(5 * time.Second).
    94  			Build(ctx)
    95  		Expect(err).ToNot(HaveOccurred())
    96  		Expect(wrapper).ToNot(BeNil())
    97  		err = wrapper.Close()
    98  		Expect(err).ToNot(HaveOccurred())
    99  	})
   100  
   101  	It("Can't be created with zero retry interval", func() {
   102  		wrapper, err := NewTransportWrapper().
   103  			Logger(logger).
   104  			Interval(0).
   105  			Build(ctx)
   106  		Expect(err).To(HaveOccurred())
   107  		Expect(wrapper).To(BeNil())
   108  		message := err.Error()
   109  		Expect(message).To(ContainSubstring("interval"))
   110  		Expect(message).To(ContainSubstring("0"))
   111  		Expect(message).To(ContainSubstring("greater than zero"))
   112  	})
   113  
   114  	It("Can't be created with negative retry interval", func() {
   115  		wrapper, err := NewTransportWrapper().
   116  			Logger(logger).
   117  			Interval(0).
   118  			Build(ctx)
   119  		Expect(err).To(HaveOccurred())
   120  		Expect(wrapper).To(BeNil())
   121  		message := err.Error()
   122  		Expect(message).To(ContainSubstring("interval"))
   123  		Expect(message).To(ContainSubstring("0"))
   124  		Expect(message).To(ContainSubstring("greater than zero"))
   125  	})
   126  
   127  	It("Can be created with jitter between zero and one", func() {
   128  		wrapper, err := NewTransportWrapper().
   129  			Logger(logger).
   130  			Jitter(0.3).
   131  			Build(ctx)
   132  		Expect(err).ToNot(HaveOccurred())
   133  		Expect(wrapper).ToNot(BeNil())
   134  		err = wrapper.Close()
   135  		Expect(err).ToNot(HaveOccurred())
   136  	})
   137  
   138  	It("Can be created with zero jitter", func() {
   139  		wrapper, err := NewTransportWrapper().
   140  			Logger(logger).
   141  			Jitter(0.0).
   142  			Build(ctx)
   143  		Expect(err).ToNot(HaveOccurred())
   144  		Expect(wrapper).ToNot(BeNil())
   145  		err = wrapper.Close()
   146  		Expect(err).ToNot(HaveOccurred())
   147  	})
   148  
   149  	It("Can't be created with negative jitter", func() {
   150  		wrapper, err := NewTransportWrapper().
   151  			Logger(logger).
   152  			Jitter(-1).
   153  			Build(ctx)
   154  		Expect(err).To(HaveOccurred())
   155  		Expect(wrapper).To(BeNil())
   156  		message := err.Error()
   157  		Expect(message).To(ContainSubstring("jitter"))
   158  		Expect(message).To(ContainSubstring("0"))
   159  		Expect(message).To(ContainSubstring("between zero and one"))
   160  	})
   161  
   162  	It("Can't be created with jitter greater than one", func() {
   163  		wrapper, err := NewTransportWrapper().
   164  			Logger(logger).
   165  			Jitter(2).
   166  			Build(ctx)
   167  		Expect(err).To(HaveOccurred())
   168  		Expect(wrapper).To(BeNil())
   169  		message := err.Error()
   170  		Expect(message).To(ContainSubstring("jitter"))
   171  		Expect(message).To(ContainSubstring("2"))
   172  		Expect(message).To(ContainSubstring("between zero and one"))
   173  	})
   174  })
   175  
   176  var _ = Describe("Server error", func() {
   177  	var ctx context.Context
   178  
   179  	BeforeEach(func() {
   180  		ctx = context.Background()
   181  	})
   182  
   183  	When("Retry enabled", func() {
   184  		It("Retries 503 without request body", func() {
   185  			// Create a transport that returns a 503 error for the first request and 200
   186  			// for the second:
   187  			transport := CombineTransports(
   188  				TextTransport(http.StatusServiceUnavailable, `ko`),
   189  				JSONTransport(http.StatusOK, `{ "ok": true }`),
   190  			)
   191  
   192  			// Wrap the transport:
   193  			wrapper, err := NewTransportWrapper().
   194  				Logger(logger).
   195  				Interval(100 * time.Millisecond).
   196  				Build(ctx)
   197  			Expect(err).ToNot(HaveOccurred())
   198  			defer func() {
   199  				err = wrapper.Close()
   200  				Expect(err).ToNot(HaveOccurred())
   201  			}()
   202  
   203  			// Create the client:
   204  			client := &http.Client{
   205  				Transport: wrapper.Wrap(transport),
   206  				Timeout:   10 * time.Second,
   207  			}
   208  
   209  			// Send the request:
   210  			response, err := client.Get("http://api.example.com/mypath")
   211  			Expect(err).ToNot(HaveOccurred())
   212  			Expect(response).ToNot(BeNil())
   213  			Expect(response.StatusCode).To(Equal(http.StatusOK))
   214  			body, err := io.ReadAll(response.Body)
   215  			Expect(err).ToNot(HaveOccurred())
   216  			Expect(body).To(MatchJSON(`{ "ok": true }`))
   217  		})
   218  
   219  		It("Retries 503 with request body", func() {
   220  			// Create a transport that returns a 503 error for the first request and 200
   221  			// for the second:
   222  			transport := CombineTransports(
   223  				TextTransport(http.StatusServiceUnavailable, `ko`),
   224  				JSONTransport(http.StatusOK, `{ "ok": true }`),
   225  			)
   226  
   227  			// Wrap the transport:
   228  			wrapper, err := NewTransportWrapper().
   229  				Logger(logger).
   230  				Interval(100 * time.Millisecond).
   231  				Build(ctx)
   232  			Expect(err).ToNot(HaveOccurred())
   233  			defer func() {
   234  				err = wrapper.Close()
   235  				Expect(err).ToNot(HaveOccurred())
   236  			}()
   237  
   238  			// Create the client:
   239  			client := &http.Client{
   240  				Transport: wrapper.Wrap(transport),
   241  				Timeout:   10 * time.Second,
   242  			}
   243  
   244  			// Send the request:
   245  			response, err := client.Post(
   246  				"http://api.example.com/mypath",
   247  				"application/json",
   248  				strings.NewReader(`{}`),
   249  			)
   250  			Expect(err).ToNot(HaveOccurred())
   251  			Expect(response).ToNot(BeNil())
   252  			Expect(response.StatusCode).To(Equal(http.StatusOK))
   253  			body, err := io.ReadAll(response.Body)
   254  			Expect(err).ToNot(HaveOccurred())
   255  			Expect(body).To(MatchJSON(`{ "ok": true }`))
   256  		})
   257  
   258  		It("Retries 429 without request body", func() {
   259  			// Create a transport that returns a 429 error for the first request and 200
   260  			// for the second:
   261  			transport := CombineTransports(
   262  				JSONTransport(http.StatusTooManyRequests, `{ "ok": false }`),
   263  				JSONTransport(http.StatusOK, `{ "ok": true }`),
   264  			)
   265  
   266  			// Wrap the transport:
   267  			wrapper, err := NewTransportWrapper().
   268  				Logger(logger).
   269  				Interval(100 * time.Millisecond).
   270  				Build(ctx)
   271  			Expect(err).ToNot(HaveOccurred())
   272  			defer func() {
   273  				err = wrapper.Close()
   274  				Expect(err).ToNot(HaveOccurred())
   275  			}()
   276  
   277  			// Create the client:
   278  			client := &http.Client{
   279  				Transport: wrapper.Wrap(transport),
   280  				Timeout:   10 * time.Second,
   281  			}
   282  
   283  			// Send the request:
   284  			response, err := client.Get("http://api.example.com/mypath")
   285  			Expect(err).ToNot(HaveOccurred())
   286  			Expect(response).ToNot(BeNil())
   287  			Expect(response.StatusCode).To(Equal(http.StatusOK))
   288  			body, err := io.ReadAll(response.Body)
   289  			Expect(err).ToNot(HaveOccurred())
   290  			Expect(body).To(MatchJSON(`{ "ok": true }`))
   291  		})
   292  
   293  		It("Retries 429 with request body", func() {
   294  			// Create a transport that returns a 429 error for the first request and 200
   295  			// for the second:
   296  			transport := CombineTransports(
   297  				JSONTransport(http.StatusTooManyRequests, `{ "ok": false }`),
   298  				JSONTransport(http.StatusOK, `{ "ok": true }`),
   299  			)
   300  
   301  			// Wrap the transport:
   302  			wrapper, err := NewTransportWrapper().
   303  				Logger(logger).
   304  				Interval(100 * time.Millisecond).
   305  				Build(ctx)
   306  			Expect(err).ToNot(HaveOccurred())
   307  			defer func() {
   308  				err = wrapper.Close()
   309  				Expect(err).ToNot(HaveOccurred())
   310  			}()
   311  
   312  			// Create the client:
   313  			client := &http.Client{
   314  				Transport: wrapper.Wrap(transport),
   315  				Timeout:   10 * time.Second,
   316  			}
   317  
   318  			// Send the request:
   319  			response, err := client.Post(
   320  				"http://api.example.com/mypath",
   321  				"application/json",
   322  				strings.NewReader(`{}`),
   323  			)
   324  			Expect(err).ToNot(HaveOccurred())
   325  			Expect(response).ToNot(BeNil())
   326  			Expect(response.StatusCode).To(Equal(http.StatusOK))
   327  			body, err := io.ReadAll(response.Body)
   328  			Expect(err).ToNot(HaveOccurred())
   329  			Expect(body).To(MatchJSON(`{ "ok": true }`))
   330  		})
   331  	})
   332  
   333  	When("Retry disabled", func() {
   334  		It("Doesn't retry 503", func() {
   335  			// Create a transport that returns a 503 error for the first request and 200
   336  			// for the second:
   337  			transport := CombineTransports(
   338  				TextTransport(http.StatusServiceUnavailable, `ko`),
   339  				JSONTransport(http.StatusOK, `{ "ok": true }`),
   340  			)
   341  
   342  			// Wrap the transport:
   343  			wrapper, err := NewTransportWrapper().
   344  				Logger(logger).
   345  				Limit(0).
   346  				Interval(100 * time.Millisecond).
   347  				Build(ctx)
   348  			Expect(err).ToNot(HaveOccurred())
   349  			defer func() {
   350  				err = wrapper.Close()
   351  				Expect(err).ToNot(HaveOccurred())
   352  			}()
   353  
   354  			// Create the client:
   355  			client := &http.Client{
   356  				Transport: wrapper.Wrap(transport),
   357  				Timeout:   10 * time.Second,
   358  			}
   359  
   360  			// Send the request:
   361  			response, err := client.Get("http://api.example.com/mypath")
   362  			Expect(err).ToNot(HaveOccurred())
   363  			Expect(response).ToNot(BeNil())
   364  			Expect(response.StatusCode).To(Equal(http.StatusServiceUnavailable))
   365  			body, err := io.ReadAll(response.Body)
   366  			Expect(err).ToNot(HaveOccurred())
   367  			Expect(string(body)).To(Equal(`ko`))
   368  		})
   369  
   370  		It("Doesn't retry 429", func() {
   371  			// Create a transport that returns a 429 error for the first request and 200
   372  			// for the second:
   373  			transport := CombineTransports(
   374  				JSONTransport(http.StatusTooManyRequests, `{ "ok": false }`),
   375  				JSONTransport(http.StatusOK, `{ "ok": true }`),
   376  			)
   377  
   378  			// Wrap the transport:
   379  			wrapper, err := NewTransportWrapper().
   380  				Logger(logger).
   381  				Limit(0).
   382  				Interval(100 * time.Millisecond).
   383  				Build(ctx)
   384  			Expect(err).ToNot(HaveOccurred())
   385  			defer func() {
   386  				err = wrapper.Close()
   387  				Expect(err).ToNot(HaveOccurred())
   388  			}()
   389  
   390  			// Create the client:
   391  			client := &http.Client{
   392  				Transport: wrapper.Wrap(transport),
   393  				Timeout:   10 * time.Second,
   394  			}
   395  
   396  			// Send the request:
   397  			response, err := client.Get("http://api.example.com/mypath")
   398  			Expect(err).ToNot(HaveOccurred())
   399  			Expect(response).ToNot(BeNil())
   400  			Expect(response.StatusCode).To(Equal(http.StatusTooManyRequests))
   401  			body, err := io.ReadAll(response.Body)
   402  			Expect(err).ToNot(HaveOccurred())
   403  			Expect(body).To(MatchJSON(`{ "ok": false }`))
   404  		})
   405  	})
   406  })
   407  
   408  var _ = Describe("Protocol error", func() {
   409  	var ctx context.Context
   410  	var listener net.Listener
   411  	var address string
   412  	var transport http.RoundTripper
   413  
   414  	BeforeEach(func() {
   415  		// Create a context:
   416  		ctx = context.Background()
   417  
   418  		// Create a listener:
   419  		listener, address = Listen()
   420  
   421  		// Create the basic transport:
   422  		transport = &http.Transport{
   423  			TLSClientConfig: &tls.Config{
   424  				InsecureSkipVerify: true,
   425  			},
   426  			ForceAttemptHTTP2: true,
   427  		}
   428  	})
   429  
   430  	AfterEach(func() {
   431  		// Close the listener:
   432  		err := listener.Close()
   433  		Expect(err).ToNot(HaveOccurred())
   434  	})
   435  
   436  	When("Retry is enabled", func() {
   437  		It("Tolerates protocol error", func() {
   438  			var err error
   439  
   440  			// Run the HTTP/2 server:
   441  			go func() {
   442  				defer GinkgoRecover()
   443  
   444  				// Reject the first connection:
   445  				first := Accept(listener)
   446  				Reject(first)
   447  
   448  				// Accept the second connection:
   449  				second := Accept(listener)
   450  				Serve(second, func(w http.ResponseWriter, r *http.Request) {
   451  					w.Header().Set("Content-Type", "application/json")
   452  					w.WriteHeader(http.StatusOK)
   453  					_, err := w.Write([]byte("{}"))
   454  					Expect(err).ToNot(HaveOccurred())
   455  				})
   456  			}()
   457  
   458  			// Wrap the transport:
   459  			wrapper, err := NewTransportWrapper().
   460  				Logger(logger).
   461  				Interval(100 * time.Millisecond).
   462  				Build(ctx)
   463  			Expect(err).ToNot(HaveOccurred())
   464  			defer func() {
   465  				err = wrapper.Close()
   466  				Expect(err).ToNot(HaveOccurred())
   467  			}()
   468  			client := &http.Client{
   469  				Transport: wrapper.Wrap(transport),
   470  				Timeout:   10 * time.Second,
   471  			}
   472  
   473  			// Send the request:
   474  			response, err := client.Get(address)
   475  			Expect(err).ToNot(HaveOccurred())
   476  			Expect(response).ToNot(BeNil())
   477  			Expect(response.StatusCode).To(Equal(http.StatusOK))
   478  			body, err := io.ReadAll(response.Body)
   479  			Expect(err).ToNot(HaveOccurred())
   480  			Expect(body).To(MatchJSON("{}"))
   481  		})
   482  
   483  		It("Honours retry limit", func() {
   484  			// Run the HTTP/2 server.
   485  			go func() {
   486  				defer GinkgoRecover()
   487  
   488  				// Reject the first four connections:
   489  				conn := Accept(listener)
   490  				Reject(conn)
   491  				conn = Accept(listener)
   492  				Reject(conn)
   493  				conn = Accept(listener)
   494  				Reject(conn)
   495  				conn = Accept(listener)
   496  				Reject(conn)
   497  			}()
   498  
   499  			// Wrap the transport:
   500  			wrapper, err := NewTransportWrapper().
   501  				Logger(logger).
   502  				Limit(3).
   503  				Interval(100 * time.Millisecond).
   504  				Jitter(0).
   505  				Build(ctx)
   506  			Expect(err).ToNot(HaveOccurred())
   507  			defer func() {
   508  				err = wrapper.Close()
   509  				Expect(err).ToNot(HaveOccurred())
   510  			}()
   511  			client := &http.Client{
   512  				Transport: wrapper.Wrap(transport),
   513  				Timeout:   10 * time.Second,
   514  			}
   515  
   516  			// Send the request:
   517  			response, err := client.Get(address)
   518  			Expect(err).To(HaveOccurred())
   519  			Expect(response).To(BeNil())
   520  			message := err.Error()
   521  			Expect(message).To(ContainSubstring("PROTOCOL_ERROR"))
   522  		})
   523  
   524  		It("Honours retry interval", func() {
   525  			// Run the HTTP/2 server.
   526  			go func() {
   527  				defer GinkgoRecover()
   528  
   529  				// We will use this to calculate the time between requests:
   530  				var start time.Time
   531  				var elapsed time.Duration
   532  
   533  				// Reject the first connection:
   534  				conn := Accept(listener)
   535  				Reject(conn)
   536  				start = time.Now()
   537  
   538  				// Reject the second connection an verify that it was sent after
   539  				// waiting the configured interval:
   540  				conn = Accept(listener)
   541  				Reject(conn)
   542  				elapsed = time.Since(start)
   543  				start = time.Now()
   544  				Expect(elapsed).To(BeNumerically(
   545  					"~",
   546  					100*time.Millisecond,
   547  					50*time.Millisecond,
   548  				))
   549  
   550  				// Reject the third connection and verify that it was sent after
   551  				// waiting the double of the configured interval:
   552  				conn = Accept(listener)
   553  				Reject(conn)
   554  				elapsed = time.Since(start)
   555  				start = time.Now()
   556  				Expect(elapsed).To(BeNumerically(
   557  					"~",
   558  					200*time.Millisecond,
   559  					50*time.Millisecond,
   560  				))
   561  
   562  				// Reject the fourth connection and verify that it was sent after
   563  				// waiting the four times the configured interval:
   564  				conn = Accept(listener)
   565  				Reject(conn)
   566  				elapsed = time.Since(start)
   567  				Expect(elapsed).To(BeNumerically(
   568  					"~",
   569  					400*time.Millisecond,
   570  					50*time.Millisecond,
   571  				))
   572  			}()
   573  
   574  			// Wrap the transport setting the jitter to zero so that we can reliably
   575  			// measure retry times:
   576  			wrapper, err := NewTransportWrapper().
   577  				Logger(logger).
   578  				Limit(3).
   579  				Interval(100 * time.Millisecond).
   580  				Jitter(0).
   581  				Build(ctx)
   582  			Expect(err).ToNot(HaveOccurred())
   583  			defer func() {
   584  				err = wrapper.Close()
   585  				Expect(err).ToNot(HaveOccurred())
   586  			}()
   587  			client := &http.Client{
   588  				Transport: wrapper.Wrap(transport),
   589  				Timeout:   10 * time.Second,
   590  			}
   591  
   592  			// Send the request:
   593  			response, err := client.Get(address)
   594  			Expect(err).To(HaveOccurred())
   595  			Expect(response).To(BeNil())
   596  			message := err.Error()
   597  			Expect(message).To(ContainSubstring("PROTOCOL_ERROR"))
   598  		})
   599  	})
   600  
   601  	When("Retry is disabled", func() {
   602  		It("Doesn't tolerate error", func() {
   603  			// Run the HTTP/2 server:
   604  			go func() {
   605  				defer GinkgoRecover()
   606  				conn := Accept(listener)
   607  				Reject(conn)
   608  			}()
   609  
   610  			// Wrap the transport:
   611  			wrapper, err := NewTransportWrapper().
   612  				Logger(logger).
   613  				Limit(0).
   614  				Interval(100 * time.Millisecond).
   615  				Jitter(0).
   616  				Build(ctx)
   617  			Expect(err).ToNot(HaveOccurred())
   618  			defer func() {
   619  				err = wrapper.Close()
   620  				Expect(err).ToNot(HaveOccurred())
   621  			}()
   622  			client := &http.Client{
   623  				Transport: wrapper.Wrap(transport),
   624  				Timeout:   10 * time.Second,
   625  			}
   626  
   627  			// Send the request:
   628  			response, err := client.Get(address)
   629  			Expect(err).To(HaveOccurred())
   630  			Expect(response).To(BeNil())
   631  			message := err.Error()
   632  			Expect(message).To(ContainSubstring("PROTOCOL_ERROR"))
   633  		})
   634  	})
   635  })
   636  
   637  var _ = It("Tolerates connection reset by peer", func() {
   638  	var err error
   639  
   640  	// Create a context:
   641  	ctx := context.Background()
   642  
   643  	// Create a listener:
   644  	listener, address := Listen()
   645  	defer func() {
   646  		err = listener.Close()
   647  		Expect(err).ToNot(HaveOccurred())
   648  	}()
   649  
   650  	// Run the server:
   651  	go func() {
   652  		defer GinkgoRecover()
   653  
   654  		// Accept the first connection and close it inmediately. This will trigger
   655  		// the `connection reset by peer` error in the client.
   656  		first := Accept(listener)
   657  		Serve(first, func(w http.ResponseWriter, r *http.Request) {
   658  			w.Header().Set("Content-Type", "application/json")
   659  			w.WriteHeader(http.StatusOK)
   660  			_, err := w.Write([]byte("{"))
   661  			Expect(err).ToNot(HaveOccurred())
   662  			err = first.Close()
   663  			Expect(err).ToNot(HaveOccurred())
   664  		})
   665  
   666  		// Accept the second connection and handle it correctly.
   667  		second := Accept(listener)
   668  		Serve(second, func(w http.ResponseWriter, r *http.Request) {
   669  			w.Header().Set("Content-Type", "application/json")
   670  			w.WriteHeader(http.StatusOK)
   671  			_, err := w.Write([]byte("{}"))
   672  			Expect(err).ToNot(HaveOccurred())
   673  		})
   674  	}()
   675  
   676  	// Wrap the transport:
   677  	wrapper, err := NewTransportWrapper().
   678  		Logger(logger).
   679  		Interval(100 * time.Millisecond).
   680  		Jitter(0).
   681  		Build(ctx)
   682  	Expect(err).ToNot(HaveOccurred())
   683  	defer func() {
   684  		err = wrapper.Close()
   685  		Expect(err).ToNot(HaveOccurred())
   686  	}()
   687  	client := &http.Client{
   688  		Transport: wrapper.Wrap(&http.Transport{
   689  			TLSClientConfig: &tls.Config{
   690  				InsecureSkipVerify: true,
   691  			},
   692  			ForceAttemptHTTP2: true,
   693  		}),
   694  		Timeout: 10 * time.Second,
   695  	}
   696  
   697  	// Send the request:
   698  	response, err := client.Get(address)
   699  	Expect(err).ToNot(HaveOccurred())
   700  	Expect(response).ToNot(BeNil())
   701  	Expect(response.StatusCode).To(Equal(http.StatusOK))
   702  	body, err := io.ReadAll(response.Body)
   703  	Expect(err).ToNot(HaveOccurred())
   704  	Expect(body).To(MatchJSON("{}"))
   705  })
   706  
   707  var _ = It("Puts do NOT tolerate connection reset by peer", func() {
   708  	var err error
   709  
   710  	// Create a context:
   711  	ctx := context.Background()
   712  
   713  	// Create a listener:
   714  	listener, address := Listen()
   715  	defer func() {
   716  		err = listener.Close()
   717  		Expect(err).ToNot(HaveOccurred())
   718  	}()
   719  
   720  	// Run the server:
   721  	go func() {
   722  		defer GinkgoRecover()
   723  
   724  		// Accept the first connection and close it inmediately. This will trigger
   725  		// the `connection reset by peer` error in the client.
   726  		first := Accept(listener)
   727  		Serve(first, func(w http.ResponseWriter, r *http.Request) {
   728  			w.Header().Set("Content-Type", "application/json")
   729  			w.WriteHeader(http.StatusOK)
   730  			_, err := w.Write([]byte("{"))
   731  			Expect(err).ToNot(HaveOccurred())
   732  			err = first.Close()
   733  			Expect(err).ToNot(HaveOccurred())
   734  		})
   735  	}()
   736  
   737  	// Wrap the transport:
   738  	wrapper, err := NewTransportWrapper().
   739  		Logger(logger).
   740  		Interval(100 * time.Millisecond).
   741  		Jitter(0).
   742  		Build(ctx)
   743  	Expect(err).ToNot(HaveOccurred())
   744  	defer func() {
   745  		err = wrapper.Close()
   746  		Expect(err).ToNot(HaveOccurred())
   747  	}()
   748  	client := &http.Client{
   749  		Transport: wrapper.Wrap(&http.Transport{
   750  			TLSClientConfig: &tls.Config{
   751  				InsecureSkipVerify: true,
   752  			},
   753  			ForceAttemptHTTP2: true,
   754  		}),
   755  		Timeout: 10 * time.Second,
   756  	}
   757  
   758  	// Send the request:
   759  	response, err := client.Post(address, "", nil)
   760  	Expect(err).To(HaveOccurred())
   761  	Expect(response).To(BeNil())
   762  })
   763  
   764  var _ = It("Doesn't change request body object", func() {
   765  	var err error
   766  
   767  	// Create a context:
   768  	ctx := context.Background()
   769  
   770  	// Prepare the server:
   771  	server := MakeTCPServer()
   772  	defer server.Close()
   773  	server.AppendHandlers(
   774  		RespondWithJSON(http.StatusOK, `{}`),
   775  	)
   776  
   777  	// Wrap the transport:
   778  	wrapper, err := NewTransportWrapper().
   779  		Logger(logger).
   780  		Jitter(0).
   781  		Build(ctx)
   782  	Expect(err).ToNot(HaveOccurred())
   783  	defer func() {
   784  		err = wrapper.Close()
   785  		Expect(err).ToNot(HaveOccurred())
   786  	}()
   787  	client := &http.Client{
   788  		Transport: wrapper.Wrap(&http.Transport{}),
   789  		Timeout:   10 * time.Second,
   790  	}
   791  
   792  	// Send the request:
   793  	body := io.NopCloser(strings.NewReader(`{}`))
   794  	addr, err := url.Parse(server.URL())
   795  	Expect(err).ToNot(HaveOccurred())
   796  	request := &http.Request{
   797  		Method: http.MethodGet,
   798  		URL:    addr,
   799  		Body:   body,
   800  	}
   801  	_, err = client.Do(request)
   802  	Expect(err).ToNot(HaveOccurred())
   803  	Expect(request.Body).To(Equal(body))
   804  })
   805  
   806  var _ = It("Tolerates unepected EOF", func() {
   807  	var err error
   808  
   809  	// Create a context:
   810  	ctx := context.Background()
   811  
   812  	// Create a listener:
   813  	listener, address := Listen()
   814  	defer func() {
   815  		err = listener.Close()
   816  		Expect(err).ToNot(HaveOccurred())
   817  	}()
   818  
   819  	// Run the server:
   820  	go func() {
   821  		defer GinkgoRecover()
   822  
   823  		// Accept the first connection and handle it with a server that will close
   824  		// the it after sending only half of the response body. This will trigger
   825  		// the `EOF` error in the client.
   826  		first := Accept(listener)
   827  		Serve(first, func(w http.ResponseWriter, r *http.Request) {
   828  			w.Header().Set("Content-Type", "application/json")
   829  			w.WriteHeader(http.StatusOK)
   830  			_, err := w.Write([]byte("{"))
   831  			Expect(err).ToNot(HaveOccurred())
   832  			err = first.Close()
   833  			Expect(err).ToNot(HaveOccurred())
   834  		})
   835  
   836  		// Accept the second connection and handle it correctly.
   837  		second := Accept(listener)
   838  		Serve(second, func(w http.ResponseWriter, r *http.Request) {
   839  			w.Header().Set("Content-Type", "application/json")
   840  			w.WriteHeader(http.StatusOK)
   841  			_, err := w.Write([]byte("{}"))
   842  			Expect(err).ToNot(HaveOccurred())
   843  		})
   844  	}()
   845  
   846  	// Wrap the transport:
   847  	wrapper, err := NewTransportWrapper().
   848  		Logger(logger).
   849  		Interval(100 * time.Millisecond).
   850  		Jitter(0).
   851  		Build(ctx)
   852  	Expect(err).ToNot(HaveOccurred())
   853  	defer func() {
   854  		err = wrapper.Close()
   855  		Expect(err).ToNot(HaveOccurred())
   856  	}()
   857  	client := &http.Client{
   858  		Transport: wrapper.Wrap(&http.Transport{
   859  			TLSClientConfig: &tls.Config{
   860  				InsecureSkipVerify: true,
   861  			},
   862  			ForceAttemptHTTP2: true,
   863  		}),
   864  		Timeout: 10 * time.Second,
   865  	}
   866  
   867  	// Send the request:
   868  	response, err := client.Get(address)
   869  	Expect(err).ToNot(HaveOccurred())
   870  	Expect(response).ToNot(BeNil())
   871  	Expect(response.StatusCode).To(Equal(http.StatusOK))
   872  	body, err := io.ReadAll(response.Body)
   873  	Expect(err).ToNot(HaveOccurred())
   874  	Expect(body).To(MatchJSON("{}"))
   875  })
   876  
   877  // Listen creates an HTTP/2 listener.
   878  func Listen() (listener net.Listener, address string) {
   879  	// Create a TLS listener that will be used to process incoming requests
   880  	// simulating an HTTP/2 server:
   881  	listener, err := tls.Listen("tcp", "127.0.0.1:0", &tls.Config{
   882  		Certificates: []tls.Certificate{
   883  			LocalhostCertificate(),
   884  		},
   885  		NextProtos: []string{
   886  			http2.NextProtoTLS,
   887  		},
   888  	})
   889  	Expect(err).ToNot(HaveOccurred())
   890  
   891  	// Calculate the listener URL:
   892  	address = "https://" + listener.Addr().String()
   893  
   894  	return
   895  }
   896  
   897  // Accept accepts an HTTP/2 connection.
   898  func Accept(listener net.Listener) net.Conn {
   899  	// Accept the connection and complete the TLS handshake.
   900  	conn, err := listener.Accept()
   901  	Expect(err).ToNot(HaveOccurred())
   902  	err = conn.(*tls.Conn).Handshake()
   903  	Expect(err).ToNot(HaveOccurred())
   904  
   905  	// Return the connection:
   906  	return conn
   907  }
   908  
   909  // Reject sends an HTTP/2 go away frame to the given connection and then closes it.
   910  func Reject(conn net.Conn) {
   911  	// Read the HTTP2 connection preface, otherwise the client won't reach the part of
   912  	// the code where the protocol error is detected:
   913  	buffer := make([]byte, len(http2.ClientPreface))
   914  	count, err := conn.Read(buffer)
   915  	Expect(err).ToNot(HaveOccurred())
   916  	Expect(count).To(Equal(len(http2.ClientPreface)))
   917  	Expect(string(buffer)).To(Equal(http2.ClientPreface))
   918  
   919  	// Send the go away frame:
   920  	framer := http2.NewFramer(conn, conn)
   921  	err = framer.WriteGoAway(0, http2.ErrCodeStreamClosed, nil)
   922  	Expect(err).ToNot(HaveOccurred())
   923  
   924  	// Close the connection:
   925  	err = conn.Close()
   926  	Expect(err).ToNot(HaveOccurred())
   927  }
   928  
   929  // Serve handles request received from the given connection using a real HTTP/2 server and the given
   930  // handler function.
   931  func Serve(conn net.Conn, handler http.HandlerFunc) {
   932  	server := &http2.Server{}
   933  	server.ServeConn(conn, &http2.ServeConnOpts{
   934  		Handler: handler,
   935  	})
   936  }