Skip to content

Commit

Permalink
feat: add streaming mode http proxy test (#258)
Browse files Browse the repository at this point in the history
Adds a single test for streaming with HTTP proxy configuration
(client-side).

Ideally, the next bit of work will be..
- to support verifying that events and polling also go through the
proxy.
- extending to server SDKs (should be just adding to the
`server_side_stream_all.go`, but requires an SDK to test it)

The purpose of this test is to check that an SDK can make a request via
a configured HTTP Proxy.

In this mode, the SDK sends requests to the proxy, and the request body
contains the intended URL that the proxy should connect to on behalf of
the SDK.

Future work might be to support other proxy modes, like HTTPS proxy
(where the connection between SDK and proxy is encrypted) or SOCKS4/5.
  • Loading branch information
cwaldren-ld authored Nov 15, 2024
1 parent ec1ccdf commit b4e149e
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 0 deletions.
10 changes: 10 additions & 0 deletions docs/service_spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,14 @@ This means the SDK supports setting the wrapper name and version and includes th

When this capability is set a `wrapper` configuration will be included with a subset of tests.


### Capability `"http-proxy"`

This indicates the SDK is capable of configuring an HTTP proxy for its network requests.

All requests should be sent to the proxy. This is generally implemented in an SDK via standard networking
library capabilities, such as setting an environment variable (like `http_proxy`) or a configuration option.

### Stop test service: `DELETE /`

The test harness sends this request at the end of a test run if you have specified `--stop-service-at-end` on the [command line](./running.md). The test service should simply quit. This is a convenience so CI scripts can simply start the test service in the background and assume it will be stopped for them.
Expand Down Expand Up @@ -291,6 +299,8 @@ A `POST` request indicates that the test harness wants to start an instance of t
* `wrapper` (object, optional): If specified contains wrapper configuration.
* `name`: The name of the wrapper.
* `version`: The version of the wrapper.
* `proxy` (object, optional): If specified contains proxy configuration.
* `httpProxy` (string, optional): An HTTP proxy, of the form `http://host:port`.

The response to a valid request is any HTTP `2xx` status, with a `Location` header whose value is the URL of the test service resource representing this SDK client instance (that is, the one that would be used for "Close client" or "Send command" as described below).

Expand Down
2 changes: 2 additions & 0 deletions sdktests/client_side_stream_all.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ func doClientSideStreamRequestTest(t *ldtest.T) {
streamTests := NewCommonStreamingTests(t, "doClientSideStreamRequestTest",
WithCredential(envIDOrMobileKey))

streamTests.RequestViaHTTPProxy(t)

streamTests.RequestMethodAndHeaders(t, envIDOrMobileKey)

requestPathMatcher := func(method flagRequestMethod) m.Matcher {
Expand Down
9 changes: 9 additions & 0 deletions sdktests/common_tests_base.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,3 +233,12 @@ func (c commonTestsBase) sendArbitraryEvent(t *ldtest.T, client *SDKClient) {
}
client.SendCustomEvent(t, params)
}

func (c commonTestsBase) withHTTPProxy(url string) SDKConfigurer {
return helpers.ConfigOptionFunc[servicedef.SDKConfigParams](func(configOut *servicedef.SDKConfigParams) error {
configOut.Proxy = o.Some(servicedef.SDKConfigProxyParams{
HTTPProxy: o.Some(url),
})
return nil
})
}
38 changes: 38 additions & 0 deletions sdktests/common_tests_stream_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package sdktests

import (
"fmt"
"net/url"
"strings"
"time"

Expand Down Expand Up @@ -182,3 +183,40 @@ func (c CommonStreamingTests) RequestContextProperties(t *ldtest.T, getPath stri
}
})
}

func (c CommonStreamingTests) RequestViaHTTPProxy(t *ldtest.T) {
t.RequireCapability(servicedef.CapabilityHTTPProxy)
t.Run("http proxy", func(t *ldtest.T) {
dataSource, configurers := c.setupDataSources(t, nil)

// The idea here is that we'll configure the SDK's service endpoints with an arbitrary host, but with the
// correct path that the test harness expects (like /endpoints/1). Then, we'll inject the actual test harness's
// endpoint via the HTTP Proxy configuration.
//
// The SDK should therefore:
// 1. Open a socket to the test harness's host and port
// 2. Send an HTTP request that has the arbitrary host and the correct path
//
// If the SDK didn't support proxying, then it would attempt to connect to the arbitrary host and
// the harness should fail the connection assertion.
streamURI := strings.Replace(dataSource.Endpoint().BaseURL(), "localhost", "not.valid.local", 1)

u, err := url.Parse(dataSource.Endpoint().BaseURL())
if err != nil {
t.Errorf("unexpected error parsing URL: %s", err)
t.FailNow()
}
u.Path = ""

_ = NewSDKClient(t, c.baseSDKConfigurationPlus(
append(configurers,
WithStreamingConfig(servicedef.SDKConfigStreamingParams{
BaseURI: streamURI,
}),
c.withHTTPProxy(u.String()),
)...)...)

_, err = dataSource.Endpoint().AwaitConnection(time.Second)
assert.NoError(t, err)
})
}
5 changes: 5 additions & 0 deletions servicedef/sdk_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type SDKConfigParams struct {
InitCanFail bool `json:"initCanFail,omitempty"`
ServiceEndpoints o.Maybe[SDKConfigServiceEndpointsParams] `json:"serviceEndpoints,omitempty"`
TLS o.Maybe[SDKConfigTLSParams] `json:"tls,omitempty"`
Proxy o.Maybe[SDKConfigProxyParams] `json:"proxy,omitempty"`
Streaming o.Maybe[SDKConfigStreamingParams] `json:"streaming,omitempty"`
Polling o.Maybe[SDKConfigPollingParams] `json:"polling,omitempty"`
Events o.Maybe[SDKConfigEventParams] `json:"events,omitempty"`
Expand All @@ -33,6 +34,10 @@ type SDKConfigTLSParams struct {
CustomCAFile string `json:"customCAFile,omitempty"`
}

type SDKConfigProxyParams struct {
HTTPProxy o.Maybe[string] `json:"httpProxy,omitempty"`
}

type SDKConfigServiceEndpointsParams struct {
Streaming string `json:"streaming,omitempty"`
Polling string `json:"polling,omitempty"`
Expand Down
4 changes: 4 additions & 0 deletions servicedef/service_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ const (
// CapabilityWrapper indicates that the SDK supports setting wrapper name and version and including them in request
// headers.
CapabilityWrapper = "wrapper"

// CapabilityHTTPProxy indicates that the SDK supports setting an HTTP proxy, through which the SDK will
// make all requests.
CapabilityHTTPProxy = "http-proxy"
)

type StatusRep struct {
Expand Down

0 comments on commit b4e149e

Please sign in to comment.