...

Source file src/github.com/launchdarkly/go-server-sdk/v6/ldcomponents/http_configuration_builder_test.go

Documentation: github.com/launchdarkly/go-server-sdk/v6/ldcomponents

     1  package ldcomponents
     2  
     3  import (
     4  	"crypto/x509"
     5  	"net/http"
     6  	"net/http/httptest"
     7  	"net/url"
     8  	"os"
     9  	"reflect"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/launchdarkly/go-server-sdk/v6/interfaces"
    14  	"github.com/launchdarkly/go-server-sdk/v6/internal"
    15  	"github.com/launchdarkly/go-server-sdk/v6/subsystems"
    16  
    17  	helpers "github.com/launchdarkly/go-test-helpers/v3"
    18  	"github.com/launchdarkly/go-test-helpers/v3/httphelpers"
    19  
    20  	"github.com/stretchr/testify/assert"
    21  	"github.com/stretchr/testify/require"
    22  )
    23  
    24  func TestHTTPConfigurationBuilder(t *testing.T) {
    25  	basicConfig := subsystems.BasicClientContext{SDKKey: "test-key"}
    26  
    27  	t.Run("defaults", func(t *testing.T) {
    28  		c, err := HTTPConfiguration().Build(basicConfig)
    29  		require.NoError(t, err)
    30  
    31  		headers := c.DefaultHeaders
    32  		assert.Len(t, headers, 2)
    33  		assert.Equal(t, "test-key", headers.Get("Authorization"))
    34  		assert.Equal(t, "GoClient/"+internal.SDKVersion, headers.Get("User-Agent"))
    35  
    36  		client := c.CreateHTTPClient()
    37  		assert.Equal(t, DefaultConnectTimeout, client.Timeout)
    38  
    39  		require.NotNil(t, client.Transport)
    40  		transport := client.Transport.(*http.Transport)
    41  		require.NotNil(t, transport)
    42  		assert.Equal(t, reflect.ValueOf(http.ProxyFromEnvironment).Pointer(), reflect.ValueOf(transport.Proxy).Pointer())
    43  		assert.Equal(t, 100, transport.MaxIdleConns)
    44  		assert.Equal(t, 90*time.Second, transport.IdleConnTimeout)
    45  		assert.Equal(t, 10*time.Second, transport.TLSHandshakeTimeout)
    46  		assert.Equal(t, 1*time.Second, transport.ExpectContinueTimeout)
    47  	})
    48  
    49  	t.Run("CACert", func(t *testing.T) {
    50  		httphelpers.WithSelfSignedServer(httphelpers.HandlerWithStatus(200), func(server *httptest.Server, certData []byte, certs *x509.CertPool) {
    51  			_, err := HTTPConfiguration().
    52  				CACert(certData).
    53  				Build(basicConfig)
    54  			require.NoError(t, err)
    55  		})
    56  	})
    57  
    58  	t.Run("CACert with invalid certificate", func(t *testing.T) {
    59  		badCertData := []byte("no")
    60  		_, err := HTTPConfiguration().
    61  			CACert(badCertData).
    62  			Build(basicConfig)
    63  		require.Error(t, err)
    64  	})
    65  
    66  	t.Run("CACertFile", func(t *testing.T) {
    67  		httphelpers.WithSelfSignedServer(httphelpers.HandlerWithStatus(200), func(server *httptest.Server, certData []byte, certs *x509.CertPool) {
    68  			helpers.WithTempFileData(certData, func(filename string) {
    69  				_, err := HTTPConfiguration().
    70  					CACertFile(filename).
    71  					Build(basicConfig)
    72  				require.NoError(t, err)
    73  			})
    74  		})
    75  	})
    76  
    77  	t.Run("CACertFile with invalid certificate", func(t *testing.T) {
    78  		badCertData := []byte("no")
    79  		helpers.WithTempFileData(badCertData, func(filename string) {
    80  			_, err := HTTPConfiguration().
    81  				CACertFile(filename).
    82  				Build(basicConfig)
    83  			require.Error(t, err)
    84  		})
    85  	})
    86  
    87  	t.Run("CACertFile with missing file", func(t *testing.T) {
    88  		helpers.WithTempFile(func(filename string) {
    89  			_ = os.Remove(filename)
    90  			_, err := HTTPConfiguration().
    91  				CACertFile(filename).
    92  				Build(basicConfig)
    93  			require.Error(t, err)
    94  		})
    95  	})
    96  
    97  	t.Run("ConnectTimeout", func(t *testing.T) {
    98  		timeout := 700 * time.Millisecond
    99  		c1, err := HTTPConfiguration().
   100  			ConnectTimeout(timeout).
   101  			Build(basicConfig)
   102  		require.NoError(t, err)
   103  
   104  		client1 := c1.CreateHTTPClient()
   105  		assert.Equal(t, timeout, client1.Timeout)
   106  
   107  		c2, err := HTTPConfiguration().
   108  			ConnectTimeout(-1 * time.Millisecond).
   109  			Build(basicConfig)
   110  		require.NoError(t, err)
   111  
   112  		client2 := c2.CreateHTTPClient()
   113  		assert.Equal(t, DefaultConnectTimeout, client2.Timeout)
   114  
   115  	})
   116  
   117  	t.Run("HTTPClientFactory", func(t *testing.T) {
   118  		hc := &http.Client{Timeout: time.Hour}
   119  
   120  		c, err := HTTPConfiguration().
   121  			HTTPClientFactory(func() *http.Client { return hc }).
   122  			Build(basicConfig)
   123  		require.NoError(t, err)
   124  
   125  		assert.Equal(t, hc, c.CreateHTTPClient())
   126  	})
   127  
   128  	t.Run("ProxyURL", func(t *testing.T) {
   129  		// Create a fake proxy server - really it's just an embedded HTTP server that always
   130  		// returns a 200 status, but the Go HTTP client doesn't know the difference. Seeing
   131  		// a request arrive at our server, with an absolute URL of http://example/ - instead
   132  		// of the request actually going to http://example/ - proves that the proxy setting
   133  		// was respected.
   134  		fakeTargetURL := "http://example/"
   135  		handler, requestsCh := httphelpers.RecordingHandler(httphelpers.HandlerWithStatus(200))
   136  
   137  		httphelpers.WithServer(handler, func(server *httptest.Server) {
   138  			c, err := HTTPConfiguration().
   139  				ProxyURL(server.URL).
   140  				Build(basicConfig)
   141  			require.NoError(t, err)
   142  
   143  			client := c.CreateHTTPClient()
   144  			resp, err := client.Get(fakeTargetURL)
   145  			require.NoError(t, err)
   146  			assert.Equal(t, 200, resp.StatusCode)
   147  
   148  			r := <-requestsCh
   149  			assert.Equal(t, fakeTargetURL, r.Request.RequestURI)
   150  		})
   151  	})
   152  
   153  	t.Run("ProxyURL with basicauth", func(t *testing.T) {
   154  		// See comment in previous test
   155  		fakeTargetURL := "http://example/"
   156  		handler, requestsCh := httphelpers.RecordingHandler(httphelpers.HandlerWithStatus(200))
   157  
   158  		httphelpers.WithServer(handler, func(server *httptest.Server) {
   159  			parsedURL, _ := url.Parse(server.URL)
   160  			urlWithCredentials := "http://lucy:cat@" + parsedURL.Host
   161  			c, err := HTTPConfiguration().
   162  				ProxyURL(urlWithCredentials).
   163  				Build(basicConfig)
   164  			require.NoError(t, err)
   165  			client := c.CreateHTTPClient()
   166  			resp, err := client.Get(fakeTargetURL)
   167  			require.NoError(t, err)
   168  			assert.Equal(t, 200, resp.StatusCode)
   169  			r := <-requestsCh
   170  			assert.Equal(t, fakeTargetURL, r.Request.RequestURI)
   171  			assert.Equal(t, "Basic bHVjeTpjYXQ=", r.Request.Header.Get("Proxy-Authorization"))
   172  		})
   173  	})
   174  
   175  	t.Run("ProxyURL with invalid URL", func(t *testing.T) {
   176  		proxyURL := ":///"
   177  
   178  		_, err := HTTPConfiguration().
   179  			ProxyURL(proxyURL).
   180  			Build(basicConfig)
   181  		require.Error(t, err)
   182  	})
   183  
   184  	t.Run("Custom header set/get", func(t *testing.T) {
   185  		c, err := HTTPConfiguration().
   186  			Header("Custom-Header", "foo").
   187  			Build(basicConfig)
   188  		require.NoError(t, err)
   189  		assert.Equal(t, "foo", c.DefaultHeaders.Get("Custom-Header"))
   190  	})
   191  
   192  	t.Run("Repeat assignments of custom header take latest value", func(t *testing.T) {
   193  		c, err := HTTPConfiguration().
   194  			Header("Custom-Header", "foo").
   195  			Header("Custom-Header", "bar").
   196  			Header("Custom-Header", "baz").
   197  			Build(basicConfig)
   198  		require.NoError(t, err)
   199  		assert.Equal(t, "baz", c.DefaultHeaders.Get("Custom-Header"))
   200  	})
   201  
   202  	t.Run("Custom header values overwrite required headers", func(t *testing.T) {
   203  		c, err := HTTPConfiguration().
   204  			Header("User-Agent", "foo").
   205  			Header("Authorization", "bar").
   206  			Build(basicConfig)
   207  		require.NoError(t, err)
   208  		assert.Equal(t, "foo", c.DefaultHeaders.Get("User-Agent"))
   209  		assert.Equal(t, "bar", c.DefaultHeaders.Get("Authorization"))
   210  	})
   211  
   212  	t.Run("User-Agent", func(t *testing.T) {
   213  		c, err := HTTPConfiguration().
   214  			UserAgent("extra").
   215  			Build(basicConfig)
   216  		require.NoError(t, err)
   217  
   218  		assert.Equal(t, "GoClient/"+internal.SDKVersion+" extra", c.DefaultHeaders.Get("User-Agent"))
   219  	})
   220  
   221  	t.Run("Wrapper", func(t *testing.T) {
   222  		c1, err := HTTPConfiguration().
   223  			Wrapper("FancySDK", "").
   224  			Build(basicConfig)
   225  		require.NoError(t, err)
   226  
   227  		assert.Equal(t, "FancySDK", c1.DefaultHeaders.Get("X-LaunchDarkly-Wrapper"))
   228  
   229  		c2, err := HTTPConfiguration().
   230  			Wrapper("FancySDK", "2.0").
   231  			Build(basicConfig)
   232  		require.NoError(t, err)
   233  
   234  		assert.Equal(t, "FancySDK/2.0", c2.DefaultHeaders.Get("X-LaunchDarkly-Wrapper"))
   235  	})
   236  
   237  	t.Run("tags header", func(t *testing.T) {
   238  		t.Run("no tags", func(t *testing.T) {
   239  			c, err := HTTPConfiguration().Build(basicConfig)
   240  			require.NoError(t, err)
   241  			assert.Nil(t, c.DefaultHeaders.Values("X-LaunchDarkly-Tags"))
   242  		})
   243  
   244  		t.Run("some tags", func(t *testing.T) {
   245  			bc := basicConfig
   246  			bc.ApplicationInfo = interfaces.ApplicationInfo{ApplicationID: "appid", ApplicationVersion: "appver"}
   247  			c, err := HTTPConfiguration().Build(bc)
   248  			require.NoError(t, err)
   249  			assert.Equal(t, "application-id/appid application-version/appver", c.DefaultHeaders.Get("X-LaunchDarkly-Tags"))
   250  		})
   251  	})
   252  
   253  	t.Run("nil safety", func(t *testing.T) {
   254  		var b *HTTPConfigurationBuilder = nil
   255  		b = b.ConnectTimeout(0).Header("a", "b").ProxyURL("c").Wrapper("d", "e")
   256  		_ = b.DescribeConfiguration(subsystems.BasicClientContext{})
   257  		_, _ = b.Build(subsystems.BasicClientContext{})
   258  	})
   259  }
   260  

View as plain text