Skip to content

Commit

Permalink
Added 'additive' proxy strategy behavior that sets additional headers…
Browse files Browse the repository at this point in the history
… to the request (#270)
  • Loading branch information
azagniotov authored Apr 3, 2021
1 parent e441a26 commit 75ae8e8
Show file tree
Hide file tree
Showing 15 changed files with 200 additions and 31 deletions.
2 changes: 2 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#### 7.3.2-SNAPSHOT

* PR [#270](https://github.com/azagniotov/stubby4j/pull/270) - Added `additive` proxy strategy behavior that sets additional headers to the request (https://github.com/azagniotov)

#### 7.3.1

* PR [#264](https://github.com/azagniotov/stubby4j/pull/264) - Multiple proxy configurations support (https://github.com/azagniotov)
Expand Down
29 changes: 25 additions & 4 deletions docs/REQUEST_PROXYING.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,20 +122,41 @@ This can be anything describing your proxy configuration.

#### strategy (`required`)

Describes how the request to-be-proxied should be proxied. Currently only `as-is` strategy is supported, which means that request will be proxied without any changes/modifications before being sent to the proxy service. In the future enhancements to the request proxying functionality, more strategies will be supported.
Describes how the request to-be-proxied should be proxied. Currently only the following strategy values are supported:
* `as-is`: no changes/modifications will be applied to the request before proxying it
* `additive`: additive changes will be applied to the request before proxying it. The additive changes currently supported are setting of additional HTTP headers using the `headers` property on the `proxy-config` object.

In the future enhancements to the request proxying functionality, more strategies will be supported.

#### properties (`required`)

Describes the properties of the proxy, e.g.: endpoint, headers, etc. Currently only `endpoint` property is supported. In the future enhancements to the request proxying functionality, more properties will be supported.
A map of key/value pairs describing the properties of the proxy, e.g.: endpoint, etc. Currently only `endpoint` property is supported. In the future enhancements to the request proxying functionality, more properties will be supported.

##### endpoint (`required`)

#### endpoint (`required`)
Must be defined under the `properties` property. Describes the target service endpoint where the request will be proxied to. This should be a protocol scheme + fully qualified domain name (i.e.: `FQDN`) without any URI paths:

Describes the target service endpoint where the request will be proxied to. This should be a protocol scheme + fully qualified domain name (i.e.: `FQDN`) without any URI paths:
* Correct: `https://jsonplaceholder.typicode.com`
* Incorrect: `jsonplaceholder.typicode.com`
* Incorrect: `https://jsonplaceholder.typicode.com/`
* Incorrect: `https://jsonplaceholder.typicode.com/posts/1`

#### headers (`optional`)

A map of key/value pairs describing an HTTP header name and its value. The `headers` property can be used when `strategy` property is set to `additive`. The headers will be added to the request being proxied in an additive manner, i.e.: they will not replace the headers already set on the request.

```yaml
- proxy-config:
uuid: some-other-unique-name
strategy: additive
properties:
endpoint: https://jsonplaceholder.typicode.com
headers:
content-type: application/json+special
x-custom-header: something/unique
x-custom-header-2: another/thing
```

## Application of proxy config at runtime

Request proxying happens when there is at least one `proxy config` object defined in the YAML config and an incoming HTTP request did not match any of the declared `stubby4j`'s stubs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ public void shouldReturnProxiedResponseUsingSpecificProxyConfig_WhenStubsWereNot
// The 'null' overrides the default value "gzip", also I had to .disableContentCompression() on WEB_CLIENT
httpHeaders.setAcceptEncoding(null);

// The 'some-unique-name' is actually set in include-proxy-config.yaml
// The 'some-unique-name' is actually set in 'proxy-config' object defined in resources/yaml/include-proxy-config.yaml
httpHeaders.set(HEADER_X_STUBBY_PROXY_CONFIG, "some-unique-name");
request.setHeaders(httpHeaders);

Expand Down Expand Up @@ -175,7 +175,9 @@ public void should_UpdateStubbedProxyConfig_WithJsonRequest_ByValidUuid() throws
assertThat(getResponseContent).isEqualTo(
"- proxy-config:\n" +
" uuid: some-unique-name\n" +
" strategy: custom\n" +
" strategy: additive\n" +
" headers:\n" +
" x-original-stubby4j-custom-header: custom/value\n" +
" properties:\n" +
" endpoint: https://jsonplaceholder.typicode.com");

Expand Down Expand Up @@ -208,7 +210,9 @@ public void should_UpdateStubbedProxyConfig_WithJsonRequest_ByValidUuid() throws
assertThat(getResponseContent).isEqualTo(
"- proxy-config:\n" +
" uuid: some-unique-name\n" +
" strategy: custom\n" +
" strategy: additive\n" +
" headers:\n" +
" x-custom-header: custom/value\n" +
" properties:\n" +
" endpoint: https://UPDATED.com");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
{
"proxy-config": {
"uuid": "some-unique-name",
"strategy": "custom",
"strategy": "additive",
"headers": {
"x-custom-header": "custom/value"
},
"properties": {
"endpoint": "https://UPDATED.com"
}
Expand Down
8 changes: 5 additions & 3 deletions src/functional-test/resources/yaml/include-proxy-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,22 @@

- proxy-config:
uuid: some-unique-name
strategy: custom
strategy: additive
headers:
x-original-stubby4j-custom-header: custom/value
properties:
endpoint: https://jsonplaceholder.typicode.com

- proxy-config:
description: description-2
uuid: some-unique-name-two
strategy: custom
strategy: additive
properties:
endpoint: https://google.com

- proxy-config:
description: description-3
uuid: some-unique-name-three
strategy: custom
strategy: additive
properties:
endpoint: https://google.com
Original file line number Diff line number Diff line change
Expand Up @@ -967,7 +967,7 @@ public void shouldUnmarshall_toProxyConfigs() throws Exception {

final StubProxyConfig customStubProxyConfig = proxyConfigs.get("some-unique-name");
assertThat(customStubProxyConfig.getUUID()).isEqualTo("some-unique-name");
assertThat(customStubProxyConfig.getStrategy()).isEqualTo(StubProxyStrategy.CUSTOM);
assertThat(customStubProxyConfig.getStrategy()).isEqualTo(StubProxyStrategy.ADDITIVE);
assertThat(customStubProxyConfig.getProperties().size()).isEqualTo(1);
assertThat(customStubProxyConfig.getProperties().get("endpoint")).isEqualTo("https://jsonplaceholder.typicode.com");

Expand All @@ -985,7 +985,7 @@ public void shouldUnmarshall_toProxyConfigs() throws Exception {
"- proxy-config:\n" +
" description: woah! this is a unique proxy-config\n" +
" uuid: some-unique-name\n" +
" strategy: custom\n" +
" strategy: additive\n" +
" properties:\n" +
" endpoint: https://jsonplaceholder.typicode.com\n");
}
Expand Down Expand Up @@ -1014,7 +1014,7 @@ public void shouldUnmarshall_toProxyConfigsWithStubs() throws Exception {

final StubProxyConfig customStubProxyConfig = proxyConfigs.get("some-unique-name");
assertThat(customStubProxyConfig.getUUID()).isEqualTo("some-unique-name");
assertThat(customStubProxyConfig.getStrategy()).isEqualTo(StubProxyStrategy.CUSTOM);
assertThat(customStubProxyConfig.getStrategy()).isEqualTo(StubProxyStrategy.ADDITIVE);
assertThat(customStubProxyConfig.getProperties().size()).isEqualTo(1);
assertThat(customStubProxyConfig.getProperties().get("endpoint")).isEqualTo("https://jsonplaceholder.typicode.com");

Expand All @@ -1027,7 +1027,7 @@ public void shouldUnmarshall_toProxyConfigsWithStubs() throws Exception {
assertThat(customStubProxyConfig.getProxyConfigAsYAML()).isEqualTo(
"- proxy-config:\n" +
" uuid: some-unique-name\n" +
" strategy: custom\n" +
" strategy: additive\n" +
" properties:\n" +
" endpoint: https://jsonplaceholder.typicode.com\n");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@

- proxy-config:
uuid: some-unique-name
strategy: custom
strategy: additive
properties:
endpoint: https://jsonplaceholder.typicode.com
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

- proxy-config:
uuid: some-unique-name
strategy: custom
strategy: additive
properties:
endpoint: https://jsonplaceholder.typicode.com

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@
- proxy-config:
description: woah! this is a unique proxy-config
uuid: some-unique-name
strategy: custom
strategy: additive
properties:
endpoint: https://jsonplaceholder.typicode.com
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@

- proxy-config:
uuid: another-unique-name
strategy: custom
strategy: additive
properties:
endpoint: https://jsonplaceholder.typicode.com
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ public StubProxyStrategy getStrategy() {
return strategy;
}

public boolean isAdditiveStrategy() {
return strategy == StubProxyStrategy.ADDITIVE;
}

public final Map<String, String> getHeaders() {
return new HashMap<>(headers);
}
Expand Down Expand Up @@ -102,12 +106,7 @@ public static final class Builder extends AbstractBuilder<StubProxyConfig> {

public Builder() {
super();
this.description = null;
this.uuid = DEFAULT_UUID;
this.strategy = null;
this.headers = new LinkedHashMap<>();
this.properties = new LinkedHashMap<>();
this.proxyConfigAsYAML = null;
reset();
}

public Builder withDescription(final String description) {
Expand Down Expand Up @@ -170,15 +169,20 @@ public StubProxyConfig build() {
properties,
proxyConfigAsYAML);

reset();

this.fieldNameAndValues.clear();

return stubProxyConfig;
}

private void reset() {
this.description = null;
this.uuid = DEFAULT_UUID;
this.strategy = null;
this.strategy = StubProxyStrategy.AS_IS;
this.headers = new LinkedHashMap<>();
this.properties = new LinkedHashMap<>();
this.proxyConfigAsYAML = null;
this.fieldNameAndValues.clear();

return stubProxyConfig;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,21 @@
import static io.github.azagniotov.stubby4j.utils.StringUtils.toLower;

public enum StubProxyStrategy {
/**
* No changes/modifications will be applied to the request before proxying it.
* <p>
* See: https://github.com/azagniotov/stubby4j/blob/master/docs/REQUEST_PROXYING.md#supported-yaml-properties
*/
AS_IS("as-is"),
CUSTOM("custom");

/**
* Additive changes will be applied to the request before proxying it. The additive changes
* currently supported are setting of additional HTTP headers using the `headers` property on the
* `proxy-config` object.
* <p>
* See: https://github.com/azagniotov/stubby4j/blob/master/docs/REQUEST_PROXYING.md#supported-yaml-properties
*/
ADDITIVE("additive");

private static final Map<String, StubProxyStrategy> PROPERTY_NAME_TO_ENUM_MEMBER;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,9 @@ private StubResponse proxyRequest(final StubHttpLifecycle incomingHttpLifecycle)

try {
incomingRequest.getHeaders().put(HEADER_X_STUBBY_PROXY_REQUEST, proxyRoundTripUuid);

handleIfAdditiveProxyStrategy(incomingRequest, proxyConfig);

final StubbyResponse stubbyResponse = stubbyHttpTransport.httpRequestFromStub(incomingRequest, proxyEndpoint);
for (Map.Entry<String, List<String>> entry : stubbyResponse.headers().entrySet()) {
final String headerName = ObjectUtils.isNull(entry.getKey()) ? "null" : entry.getKey();
Expand Down Expand Up @@ -253,6 +256,16 @@ private StubResponse proxyRequest(final StubHttpLifecycle incomingHttpLifecycle)
}
}

private void handleIfAdditiveProxyStrategy(final StubRequest incomingRequest, final StubProxyConfig proxyConfig) {
if (proxyConfig.isAdditiveStrategy()) {
if (proxyConfig.hasHeaders()) {
for (final Map.Entry<String, String> headerEntry : proxyConfig.getHeaders().entrySet()) {
incomingRequest.getHeaders().put(headerEntry.getKey(), headerEntry.getValue());
}
}
}
}

private void recordResponse(StubHttpLifecycle incomingRequest, StubHttpLifecycle matchedStub, StubResponse matchedStubResponse) {
final String recordingSource = String.format("%s%s", matchedStubResponse.getBody(), incomingRequest.getUrl());
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,22 @@ public void cleanup() throws Exception {
RegexParser.REGEX_PATTERN_CACHE.clear();
}

@Test
public void stubbedProxyConfigDefaultStrategyNotAdditive() throws Exception {

final StubProxyConfig stubProxyConfig = builder.build();
assertThat(stubProxyConfig.isAdditiveStrategy()).isFalse();
assertThat(stubProxyConfig.getStrategy()).isEqualTo(StubProxyStrategy.AS_IS);
}

@Test
public void stubbedProxyConfigStrategyAdditive() throws Exception {

final StubProxyConfig stubProxyConfig = builder.withStrategy(StubProxyStrategy.ADDITIVE.toString()).build();
assertThat(stubProxyConfig.isAdditiveStrategy()).isTrue();
assertThat(stubProxyConfig.getStrategy()).isEqualTo(StubProxyStrategy.ADDITIVE);
}

@Test
public void stubbedProxyConfigHasNoHeaders() throws Exception {

Expand Down Expand Up @@ -173,7 +189,7 @@ public void stubbedProxyConfigNotEqualsAssertingConfig() throws Exception {

final StubProxyConfig assertingStubProxyConfig = builder
.withUuid("unique")
.withStrategy("custom")
.withStrategy("additive")
.withPropertyEndpoint("http://google.com")
.build();

Expand Down
Loading

0 comments on commit 75ae8e8

Please sign in to comment.