Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 19 additions & 14 deletions client/src/main/java/io/split/client/RequestDecorator.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import io.split.client.dtos.RequestContext;

import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
//`import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.Header;

import java.util.HashSet;
Expand All @@ -13,8 +14,10 @@
import java.util.Set;
import java.util.List;

class NoOpHeaderDecorator implements CustomHeaderDecorator {
public NoOpHeaderDecorator() {}
class NoOpHeaderDecorator implements CustomHeaderDecorator {
public NoOpHeaderDecorator() {
}

@Override
public Map<String, List<String>> getHeaderOverrides(RequestContext context) {
return new HashMap<>();
Expand All @@ -36,32 +39,33 @@ public final class RequestDecorator {
"content-encoding",
"accept",
"keep-alive",
"x-fastly-debug"
));
"x-fastly-debug"));

public RequestDecorator(CustomHeaderDecorator headerDecorator) {
_headerDecorator = (headerDecorator == null)
? new NoOpHeaderDecorator()
: headerDecorator;
}

public HttpUriRequestBase decorateHeaders(HttpUriRequestBase request) {
public HttpRequest decorateHeaders(HttpRequest request) {
try {
Map<String, List<String>> headers = _headerDecorator.getHeaderOverrides(new RequestContext(convertToMap(request.getHeaders())));
for (Map.Entry entry : headers.entrySet()) {
if (isHeaderAllowed(entry.getKey().toString())) {
List<String> values = (List<String>) entry.getValue();
Map<String, List<String>> headers = _headerDecorator
.getHeaderOverrides(new RequestContext(convertToMap(request.getHeaders())));
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
if (isHeaderAllowed(entry.getKey())) {
List<String> values = entry.getValue();
for (int i = 0; i < values.size(); i++) {
if (i == 0) {
request.setHeader(entry.getKey().toString(), values.get(i));
request.setHeader(entry.getKey(), values.get(i));
} else {
request.addHeader(entry.getKey().toString(), values.get(i));
request.addHeader(entry.getKey(), values.get(i));
}
}
}
}
} catch (Exception e) {
throw new IllegalArgumentException(String.format("Problem adding custom headers to request decorator: %s", e), e);
throw new IllegalArgumentException(
String.format("Problem adding custom headers to request decorator: %s", e), e);
}

return request;
Expand All @@ -70,9 +74,10 @@ public HttpUriRequestBase decorateHeaders(HttpUriRequestBase request) {
private boolean isHeaderAllowed(String headerName) {
return !forbiddenHeaders.contains(headerName.toLowerCase());
}

private Map<String, List<String>> convertToMap(Header[] to_convert) {
Map<String, List<String>> to_return = new HashMap<String, List<String>>();
for (Integer i = 0; i < to_convert.length; i++ ) {
for (Integer i = 0; i < to_convert.length; i++) {
if (!to_return.containsKey(to_convert[i].getName())) {
to_return.put(to_convert[i].getName(), new ArrayList<String>());
}
Expand Down
203 changes: 121 additions & 82 deletions client/src/main/java/io/split/client/SplitFactoryImpl.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -42,17 +43,19 @@ public class HttpImpressionsSender implements ImpressionsSender {
private final ImpressionsManager.Mode _mode;
private final TelemetryRuntimeProducer _telemetryRuntimeProducer;

public static HttpImpressionsSender create(SplitHttpClient client, URI eventsRootEndpoint, ImpressionsManager.Mode mode,
TelemetryRuntimeProducer telemetryRuntimeProducer) throws URISyntaxException {
public static HttpImpressionsSender create(SplitHttpClient client, URI eventsRootEndpoint,
ImpressionsManager.Mode mode,
TelemetryRuntimeProducer telemetryRuntimeProducer) throws URISyntaxException {
return new HttpImpressionsSender(client,
Utils.appendPath(eventsRootEndpoint, BULK_ENDPOINT_PATH),
Utils.appendPath(eventsRootEndpoint, COUNT_ENDPOINT_PATH),
mode,
telemetryRuntimeProducer);
}

private HttpImpressionsSender(SplitHttpClient client, URI impressionBulkTarget, URI impressionCountTarget, ImpressionsManager.Mode mode,
TelemetryRuntimeProducer telemetryRuntimeProducer) {
private HttpImpressionsSender(SplitHttpClient client, URI impressionBulkTarget, URI impressionCountTarget,
ImpressionsManager.Mode mode,
TelemetryRuntimeProducer telemetryRuntimeProducer) {
_client = client;
_mode = mode;
_impressionBulkTarget = impressionBulkTarget;
Expand All @@ -65,19 +68,21 @@ public void postImpressionsBulk(List<TestImpressions> impressions) {
long initTime = System.currentTimeMillis();
try {
HttpEntity entity = Utils.toJsonEntity(impressions);
Map<String, String> additionalHeader = new HashMap<>();
additionalHeader.put(IMPRESSIONS_MODE_HEADER, _mode.toString());
SplitHttpResponse response = _client.post(_impressionBulkTarget, entity, additionalHeader);
Map<String, List<String>> additionalHeaders = Collections.singletonMap(IMPRESSIONS_MODE_HEADER,
Collections.singletonList(_mode.toString()));
SplitHttpResponse response = _client.post(_impressionBulkTarget, entity, additionalHeaders);

if (response.statusCode() < HttpStatus.SC_OK || response.statusCode() >= HttpStatus.SC_MULTIPLE_CHOICES) {
_telemetryRuntimeProducer.recordSyncError(ResourceEnum.IMPRESSION_SYNC, response.statusCode());
}
_telemetryRuntimeProducer.recordSuccessfulSync(LastSynchronizationRecordsEnum.IMPRESSIONS, System.currentTimeMillis());
_telemetryRuntimeProducer.recordSuccessfulSync(LastSynchronizationRecordsEnum.IMPRESSIONS,
System.currentTimeMillis());

} catch (Throwable t) {
_logger.warn("Exception when posting impressions" + impressions, t);
} finally {
_telemetryRuntimeProducer.recordSyncLatency(HTTPLatenciesEnum.IMPRESSIONS, System.currentTimeMillis() - initTime);
_telemetryRuntimeProducer.recordSyncLatency(HTTPLatenciesEnum.IMPRESSIONS,
System.currentTimeMillis() - initTime);
}
}

Expand All @@ -91,14 +96,16 @@ public void postCounters(HashMap<ImpressionCounter.Key, Integer> raw) {

try {
SplitHttpResponse response = _client.post(_impressionCountTarget,
Utils.toJsonEntity(ImpressionCount.fromImpressionCounterData(raw)),
null);
Utils.toJsonEntity(ImpressionCount.fromImpressionCounterData(raw)),
null);

if (response.statusCode() < HttpStatus.SC_OK || response.statusCode() >= HttpStatus.SC_MULTIPLE_CHOICES) {
_telemetryRuntimeProducer.recordSyncError(ResourceEnum.IMPRESSION_COUNT_SYNC, response.statusCode());
}
_telemetryRuntimeProducer.recordSyncLatency(HTTPLatenciesEnum.IMPRESSIONS_COUNT, System.currentTimeMillis() - initTime);
_telemetryRuntimeProducer.recordSuccessfulSync(LastSynchronizationRecordsEnum.IMPRESSIONS_COUNT, System.currentTimeMillis());
_telemetryRuntimeProducer.recordSyncLatency(HTTPLatenciesEnum.IMPRESSIONS_COUNT,
System.currentTimeMillis() - initTime);
_telemetryRuntimeProducer.recordSuccessfulSync(LastSynchronizationRecordsEnum.IMPRESSIONS_COUNT,
System.currentTimeMillis());
} catch (IOException exc) {
_logger.warn("Exception when posting impression counters: ", exc);
}
Expand Down
20 changes: 11 additions & 9 deletions client/src/main/java/io/split/service/SplitHttpClient.java
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
package io.split.service;

import io.split.engine.common.FetchOptions;
import io.split.telemetry.domain.enums.HttpParamsWrapper;
import io.split.client.dtos.SplitHttpResponse;

import org.apache.hc.core5.http.HttpEntity;
import java.io.IOException;
import java.net.URI;
import java.util.List;
import java.util.Map;

public interface SplitHttpClient {
/**
* Wrapper for HTTP get method
* @param uri the URL to be used
*
* @param uri the URL to be used
* @param options The FetchOptions object that contains headers.
* @return The response structure SplitHttpResponse
*/
public SplitHttpResponse get(URI uri, FetchOptions options, Map<String, String> additionalHeaders);
public SplitHttpResponse get(URI uri, FetchOptions options, Map<String, List<String>> additionalHeaders);

/**
* Wrapper for HTTP post method
* @param uri the URL to be used
* @param entity HttpEntity object that has The body load
*
* @param uri the URL to be used
* @param entity HttpEntity object that has The body load
* @param additionalHeaders Any additional headers to be added.
* @return The response structure SplitHttpResponse
*/
public SplitHttpResponse post
(URI uri,
HttpEntity entity,
Map<String, String> additionalHeaders) throws IOException;
public SplitHttpResponse post(URI uri,
HttpEntity entity,
Map<String, List<String>> additionalHeaders) throws IOException;
}
84 changes: 59 additions & 25 deletions client/src/main/java/io/split/service/SplitHttpClientImpl.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.split.service;

import io.split.client.RequestDecorator;
import io.split.client.utils.SDKMetadata;
import io.split.client.utils.Utils;
import io.split.engine.common.FetchOptions;
import io.split.client.dtos.SplitHttpResponse;
Expand All @@ -16,77 +17,99 @@
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import org.apache.hc.core5.http.HttpRequest;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

public final class SplitHttpClientImpl implements SplitHttpClient {

private static final Logger _log = LoggerFactory.getLogger(SplitHttpClient.class);
private static final String HEADER_CACHE_CONTROL_NAME = "Cache-Control";
private static final String HEADER_CACHE_CONTROL_VALUE = "no-cache";
private static final String HEADER_API_KEY = "Authorization";
private static final String HEADER_CLIENT_KEY = "SplitSDKClientKey";
private static final String HEADER_CLIENT_MACHINE_NAME = "SplitSDKMachineName";
private static final String HEADER_CLIENT_MACHINE_IP = "SplitSDKMachineIP";
private static final String HEADER_CLIENT_VERSION = "SplitSDKVersion";

private final CloseableHttpClient _client;
private final RequestDecorator _requestDecorator;
private final String _apikey;
private final SDKMetadata _metadata;

public static SplitHttpClientImpl create(
CloseableHttpClient client,
RequestDecorator requestDecorator
) throws URISyntaxException {
return new SplitHttpClientImpl(client, requestDecorator);
public static SplitHttpClientImpl create(CloseableHttpClient client,
RequestDecorator requestDecorator,
String apikey,
SDKMetadata metadata) throws URISyntaxException {
return new SplitHttpClientImpl(client, requestDecorator, apikey, metadata);
}

private SplitHttpClientImpl
(CloseableHttpClient client,
RequestDecorator requestDecorator) {
private SplitHttpClientImpl(CloseableHttpClient client,
RequestDecorator requestDecorator,
String apikey,
SDKMetadata metadata) {
_client = client;
_requestDecorator = requestDecorator;
_apikey = apikey;
_metadata = metadata;
}

public SplitHttpResponse get(URI uri, FetchOptions options, Map<String, String> additionalHeaders) {
public SplitHttpResponse get(URI uri, FetchOptions options, Map<String, List<String>> additionalHeaders) {
CloseableHttpResponse response = null;

try {
HttpGet request = new HttpGet(uri);
setBasicHeaders(request);
if (additionalHeaders != null) {
for (Map.Entry entry : additionalHeaders.entrySet()) {
request.addHeader(entry.getKey().toString(), entry.getValue());
for (Map.Entry<String, List<String>> entry : additionalHeaders.entrySet()) {
for (String value : entry.getValue()) {
request.addHeader(entry.getKey(), value);
}
}
}
if(options.cacheControlHeadersEnabled()) {
if (options.cacheControlHeadersEnabled()) {
request.setHeader(HEADER_CACHE_CONTROL_NAME, HEADER_CACHE_CONTROL_VALUE);
}
request = (HttpGet) _requestDecorator.decorateHeaders(request);

_requestDecorator.decorateHeaders(request);

response = _client.execute(request);

if (_log.isDebugEnabled()) {
_log.debug(String.format("[%s] %s. Status code: %s", request.getMethod(), uri.toURL(), response.getCode()));
_log.debug(String.format("[%s] %s. Status code: %s", request.getMethod(), uri.toURL(),
response.getCode()));
}

String statusMessage = "";
if (response.getCode() < HttpStatus.SC_OK || response.getCode() >= HttpStatus.SC_MULTIPLE_CHOICES) {
_log.warn(String.format("Response status was: %s. Reason: %s", response.getCode() , response.getReasonPhrase()));
_log.warn(String.format("Response status was: %s. Reason: %s", response.getCode(),
response.getReasonPhrase()));
statusMessage = response.getReasonPhrase();
}
return new SplitHttpResponse(response.getCode(),
statusMessage,
EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8),
response.getHeaders());
statusMessage,
EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8),
response.getHeaders());
} catch (Exception e) {
throw new IllegalStateException(String.format("Problem in http get operation: %s", e), e);
} finally {
Utils.forceClose(response);
}
}

public SplitHttpResponse post
(URI uri,
HttpEntity entity,
Map<String, String> additionalHeaders) throws IOException {
public SplitHttpResponse post(URI uri, HttpEntity entity, Map<String, List<String>> additionalHeaders)
throws IOException {

CloseableHttpResponse response = null;
try {
HttpPost request = new HttpPost(uri);
setBasicHeaders(request);
if (additionalHeaders != null) {
for (Map.Entry entry : additionalHeaders.entrySet()) {
request.addHeader(entry.getKey().toString(), entry.getValue());
for (Map.Entry<String, List<String>> entry : additionalHeaders.entrySet()) {
for (String value : entry.getValue()) {
request.addHeader(entry.getKey(), value);
}
}
}
request.setEntity(entity);
Expand All @@ -97,7 +120,8 @@ public SplitHttpResponse get(URI uri, FetchOptions options, Map<String, String>
String statusMessage = "";
if (response.getCode() < HttpStatus.SC_OK || response.getCode() >= HttpStatus.SC_MULTIPLE_CHOICES) {
statusMessage = response.getReasonPhrase();
_log.warn(String.format("Response status was: %s. Reason: %s", response.getCode(), response.getReasonPhrase()));
_log.warn(String.format("Response status was: %s. Reason: %s", response.getCode(),
response.getReasonPhrase()));
}
return new SplitHttpResponse(response.getCode(), statusMessage, "", response.getHeaders());
} catch (Exception e) {
Expand All @@ -106,4 +130,14 @@ public SplitHttpResponse get(URI uri, FetchOptions options, Map<String, String>
Utils.forceClose(response);
}
}

private void setBasicHeaders(HttpRequest request) {
request.setHeader(HEADER_API_KEY, "Bearer " + _apikey);
request.setHeader(HEADER_CLIENT_VERSION, _metadata.getSdkVersion());
request.setHeader(HEADER_CLIENT_MACHINE_IP, _metadata.getMachineIp());
request.setHeader(HEADER_CLIENT_MACHINE_NAME, _metadata.getMachineName());
request.setHeader(HEADER_CLIENT_KEY, _apikey.length() > 4
? _apikey.substring(_apikey.length() - 4)
: _apikey);
}
}
Loading