From 9e46ad592808810b20fb0533822fe88c9e2dfe31 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Tue, 10 Sep 2024 09:10:05 -0700 Subject: [PATCH 1/4] Refactor kerberos code to sub module --- client/pom.xml | 12 - .../io/split/client/SplitClientConfig.java | 25 +- .../io/split/client/SplitFactoryBuilder.java | 1 + .../io/split/client/SplitFactoryImpl.java | 86 ++--- .../io/split/service/SplitHttpClient.java | 8 +- .../io/split/service/SplitHttpClientImpl.java | 11 + .../HTTPKerberosAuthIntercepterTest.java | 112 ------- .../service/HttpSplitClientKerberosTest.java | 303 ------------------ client/src/test/resources/krb5.conf | 37 --- kerberos/pom.xml | 90 ++++++ .../HTTPKerberosAuthInterceptor.java | 4 +- .../kerberos}/KerberosAuthException.java | 2 +- .../SplitHttpClientKerberosBuilder.java | 56 ++++ .../SplitHttpClientKerberosImpl.java | 50 +-- pom.xml | 1 + 15 files changed, 247 insertions(+), 551 deletions(-) delete mode 100644 client/src/test/java/io/split/service/HTTPKerberosAuthIntercepterTest.java delete mode 100644 client/src/test/java/io/split/service/HttpSplitClientKerberosTest.java delete mode 100644 client/src/test/resources/krb5.conf create mode 100644 kerberos/pom.xml rename {client/src/main/java/io/split/service => kerberos/src/main/java/io/split/kerberos}/HTTPKerberosAuthInterceptor.java (99%) rename {client/src/main/java/io/split/client/exceptions => kerberos/src/main/java/io/split/kerberos}/KerberosAuthException.java (87%) create mode 100644 kerberos/src/main/java/io/split/kerberos/SplitHttpClientKerberosBuilder.java rename {client/src/main/java/io/split/service => kerberos/src/main/java/io/split/kerberos}/SplitHttpClientKerberosImpl.java (87%) diff --git a/client/pom.xml b/client/pom.xml index 0fb3f8549..d9c1629a2 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -177,18 +177,6 @@ snakeyaml 2.0 - - com.squareup.okhttp3 - okhttp - 4.12.0 - true - - - com.squareup.okhttp3 - logging-interceptor - 4.12.0 - true - diff --git a/client/src/main/java/io/split/client/SplitClientConfig.java b/client/src/main/java/io/split/client/SplitClientConfig.java index 0a6b0fbd2..effd2bb95 100644 --- a/client/src/main/java/io/split/client/SplitClientConfig.java +++ b/client/src/main/java/io/split/client/SplitClientConfig.java @@ -5,6 +5,7 @@ import io.split.client.utils.FileTypeEnum; import io.split.integrations.IntegrationsConfig; import io.split.service.ProxyAuthScheme; +import io.split.service.SplitHttpClient; import io.split.storages.enums.OperationMode; import io.split.storages.enums.StorageMode; import org.apache.hc.core5.http.HttpHost; @@ -94,6 +95,7 @@ public class SplitClientConfig { private final CustomHeaderDecorator _customHeaderDecorator; private final ProxyAuthScheme _proxyAuthScheme; private final String _proxyKerberosPrincipalName; + private final SplitHttpClient _proxyKerberosClient; public static Builder builder() { return new Builder(); @@ -152,7 +154,8 @@ private SplitClientConfig(String endpoint, int invalidSets, CustomHeaderDecorator customHeaderDecorator, ProxyAuthScheme proxyAuthScheme, - String proxyKerberosPrincipalName) { + String proxyKerberosPrincipalName, + SplitHttpClient proxyKerberosClient) { _endpoint = endpoint; _eventsEndpoint = eventsEndpoint; _featuresRefreshRate = pollForFeatureChangesEveryNSeconds; @@ -207,6 +210,7 @@ private SplitClientConfig(String endpoint, _customHeaderDecorator = customHeaderDecorator; _proxyAuthScheme = proxyAuthScheme; _proxyKerberosPrincipalName = proxyKerberosPrincipalName; + _proxyKerberosClient = proxyKerberosClient; Properties props = new Properties(); try { @@ -419,6 +423,7 @@ public ProxyAuthScheme proxyAuthScheme() { } public String proxyKerberosPrincipalName() { return _proxyKerberosPrincipalName; } + public SplitHttpClient proxyKerberosClient() { return _proxyKerberosClient; } public static final class Builder { private String _endpoint = SDK_ENDPOINT; @@ -478,6 +483,7 @@ public static final class Builder { private CustomHeaderDecorator _customHeaderDecorator = null; private ProxyAuthScheme _proxyAuthScheme = null; private String _proxyKerberosPrincipalName = null; + private SplitHttpClient _proxyKerberosClient = null; public Builder() { } @@ -994,6 +1000,17 @@ public Builder proxyKerberosPrincipalName(String proxyKerberosPrincipalName) { return this; } + /** + * Kerberos Http Client + * + * @param proxyKerberosClient + * @return this builder + */ + public Builder proxyKerberosClient(SplitHttpClient proxyKerberosClient) { + _proxyKerberosClient = proxyKerberosClient; + return this; + } + /** * Thread Factory * @@ -1060,6 +1077,9 @@ private void verifyAuthScheme() { if (_proxyKerberosPrincipalName == null) { throw new IllegalStateException("Kerberos mode require Kerberos Principal Name."); } + if (_proxyKerberosClient == null) { + throw new IllegalStateException("Kerberos mode require Kerberos Http Client."); + } } } @@ -1184,7 +1204,8 @@ public SplitClientConfig build() { _invalidSetsCount, _customHeaderDecorator, _proxyAuthScheme, - _proxyKerberosPrincipalName); + _proxyKerberosPrincipalName, + _proxyKerberosClient); } } } \ No newline at end of file diff --git a/client/src/main/java/io/split/client/SplitFactoryBuilder.java b/client/src/main/java/io/split/client/SplitFactoryBuilder.java index c2271ec4f..2b48fb0d3 100644 --- a/client/src/main/java/io/split/client/SplitFactoryBuilder.java +++ b/client/src/main/java/io/split/client/SplitFactoryBuilder.java @@ -2,6 +2,7 @@ import io.split.inputValidation.ApiKeyValidator; import io.split.grammar.Treatments; +import io.split.service.SplitHttpClient; import io.split.storages.enums.StorageMode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/client/src/main/java/io/split/client/SplitFactoryImpl.java b/client/src/main/java/io/split/client/SplitFactoryImpl.java index 325683421..7595768d2 100644 --- a/client/src/main/java/io/split/client/SplitFactoryImpl.java +++ b/client/src/main/java/io/split/client/SplitFactoryImpl.java @@ -58,10 +58,9 @@ import io.split.engine.segments.SegmentSynchronizationTaskImp; import io.split.integrations.IntegrationsConfig; import io.split.service.ProxyAuthScheme; -import io.split.service.SplitHttpClientKerberosImpl; import io.split.service.SplitHttpClientImpl; import io.split.service.SplitHttpClient; -import io.split.service.HTTPKerberosAuthInterceptor; + import io.split.storages.SegmentCache; import io.split.storages.SegmentCacheConsumer; import io.split.storages.SegmentCacheProducer; @@ -86,6 +85,7 @@ import io.split.telemetry.synchronizer.TelemetryInMemorySubmitter; import io.split.telemetry.synchronizer.TelemetrySyncTask; import io.split.telemetry.synchronizer.TelemetrySynchronizer; + import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.Credentials; import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; @@ -108,26 +108,16 @@ import org.slf4j.LoggerFactory; import pluggable.CustomStorageWrapper; -import okhttp3.Authenticator; -import okhttp3.OkHttpClient; -import okhttp3.OkHttpClient.Builder; -import okhttp3.logging.HttpLoggingInterceptor; - import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; import java.net.URI; import java.net.URISyntaxException; -import java.net.InetSocketAddress; -import java.net.Proxy; -import java.util.Map; -import java.util.HashMap; import java.util.concurrent.ExecutorService; import java.util.stream.Collectors; import java.util.HashSet; import java.util.List; import java.util.ArrayList; -import java.util.concurrent.TimeUnit; import static io.split.client.utils.SplitExecutorFactory.buildExecutorService; @@ -167,15 +157,16 @@ public class SplitFactoryImpl implements SplitFactory { private final SplitSynchronizationTask _splitSynchronizationTask; private final EventsTask _eventsTask; private final SyncManager _syncManager; - private final SplitHttpClient _splitHttpClient; + private SplitHttpClient _splitHttpClient; private final UserStorageWrapper _userStorageWrapper; private final ImpressionsSender _impressionsSender; private final URI _rootTarget; private final URI _eventsRootTarget; private final UniqueKeysTracker _uniqueKeysTracker; + private RequestDecorator _requestDecorator; // Constructor for standalone mode - public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyntaxException, IOException { + public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyntaxException { _userStorageWrapper = null; _operationMode = config.operationMode(); _startTime = System.currentTimeMillis(); @@ -199,8 +190,14 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn _gates = new SDKReadinessGates(); // HttpClient - RequestDecorator requestDecorator = new RequestDecorator(config.customHeaderDecorator()); - _splitHttpClient = buildSplitHttpClient(apiToken, config, _sdkMetadata, requestDecorator); + _requestDecorator = new RequestDecorator(config.customHeaderDecorator()); + if (config.proxyAuthScheme() != ProxyAuthScheme.KERBEROS) { + _splitHttpClient = buildSplitHttpClient(apiToken, config, _sdkMetadata, _requestDecorator); + } else { + _splitHttpClient = config.proxyKerberosClient(); + _splitHttpClient.setMetaData(_sdkMetadata); + _splitHttpClient.setRequestDecorator(_requestDecorator); + } // Roots _rootTarget = URI.create(config.endpoint()); @@ -269,7 +266,7 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn // SyncManager SplitTasks splitTasks = SplitTasks.build(_splitSynchronizationTask, _segmentSynchronizationTaskImp, _impressionsManager, _eventsTask, _telemetrySyncTask, _uniqueKeysTracker); - SplitAPI splitAPI = SplitAPI.build(_splitHttpClient, buildSSEdHttpClient(apiToken, config, _sdkMetadata), requestDecorator); + SplitAPI splitAPI = SplitAPI.build(_splitHttpClient, buildSSEdHttpClient(apiToken, config, _sdkMetadata), _requestDecorator); _syncManager = SyncManagerImp.build(splitTasks, _splitFetcher, splitCache, splitAPI, segmentCache, _gates, _telemetryStorageProducer, _telemetrySynchronizer, config, splitParser, @@ -287,6 +284,14 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn } } + public RequestDecorator getRequestDecorator() { + return _requestDecorator; + } + + public SDKMetadata getSDKMetaData() { + return _sdkMetadata; + } + // Constructor for consumer mode protected SplitFactoryImpl(String apiToken, SplitClientConfig config, CustomStorageWrapper customStorageWrapper) throws URISyntaxException { @@ -503,36 +508,12 @@ public boolean isDestroyed() { return isTerminated; } + public void setSplitHttpClient(SplitHttpClient splitHttpClient) { + _splitHttpClient = splitHttpClient; + } protected static SplitHttpClient buildSplitHttpClient(String apiToken, SplitClientConfig config, SDKMetadata sdkMetadata, RequestDecorator requestDecorator) - throws URISyntaxException, IOException { - // setup Kerberos client - if (config.proxyAuthScheme() == ProxyAuthScheme.KERBEROS) { - _log.info("Using Kerberos-Proxy Authentication Scheme."); - Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(config.proxy().getHostName(), config.proxy().getPort())); - HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); - if (config.debugEnabled()) { - logging.setLevel(HttpLoggingInterceptor.Level.HEADERS); - } else { - logging.setLevel(HttpLoggingInterceptor.Level.NONE); - } - - Map kerberosOptions = new HashMap<>(); - kerberosOptions.put("com.sun.security.auth.module.Krb5LoginModule", "required"); - kerberosOptions.put("refreshKrb5Config", "false"); - kerberosOptions.put("doNotPrompt", "false"); - kerberosOptions.put("useTicketCache", "true"); - - Authenticator proxyAuthenticator = getProxyAuthenticator(config, kerberosOptions); - OkHttpClient client = buildOkHttpClient(proxy, config, logging, proxyAuthenticator); - - return SplitHttpClientKerberosImpl.create( - client, - requestDecorator, - apiToken, - sdkMetadata); - } - + throws URISyntaxException { SSLConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactoryBuilder.create() .setSslContext(SSLContexts.createSystemDefault()) .setTlsVersions(TLS.V_1_1, TLS.V_1_2) @@ -570,21 +551,6 @@ protected static SplitHttpClient buildSplitHttpClient(String apiToken, SplitClie sdkMetadata); } - protected static OkHttpClient buildOkHttpClient(Proxy proxy, SplitClientConfig config, - HttpLoggingInterceptor logging, Authenticator proxyAuthenticator) { - return new Builder() - .proxy(proxy) - .readTimeout(config.readTimeout(), TimeUnit.MILLISECONDS) - .connectTimeout(config.connectionTimeout(), TimeUnit.MILLISECONDS) - .addInterceptor(logging) - .proxyAuthenticator(proxyAuthenticator) - .build(); - } - - protected static HTTPKerberosAuthInterceptor getProxyAuthenticator(SplitClientConfig config, - Map kerberosOptions) throws IOException { - return new HTTPKerberosAuthInterceptor(config.proxyKerberosPrincipalName(), kerberosOptions); - } private static CloseableHttpClient buildSSEdHttpClient(String apiToken, SplitClientConfig config, SDKMetadata sdkMetadata) { RequestConfig requestConfig = RequestConfig.custom() diff --git a/client/src/main/java/io/split/service/SplitHttpClient.java b/client/src/main/java/io/split/service/SplitHttpClient.java index 1c88bcd4e..52026aaa0 100644 --- a/client/src/main/java/io/split/service/SplitHttpClient.java +++ b/client/src/main/java/io/split/service/SplitHttpClient.java @@ -1,5 +1,7 @@ package io.split.service; +import io.split.client.RequestDecorator; +import io.split.client.utils.SDKMetadata; import io.split.engine.common.FetchOptions; import io.split.client.dtos.SplitHttpResponse; @@ -32,4 +34,8 @@ public interface SplitHttpClient extends Closeable { public SplitHttpResponse post(URI uri, HttpEntity entity, Map> additionalHeaders) throws IOException; -} + + public void setMetaData(SDKMetadata metadata); + + public void setRequestDecorator(RequestDecorator requestDecorator); +} \ No newline at end of file diff --git a/client/src/main/java/io/split/service/SplitHttpClientImpl.java b/client/src/main/java/io/split/service/SplitHttpClientImpl.java index 64ca3a55c..af91400ea 100644 --- a/client/src/main/java/io/split/service/SplitHttpClientImpl.java +++ b/client/src/main/java/io/split/service/SplitHttpClientImpl.java @@ -145,4 +145,15 @@ private void setBasicHeaders(HttpRequest request) { public void close() throws IOException { _client.close(); } + + @Override + public void setMetaData(SDKMetadata metadata) { + // only implemented for Kerberos client + } + + @Override + public void setRequestDecorator(RequestDecorator requestDecorator) { + // only implemented for Kerberos client + } + } diff --git a/client/src/test/java/io/split/service/HTTPKerberosAuthIntercepterTest.java b/client/src/test/java/io/split/service/HTTPKerberosAuthIntercepterTest.java deleted file mode 100644 index b49eda759..000000000 --- a/client/src/test/java/io/split/service/HTTPKerberosAuthIntercepterTest.java +++ /dev/null @@ -1,112 +0,0 @@ -package io.split.service; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; - -import javax.security.auth.Subject; -import javax.security.auth.kerberos.KerberosPrincipal; -import javax.security.auth.login.AppConfigurationEntry; -import javax.security.auth.login.LoginContext; -import javax.security.auth.login.LoginException; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.Is.is; -import static org.hamcrest.core.IsEqual.equalTo; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.verify; -import static org.mockito.internal.verification.VerificationModeFactory.times; -import static org.powermock.api.mockito.PowerMockito.*; - -import java.security.PrivilegedActionException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -@RunWith(PowerMockRunner.class) -@PrepareForTest(HTTPKerberosAuthInterceptor.class) -public class HTTPKerberosAuthIntercepterTest { - - @Test - public void testBasicFlow() throws Exception { - System.setProperty("java.security.krb5.conf", "src/test/resources/krb5.conf"); - - HTTPKerberosAuthInterceptor kerberosAuthInterceptor = mock(HTTPKerberosAuthInterceptor.class); - LoginContext loginContext = PowerMockito.mock(LoginContext.class); - when(kerberosAuthInterceptor.getLoginContext(any())).thenReturn((loginContext)); - - doCallRealMethod().when(kerberosAuthInterceptor).buildSubjectCredentials(); - kerberosAuthInterceptor.buildSubjectCredentials(); - verify(loginContext, times(1)).login(); - - Subject subject = new Subject(); - when(loginContext.getSubject()).thenReturn(subject); - doCallRealMethod().when(kerberosAuthInterceptor).getContextSubject(); - kerberosAuthInterceptor.getContextSubject(); - verify(loginContext, times(1)).getSubject(); - - subject.getPrincipals().add(new KerberosPrincipal("bilal")); - subject.getPublicCredentials().add(new KerberosPrincipal("name")); - subject.getPrivateCredentials().add(new KerberosPrincipal("name")); - - doCallRealMethod().when(kerberosAuthInterceptor).getClientPrincipalName(); - assertThat(kerberosAuthInterceptor.getClientPrincipalName(), is(equalTo("bilal@ATHENA.MIT.EDU"))) ; - verify(loginContext, times(2)).getSubject(); - - when(kerberosAuthInterceptor.buildAuthorizationHeader(any())).thenReturn("secured-token"); - okhttp3.Request originalRequest = new okhttp3.Request.Builder().url("http://somthing").build(); - okhttp3.Response response = new okhttp3.Response.Builder().code(200).request(originalRequest). - protocol(okhttp3.Protocol.HTTP_1_1).message("ok").build(); - doCallRealMethod().when(kerberosAuthInterceptor).authenticate(null, response); - okhttp3.Request request = kerberosAuthInterceptor.authenticate(null, response); - assertThat(request.headers("Proxy-authorization"), is(equalTo(Arrays.asList("Negotiate secured-token")))); - } - - @Test - public void testKerberosLoginConfiguration() { - Map kerberosOptions = new HashMap(); - kerberosOptions.put("com.sun.security.auth.module.Krb5LoginModule", "required"); - kerberosOptions.put("refreshKrb5Config", "false"); - kerberosOptions.put("doNotPrompt", "false"); - kerberosOptions.put("useTicketCache", "true"); - - HTTPKerberosAuthInterceptor.KerberosLoginConfiguration kerberosConfig = new HTTPKerberosAuthInterceptor.KerberosLoginConfiguration(kerberosOptions); - AppConfigurationEntry[] appConfig = kerberosConfig.getAppConfigurationEntry(""); - assertThat("com.sun.security.auth.module.Krb5LoginModule", is(equalTo(appConfig[0].getLoginModuleName()))); - assertThat(AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, is(equalTo(appConfig[0].getControlFlag()))); - } - - @Test(expected = IllegalStateException.class) - public void testKerberosLoginConfigurationException() { - HTTPKerberosAuthInterceptor.KerberosLoginConfiguration kerberosConfig = new HTTPKerberosAuthInterceptor.KerberosLoginConfiguration(); - AppConfigurationEntry[] appConfig = kerberosConfig.getAppConfigurationEntry(""); - } - - @Test - public void testBuildAuthorizationHeader() throws LoginException, PrivilegedActionException { - System.setProperty("java.security.krb5.conf", "src/test/resources/krb5.conf"); - - HTTPKerberosAuthInterceptor kerberosAuthInterceptor = mock(HTTPKerberosAuthInterceptor.class); - HTTPKerberosAuthInterceptor.CreateAuthorizationHeaderAction ahh = mock(HTTPKerberosAuthInterceptor.CreateAuthorizationHeaderAction.class); - when(ahh.getNegotiateToken()).thenReturn("secret-token"); - when(kerberosAuthInterceptor.getAuthorizationHeaderAction(any(), any())).thenReturn(ahh); - - LoginContext loginContext = PowerMockito.mock(LoginContext.class); - doCallRealMethod().when(kerberosAuthInterceptor).buildAuthorizationHeader("bilal"); - Subject subject = new Subject(); - when(loginContext.getSubject()).thenReturn(subject); - when(kerberosAuthInterceptor.getContextSubject()).thenReturn(subject); - when(kerberosAuthInterceptor.getLoginContext(subject)).thenReturn((loginContext)); - doCallRealMethod().when(kerberosAuthInterceptor).buildSubjectCredentials(); - kerberosAuthInterceptor.buildSubjectCredentials(); - - subject.getPrincipals().add(new KerberosPrincipal("bilal")); - subject.getPublicCredentials().add(new KerberosPrincipal("name")); - subject.getPrivateCredentials().add(new KerberosPrincipal("name")); - doCallRealMethod().when(kerberosAuthInterceptor).getClientPrincipalName(); - - assertThat("secret-token", is(equalTo(kerberosAuthInterceptor.buildAuthorizationHeader("bilal")))); - } -} diff --git a/client/src/test/java/io/split/service/HttpSplitClientKerberosTest.java b/client/src/test/java/io/split/service/HttpSplitClientKerberosTest.java deleted file mode 100644 index 3ddf5c681..000000000 --- a/client/src/test/java/io/split/service/HttpSplitClientKerberosTest.java +++ /dev/null @@ -1,303 +0,0 @@ -package io.split.service; - -import com.google.common.base.Charsets; -import com.google.common.io.Files; - -import io.split.client.CustomHeaderDecorator; -import io.split.client.RequestDecorator; -import io.split.client.dtos.*; -import io.split.client.impressions.Impression; -import io.split.client.utils.Json; -import io.split.client.utils.SDKMetadata; -import io.split.client.utils.Utils; -import io.split.engine.common.FetchOptions; - -import okhttp3.OkHttpClient; -import okhttp3.OkHttpClient.*; -import okhttp3.HttpUrl; -import okhttp3.Headers; - -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import okhttp3.mockwebserver.RecordedRequest; -import org.apache.hc.core5.http.*; -import org.apache.hc.core5.http.io.entity.EntityUtils; -import org.junit.Assert; -import org.junit.Test; - -import java.io.*; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.HttpURLConnection; -import java.net.Proxy; -import java.net.InetSocketAddress; -import java.util.List; -import java.util.Arrays; -import java.util.Collections; -import java.util.Map; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.Is.is; -import static org.hamcrest.core.IsEqual.equalTo; - -public class HttpSplitClientKerberosTest { - - @Test - public void testGetWithSpecialCharacters() throws IOException, InterruptedException { - MockWebServer server = new MockWebServer(); - BufferedReader br = new BufferedReader(new FileReader("src/test/resources/split-change-special-characters.json")); - String body; - try { - StringBuilder sb = new StringBuilder(); - String line = br.readLine(); - - while (line != null) { - sb.append(line); - sb.append(System.lineSeparator()); - line = br.readLine(); - } - body = sb.toString(); - } finally { - br.close(); - } - - server.enqueue(new MockResponse().setBody(body).addHeader(HttpHeaders.VIA, "HTTP/1.1 s_proxy_rio1")); - server.start(); - HttpUrl baseUrl = server.url("/v1/"); - URI rootTarget = baseUrl.uri(); - RequestDecorator decorator = new RequestDecorator(null); - OkHttpClient client = new Builder().build(); - - SplitHttpClientKerberosImpl splitHttpClientKerberosImpl = new SplitHttpClientKerberosImpl(client, decorator, "qwerty", metadata()); - - Map> additionalHeaders = Collections.singletonMap("AdditionalHeader", - Collections.singletonList("add")); - - SplitHttpResponse splitHttpResponse = splitHttpClientKerberosImpl.get(rootTarget, - new FetchOptions.Builder().cacheControlHeaders(true).build(), additionalHeaders); - - - RecordedRequest request = server.takeRequest(); - server.shutdown(); - Headers requestHeaders = request.getHeaders(); - - assertThat(splitHttpResponse.statusCode(), is(equalTo(HttpURLConnection.HTTP_OK))); - Assert.assertEquals("/v1/", request.getPath()); - assertThat(requestHeaders.get("Authorization"), is(equalTo("Bearer qwerty"))) ; - assertThat(requestHeaders.get("SplitSDKClientKey"), is(equalTo("erty"))); - assertThat(requestHeaders.get("SplitSDKVersion"), is(equalTo("java-1.2.3"))); - assertThat(requestHeaders.get("SplitSDKMachineIP"), is(equalTo("1.2.3.4"))); - assertThat(requestHeaders.get("SplitSDKMachineName"), is(equalTo("someIP"))); - assertThat(requestHeaders.get("AdditionalHeader"), is(equalTo("add"))); - - SplitChange change = Json.fromJson(splitHttpResponse.body(), SplitChange.class); - Header[] headers = splitHttpResponse.responseHeaders(); - assertThat(headers[1].getName(), is(equalTo("via"))); - assertThat(headers[1].getValue(), is(equalTo("[HTTP/1.1 s_proxy_rio1]"))); - assertThat(splitHttpResponse.statusCode(), is(equalTo(200))); - Assert.assertNotNull(change); - Assert.assertEquals(1, change.splits.size()); - Assert.assertNotNull(change.splits.get(0)); - - Split split = change.splits.get(0); - Map configs = split.configurations; - Assert.assertEquals(2, configs.size()); - Assert.assertEquals("{\"test\": \"blue\",\"grüne Straße\": 13}", configs.get("on")); - Assert.assertEquals("{\"test\": \"blue\",\"size\": 15}", configs.get("off")); - Assert.assertEquals(2, split.sets.size()); - splitHttpClientKerberosImpl.close(); - } - - @Test - public void testGetErrors() throws IOException, InterruptedException { - MockWebServer server = new MockWebServer(); - server.enqueue(new MockResponse().setBody("").setResponseCode(HttpURLConnection.HTTP_INTERNAL_ERROR)); - server.start(); - HttpUrl baseUrl = server.url("/v1/"); - URI rootTarget = baseUrl.uri(); - RequestDecorator decorator = new RequestDecorator(null); - OkHttpClient client = new Builder().build(); - - SplitHttpClientKerberosImpl splitHttpClientKerberosImpl = new SplitHttpClientKerberosImpl(client, decorator, "qwerty", metadata()); - - Map> additionalHeaders = Collections.singletonMap("AdditionalHeader", - Collections.singletonList("add")); - - SplitHttpResponse splitHttpResponse = splitHttpClientKerberosImpl.get(rootTarget, - new FetchOptions.Builder().cacheControlHeaders(true).build(), additionalHeaders); - - - RecordedRequest request = server.takeRequest(); - server.shutdown(); - assertThat(splitHttpResponse.statusCode(), is(equalTo(HttpURLConnection.HTTP_INTERNAL_ERROR))); - splitHttpClientKerberosImpl.close(); - } - - - @Test - public void testGetParameters() throws IOException, InterruptedException { - class MyCustomHeaders implements CustomHeaderDecorator { - public MyCustomHeaders() {} - @Override - public Map> getHeaderOverrides(RequestContext context) { - Map> additionalHeaders = context.headers(); - additionalHeaders.put("first", Arrays.asList("1")); - additionalHeaders.put("second", Arrays.asList("2.1", "2.2")); - additionalHeaders.put("third", Arrays.asList("3")); - return additionalHeaders; - } - } - - MockWebServer server = new MockWebServer(); - BufferedReader br = new BufferedReader(new FileReader("src/test/resources/split-change-special-characters.json")); - String body; - try { - StringBuilder sb = new StringBuilder(); - String line = br.readLine(); - - while (line != null) { - sb.append(line); - sb.append(System.lineSeparator()); - line = br.readLine(); - } - body = sb.toString(); - } finally { - br.close(); - } - - server.enqueue(new MockResponse().setBody(body).addHeader(HttpHeaders.VIA, "HTTP/1.1 s_proxy_rio1")); - server.start(); - HttpUrl baseUrl = server.url("/splitChanges?since=1234567"); - URI rootTarget = baseUrl.uri(); - RequestDecorator decorator = new RequestDecorator(new MyCustomHeaders()); - OkHttpClient client = new Builder().build(); - - SplitHttpClientKerberosImpl splitHttpClientKerberosImpl = new SplitHttpClientKerberosImpl(client, decorator, "qwerty", metadata()); - - FetchOptions options = new FetchOptions.Builder().cacheControlHeaders(true).build(); - SplitHttpResponse splitHttpResponse = splitHttpClientKerberosImpl.get(rootTarget, options, null); - - RecordedRequest request = server.takeRequest(); - server.shutdown(); - Headers requestHeaders = request.getHeaders(); - - assertThat(requestHeaders.get("Cache-Control"), is(equalTo("no-cache"))); - assertThat(requestHeaders.get("first"), is(equalTo("1"))); - assertThat(requestHeaders.values("second"), is(equalTo(Arrays.asList("2.1","2.2")))); - assertThat(requestHeaders.get("third"), is(equalTo("3"))); - Assert.assertEquals("/splitChanges?since=1234567", request.getPath()); - assertThat(request.getMethod(), is(equalTo("GET"))); - } - - @Test(expected = IllegalStateException.class) - public void testException() throws URISyntaxException, IOException { - URI uri = new URI("https://api.split.io/splitChanges?since=1234567"); - RequestDecorator decorator = null; - - ByteArrayInputStream stubInputStream = new ByteArrayInputStream(Files.asCharSource( - new File("src/test/resources/split-change-special-characters.json"), Charsets.UTF_8).read().getBytes(Charsets.UTF_8)); - - Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("1.0.0.127", 8080)); - OkHttpClient client = new OkHttpClient.Builder() - .proxy(proxy) - .build(); - - SplitHttpClientKerberosImpl splitHtpClientKerberos = SplitHttpClientKerberosImpl.create(client, decorator, "qwerty", metadata()); - SplitHttpResponse splitHttpResponse = splitHtpClientKerberos.get(uri, - new FetchOptions.Builder().cacheControlHeaders(true).build(), null); - } - - @Test - public void testPost() throws IOException, ParseException, InterruptedException { - MockWebServer server = new MockWebServer(); - - server.enqueue(new MockResponse().addHeader(HttpHeaders.VIA, "HTTP/1.1 s_proxy_rio1")); - server.start(); - HttpUrl baseUrl = server.url("/impressions"); - URI rootTarget = baseUrl.uri(); - RequestDecorator decorator = new RequestDecorator(null); - OkHttpClient client = new Builder().build(); - - SplitHttpClientKerberosImpl splitHttpClientKerberosImpl = new SplitHttpClientKerberosImpl(client, decorator, "qwerty", metadata()); - - FetchOptions options = new FetchOptions.Builder().cacheControlHeaders(true).build(); - // Send impressions - List toSend = Arrays.asList(new TestImpressions("t1", Arrays.asList( - KeyImpression.fromImpression(new Impression("k1", null, "t1", "on", 123L, "r1", 456L, null)), - KeyImpression.fromImpression(new Impression("k2", null, "t1", "on", 123L, "r1", 456L, null)), - KeyImpression.fromImpression(new Impression("k3", null, "t1", "on", 123L, "r1", 456L, null)))), - new TestImpressions("t2", Arrays.asList( - KeyImpression.fromImpression(new Impression("k1", null, "t2", "on", 123L, "r1", 456L, null)), - KeyImpression.fromImpression(new Impression("k2", null, "t2", "on", 123L, "r1", 456L, null)), - KeyImpression.fromImpression(new Impression("k3", null, "t2", "on", 123L, "r1", 456L, null))))); - - Map> additionalHeaders = Collections.singletonMap("SplitSDKImpressionsMode", - Collections.singletonList("OPTIMIZED")); - - SplitHttpResponse splitHttpResponse = splitHttpClientKerberosImpl.post(rootTarget, Utils.toJsonEntity(toSend), - additionalHeaders); - - RecordedRequest request = server.takeRequest(); - server.shutdown(); - Headers requestHeaders = request.getHeaders(); - String postBody = EntityUtils.toString(Utils.toJsonEntity(toSend)); - - Assert.assertEquals("POST /impressions HTTP/1.1", request.getRequestLine()); - Assert.assertEquals(postBody, request.getBody().readUtf8()); - assertThat(requestHeaders.get("Authorization"), is(equalTo("Bearer qwerty"))) ; - assertThat(requestHeaders.get("SplitSDKClientKey"), is(equalTo("erty"))); - assertThat(requestHeaders.get("SplitSDKVersion"), is(equalTo("java-1.2.3"))); - assertThat(requestHeaders.get("SplitSDKMachineIP"), is(equalTo("1.2.3.4"))); - assertThat(requestHeaders.get("SplitSDKMachineName"), is(equalTo("someIP"))); - assertThat(requestHeaders.get("SplitSDKImpressionsMode"), is(equalTo("OPTIMIZED"))); - - Header[] headers = splitHttpResponse.responseHeaders(); - assertThat(headers[1].getName(), is(equalTo("via"))); - assertThat(headers[1].getValue(), is(equalTo("[HTTP/1.1 s_proxy_rio1]"))); - assertThat(splitHttpResponse.statusCode(), is(equalTo(200))); - } - - @Test - public void testPostErrors() throws IOException, InterruptedException { - MockWebServer server = new MockWebServer(); - server.enqueue(new MockResponse().setBody("").setResponseCode(HttpURLConnection.HTTP_INTERNAL_ERROR)); - server.start(); - HttpUrl baseUrl = server.url("/v1/"); - URI rootTarget = baseUrl.uri(); - RequestDecorator decorator = new RequestDecorator(null); - OkHttpClient client = new Builder().build(); - - SplitHttpClientKerberosImpl splitHttpClientKerberosImpl = new SplitHttpClientKerberosImpl(client, decorator, "qwerty", metadata()); - - Map> additionalHeaders = Collections.singletonMap("AdditionalHeader", - Collections.singletonList("add")); - - SplitHttpResponse splitHttpResponse = splitHttpClientKerberosImpl.post(rootTarget, - Utils.toJsonEntity("<>"), additionalHeaders); - - - RecordedRequest request = server.takeRequest(); - server.shutdown(); - assertThat(splitHttpResponse.statusCode(), is(equalTo(HttpURLConnection.HTTP_INTERNAL_ERROR))); - splitHttpClientKerberosImpl.close(); - } - - @Test(expected = IllegalStateException.class) - public void testPosttException() throws URISyntaxException { - RequestDecorator decorator = null; - URI uri = new URI("https://kubernetesturl.com/split/api/testImpressions/bulk"); - - Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("1.0.0.127", 8080)); - OkHttpClient client = new OkHttpClient.Builder() - .proxy(proxy) - .build(); - SplitHttpClientKerberosImpl splitHtpClientKerberos = SplitHttpClientKerberosImpl.create(client, decorator, "qwerty", metadata()); - SplitHttpResponse splitHttpResponse = splitHtpClientKerberos.post(uri, - Utils.toJsonEntity(Arrays.asList(new String[] { "A", "B", "C", "D" })), null); - } - - private SDKMetadata metadata() { - return new SDKMetadata("java-1.2.3", "1.2.3.4", "someIP"); - } - -} diff --git a/client/src/test/resources/krb5.conf b/client/src/test/resources/krb5.conf deleted file mode 100644 index 78d63ba8f..000000000 --- a/client/src/test/resources/krb5.conf +++ /dev/null @@ -1,37 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -[libdefaults] - kdc_realm = ATHENA.MIT.EDU - default_realm = ATHENA.MIT.EDU - kdc_tcp_port = 88 - kdc_udp_port = 88 - dns_lookup_realm = false - dns_lookup_kdc = false - udp_preference_limit = 1 - -[logging] - default = FILE:/var/logs/krb5kdc.log - -[realms] - ATHENA.MIT.EDU = { -# kdc = 10.12.4.76:88 -# kdc = tcp/10.12.4.76:88 -# kdc = tcp/192.168.1.19:88 - kdc = 192.168.1.19:88 - } \ No newline at end of file diff --git a/kerberos/pom.xml b/kerberos/pom.xml new file mode 100644 index 000000000..461ac046e --- /dev/null +++ b/kerberos/pom.xml @@ -0,0 +1,90 @@ + + + + java-client-parent + io.split.client + 4.13.0 + + 4.0.0 + + kerberos + jar + Kerberos + Kerberos Authentication + + + + release + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.3 + true + + false + + + + + + + + + com.squareup.okhttp3 + okhttp + 4.12.0 + + + com.squareup.okhttp3 + logging-interceptor + 4.12.0 + + + org.apache.httpcomponents.client5 + httpclient5 + 5.0.3 + + + io.split.client + java-client + 4.13.0 + compile + + + + + junit + junit + test + + + org.mockito + mockito-core + 1.10.19 + test + + + org.powermock + powermock-module-junit4 + 1.7.4 + test + + + org.powermock + powermock-api-mockito + 1.7.4 + test + + + com.squareup.okhttp3 + mockwebserver + 4.8.0 + test + + + + \ No newline at end of file diff --git a/client/src/main/java/io/split/service/HTTPKerberosAuthInterceptor.java b/kerberos/src/main/java/io/split/kerberos/HTTPKerberosAuthInterceptor.java similarity index 99% rename from client/src/main/java/io/split/service/HTTPKerberosAuthInterceptor.java rename to kerberos/src/main/java/io/split/kerberos/HTTPKerberosAuthInterceptor.java index 038425c18..b72a8fefb 100644 --- a/client/src/main/java/io/split/service/HTTPKerberosAuthInterceptor.java +++ b/kerberos/src/main/java/io/split/kerberos/HTTPKerberosAuthInterceptor.java @@ -1,6 +1,4 @@ -package io.split.service; - -import io.split.client.exceptions.KerberosAuthException; +package io.split.kerberos; import java.io.IOException; import java.util.Map; diff --git a/client/src/main/java/io/split/client/exceptions/KerberosAuthException.java b/kerberos/src/main/java/io/split/kerberos/KerberosAuthException.java similarity index 87% rename from client/src/main/java/io/split/client/exceptions/KerberosAuthException.java rename to kerberos/src/main/java/io/split/kerberos/KerberosAuthException.java index 462944d8b..563bcf423 100644 --- a/client/src/main/java/io/split/client/exceptions/KerberosAuthException.java +++ b/kerberos/src/main/java/io/split/kerberos/KerberosAuthException.java @@ -1,4 +1,4 @@ -package io.split.client.exceptions; +package io.split.kerberos; public class KerberosAuthException extends Exception { public KerberosAuthException(String message) { diff --git a/kerberos/src/main/java/io/split/kerberos/SplitHttpClientKerberosBuilder.java b/kerberos/src/main/java/io/split/kerberos/SplitHttpClientKerberosBuilder.java new file mode 100644 index 000000000..11283e3dd --- /dev/null +++ b/kerberos/src/main/java/io/split/kerberos/SplitHttpClientKerberosBuilder.java @@ -0,0 +1,56 @@ +package io.split.kerberos; + +import java.io.IOException; +import java.net.Proxy; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import okhttp3.Authenticator; +import okhttp3.OkHttpClient; +import okhttp3.OkHttpClient.Builder; +import okhttp3.logging.HttpLoggingInterceptor; + +public class SplitHttpClientKerberosBuilder { + private static final int DEFAULT_CONNECTION_TIMEOUT = 10000; + private static final int DEFAULT_READ_TIMEOUT = 10000; + + public static OkHttpClient buildOkHttpClient(Proxy proxy, String proxyKerberosPrincipalName, + boolean debugEnabled, int readTimeout, int connectionTimeout) throws IOException { + + HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); + if (debugEnabled) { + logging.setLevel(HttpLoggingInterceptor.Level.HEADERS); + } else { + logging.setLevel(HttpLoggingInterceptor.Level.NONE); + } + + if (connectionTimeout <= 0 || connectionTimeout > DEFAULT_CONNECTION_TIMEOUT) { + connectionTimeout = DEFAULT_CONNECTION_TIMEOUT; + } + if (readTimeout <= 0 || readTimeout > DEFAULT_READ_TIMEOUT) { + readTimeout = DEFAULT_READ_TIMEOUT; + } + + Map kerberosOptions = new HashMap<>(); + kerberosOptions.put("com.sun.security.auth.module.Krb5LoginModule", "required"); + kerberosOptions.put("refreshKrb5Config", "false"); + kerberosOptions.put("doNotPrompt", "false"); + kerberosOptions.put("useTicketCache", "true"); + + Authenticator proxyAuthenticator = getProxyAuthenticator(proxyKerberosPrincipalName, kerberosOptions); + + return new Builder() + .proxy(proxy) + .readTimeout(readTimeout, TimeUnit.MILLISECONDS) + .connectTimeout(connectionTimeout, TimeUnit.MILLISECONDS) + .addInterceptor(logging) + .proxyAuthenticator(proxyAuthenticator) + .build(); + } + + public static HTTPKerberosAuthInterceptor getProxyAuthenticator(String proxyKerberosPrincipalName, + Map kerberosOptions) throws IOException { + return new HTTPKerberosAuthInterceptor(proxyKerberosPrincipalName, kerberosOptions); + } +} diff --git a/client/src/main/java/io/split/service/SplitHttpClientKerberosImpl.java b/kerberos/src/main/java/io/split/kerberos/SplitHttpClientKerberosImpl.java similarity index 87% rename from client/src/main/java/io/split/service/SplitHttpClientKerberosImpl.java rename to kerberos/src/main/java/io/split/kerberos/SplitHttpClientKerberosImpl.java index ef5106e10..c29a84cbd 100644 --- a/client/src/main/java/io/split/service/SplitHttpClientKerberosImpl.java +++ b/kerberos/src/main/java/io/split/kerberos/SplitHttpClientKerberosImpl.java @@ -1,16 +1,18 @@ -package io.split.service; +package io.split.kerberos; import io.split.client.RequestDecorator; import io.split.client.dtos.SplitHttpResponse; import io.split.client.utils.SDKMetadata; import io.split.engine.common.FetchOptions; +import io.split.service.SplitHttpClient; + +import split.org.apache.hc.client5.http.classic.methods.HttpGet; +import split.org.apache.hc.core5.http.Header; +import split.org.apache.hc.core5.http.HttpEntity; +import split.org.apache.hc.core5.http.HttpRequest; +import split.org.apache.hc.core5.http.io.entity.EntityUtils; +import split.org.apache.hc.core5.http.message.BasicHeader; -import org.apache.hc.client5.http.classic.methods.HttpGet; -import org.apache.hc.core5.http.Header; -import org.apache.hc.core5.http.HttpEntity; -import org.apache.hc.core5.http.HttpRequest; -import org.apache.hc.core5.http.io.entity.EntityUtils; -import org.apache.hc.core5.http.message.BasicHeader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,26 +40,33 @@ public class SplitHttpClientKerberosImpl implements SplitHttpClient { private static final String HEADER_CLIENT_MACHINE_IP = "SplitSDKMachineIP"; private static final String HEADER_CLIENT_VERSION = "SplitSDKVersion"; - private final RequestDecorator _requestDecorator; + private RequestDecorator _requestDecorator; private final String _apikey; - private final SDKMetadata _metadata; + private SDKMetadata _metadata; private final OkHttpClient _client; - public static SplitHttpClientKerberosImpl create(OkHttpClient client, RequestDecorator requestDecorator, - String apikey, - SDKMetadata metadata) { - return new SplitHttpClientKerberosImpl(client, requestDecorator, apikey, metadata); + public static SplitHttpClientKerberosImpl create(OkHttpClient client, + String apikey) { + return new SplitHttpClientKerberosImpl(client, apikey); } - SplitHttpClientKerberosImpl(OkHttpClient client, RequestDecorator requestDecorator, - String apikey, - SDKMetadata metadata) { - _requestDecorator = requestDecorator; + SplitHttpClientKerberosImpl(OkHttpClient client, + String apikey) { _apikey = apikey; - _metadata = metadata; _client = client; } + @Override + public void setMetaData(SDKMetadata metadata) { + _metadata = metadata; + } + + @Override + public void setRequestDecorator(RequestDecorator requestDecorator) { + _requestDecorator = requestDecorator; + } + + @Override public SplitHttpResponse get(URI uri, FetchOptions options, Map> additionalHeaders) { try { Builder requestBuilder = new Builder(); @@ -98,6 +107,7 @@ public SplitHttpResponse get(URI uri, FetchOptions options, Map> additionalHeaders) { try { @@ -107,7 +117,7 @@ public SplitHttpResponse post(URI url, HttpEntity entity, setAdditionalAndDecoratedHeaders(requestBuilder, additionalHeaders); requestBuilder.addHeader("Accept-Encoding", "gzip"); requestBuilder.addHeader("Content-Type", "application/json"); - String post = EntityUtils.toString(entity); + String post = EntityUtils.toString((HttpEntity) entity); RequestBody postBody = RequestBody.create(post.getBytes()); requestBuilder.post(postBody); @@ -177,7 +187,7 @@ protected Header[] getResponseHeaders(Response response) { responseHeaders.add(responseHeader); } } - return responseHeaders.toArray(new Header[0]); + return responseHeaders.toArray(new split.org.apache.hc.core5.http.Header[0]); } @Override public void close() throws IOException { diff --git a/pom.xml b/pom.xml index e99da05fd..3f899b8ee 100644 --- a/pom.xml +++ b/pom.xml @@ -85,6 +85,7 @@ redis-wrapper testing client + kerberos From 1e9482d53f53f98dd0bb91df7c4677b1df776970 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Tue, 10 Sep 2024 14:17:18 -0700 Subject: [PATCH 2/4] refactor to http-modules --- .../io/split/client/SplitClientConfig.java | 73 +--- .../io/split/client/SplitFactoryImpl.java | 6 +- .../io/split/service/SplitHttpClient.java | 2 + .../io/split/service/SplitHttpClientImpl.java | 4 + .../client/LocalhostSplitFactoryYamlTest.java | 1 + .../split/client/SplitClientConfigTest.java | 33 -- .../io/split/client/SplitFactoryImplTest.java | 116 +----- {kerberos => http-modules}/pom.xml | 6 +- .../okhttp}/HTTPKerberosAuthInterceptor.java | 2 +- .../okhttp}/KerberosAuthException.java | 2 +- .../httpmodules/okhttp/OkHttpModule.java | 372 ++++++++++++++++++ .../httpmodules/okhttp}/ProxyAuthScheme.java | 2 +- .../HTTPKerberosAuthIntercepterTest.java | 115 ++++++ .../okhttp/HttpSplitClientKerberosTest.java | 316 +++++++++++++++ .../httpmodules/okhttp/SplitConfigTests.java | 48 +++ .../httpmodules/okhttp/SplitFactoryTests.java | 107 +++++ http-modules/src/test/resources/krb5.conf | 37 ++ .../extensions/configuration.properties | 1 + .../SplitHttpClientKerberosBuilder.java | 56 --- .../kerberos/SplitHttpClientKerberosImpl.java | 196 --------- pom.xml | 2 +- 21 files changed, 1024 insertions(+), 473 deletions(-) rename {kerberos => http-modules}/pom.xml (95%) rename {kerberos/src/main/java/io/split/kerberos => http-modules/src/main/java/io/split/httpmodules/okhttp}/HTTPKerberosAuthInterceptor.java (99%) rename {kerberos/src/main/java/io/split/kerberos => http-modules/src/main/java/io/split/httpmodules/okhttp}/KerberosAuthException.java (87%) create mode 100644 http-modules/src/main/java/io/split/httpmodules/okhttp/OkHttpModule.java rename {client/src/main/java/io/split/service => http-modules/src/main/java/io/split/httpmodules/okhttp}/ProxyAuthScheme.java (55%) create mode 100644 http-modules/src/test/java/io/split/httpmodules/okhttp/HTTPKerberosAuthIntercepterTest.java create mode 100644 http-modules/src/test/java/io/split/httpmodules/okhttp/HttpSplitClientKerberosTest.java create mode 100644 http-modules/src/test/java/io/split/httpmodules/okhttp/SplitConfigTests.java create mode 100644 http-modules/src/test/java/io/split/httpmodules/okhttp/SplitFactoryTests.java create mode 100644 http-modules/src/test/resources/krb5.conf create mode 100644 http-modules/src/test/resources/org/powermock/extensions/configuration.properties delete mode 100644 kerberos/src/main/java/io/split/kerberos/SplitHttpClientKerberosBuilder.java delete mode 100644 kerberos/src/main/java/io/split/kerberos/SplitHttpClientKerberosImpl.java diff --git a/client/src/main/java/io/split/client/SplitClientConfig.java b/client/src/main/java/io/split/client/SplitClientConfig.java index effd2bb95..faf243de6 100644 --- a/client/src/main/java/io/split/client/SplitClientConfig.java +++ b/client/src/main/java/io/split/client/SplitClientConfig.java @@ -4,7 +4,6 @@ import io.split.client.impressions.ImpressionsManager; import io.split.client.utils.FileTypeEnum; import io.split.integrations.IntegrationsConfig; -import io.split.service.ProxyAuthScheme; import io.split.service.SplitHttpClient; import io.split.storages.enums.OperationMode; import io.split.storages.enums.StorageMode; @@ -93,9 +92,7 @@ public class SplitClientConfig { private final HashSet _flagSetsFilter; private final int _invalidSets; private final CustomHeaderDecorator _customHeaderDecorator; - private final ProxyAuthScheme _proxyAuthScheme; - private final String _proxyKerberosPrincipalName; - private final SplitHttpClient _proxyKerberosClient; + private final SplitHttpClient _alternativeHTTPModule; public static Builder builder() { return new Builder(); @@ -153,9 +150,7 @@ private SplitClientConfig(String endpoint, HashSet flagSetsFilter, int invalidSets, CustomHeaderDecorator customHeaderDecorator, - ProxyAuthScheme proxyAuthScheme, - String proxyKerberosPrincipalName, - SplitHttpClient proxyKerberosClient) { + SplitHttpClient alternativeHTTPModule) { _endpoint = endpoint; _eventsEndpoint = eventsEndpoint; _featuresRefreshRate = pollForFeatureChangesEveryNSeconds; @@ -208,9 +203,7 @@ private SplitClientConfig(String endpoint, _flagSetsFilter = flagSetsFilter; _invalidSets = invalidSets; _customHeaderDecorator = customHeaderDecorator; - _proxyAuthScheme = proxyAuthScheme; - _proxyKerberosPrincipalName = proxyKerberosPrincipalName; - _proxyKerberosClient = proxyKerberosClient; + _alternativeHTTPModule = alternativeHTTPModule; Properties props = new Properties(); try { @@ -418,12 +411,8 @@ public int getInvalidSets() { public CustomHeaderDecorator customHeaderDecorator() { return _customHeaderDecorator; } - public ProxyAuthScheme proxyAuthScheme() { - return _proxyAuthScheme; - } - public String proxyKerberosPrincipalName() { return _proxyKerberosPrincipalName; } - public SplitHttpClient proxyKerberosClient() { return _proxyKerberosClient; } + public SplitHttpClient alternativeHTTPModule() { return _alternativeHTTPModule; } public static final class Builder { private String _endpoint = SDK_ENDPOINT; @@ -481,9 +470,7 @@ public static final class Builder { private HashSet _flagSetsFilter = new HashSet<>(); private int _invalidSetsCount = 0; private CustomHeaderDecorator _customHeaderDecorator = null; - private ProxyAuthScheme _proxyAuthScheme = null; - private String _proxyKerberosPrincipalName = null; - private SplitHttpClient _proxyKerberosClient = null; + private SplitHttpClient _alternativeHTTPModule = null; public Builder() { } @@ -979,35 +966,13 @@ public Builder customHeaderDecorator(CustomHeaderDecorator customHeaderDecorator } /** - * Authentication Scheme - * - * @param proxyAuthScheme - * @return this builder - */ - public Builder proxyAuthScheme(ProxyAuthScheme proxyAuthScheme) { - _proxyAuthScheme = proxyAuthScheme; - return this; - } - - /** - * Kerberos Principal Account Name - * - * @param proxyKerberosPrincipalName - * @return this builder - */ - public Builder proxyKerberosPrincipalName(String proxyKerberosPrincipalName) { - _proxyKerberosPrincipalName = proxyKerberosPrincipalName; - return this; - } - - /** - * Kerberos Http Client + * Alternative Http Client * - * @param proxyKerberosClient + * @param alternativeHTTPModule * @return this builder */ - public Builder proxyKerberosClient(SplitHttpClient proxyKerberosClient) { - _proxyKerberosClient = proxyKerberosClient; + public Builder alternativeHTTPModule(SplitHttpClient alternativeHTTPModule) { + _alternativeHTTPModule = alternativeHTTPModule; return this; } @@ -1069,20 +1034,6 @@ private void verifyEndPoints() { } } - private void verifyAuthScheme() { - if (_proxyAuthScheme == ProxyAuthScheme.KERBEROS) { - if (proxy() == null) { - throw new IllegalStateException("Kerberos mode require Proxy parameters."); - } - if (_proxyKerberosPrincipalName == null) { - throw new IllegalStateException("Kerberos mode require Kerberos Principal Name."); - } - if (_proxyKerberosClient == null) { - throw new IllegalStateException("Kerberos mode require Kerberos Http Client."); - } - } - } - private void verifyAllModes() { switch (_impressionsMode) { case OPTIMIZED: @@ -1148,8 +1099,6 @@ public SplitClientConfig build() { throw new IllegalArgumentException("Number of threads for fetching segments MUST be greater than zero"); } - verifyAuthScheme(); - return new SplitClientConfig( _endpoint, _eventsEndpoint, @@ -1203,9 +1152,7 @@ public SplitClientConfig build() { _flagSetsFilter, _invalidSetsCount, _customHeaderDecorator, - _proxyAuthScheme, - _proxyKerberosPrincipalName, - _proxyKerberosClient); + _alternativeHTTPModule); } } } \ No newline at end of file diff --git a/client/src/main/java/io/split/client/SplitFactoryImpl.java b/client/src/main/java/io/split/client/SplitFactoryImpl.java index 7595768d2..41c397b6d 100644 --- a/client/src/main/java/io/split/client/SplitFactoryImpl.java +++ b/client/src/main/java/io/split/client/SplitFactoryImpl.java @@ -57,7 +57,6 @@ import io.split.engine.segments.SegmentChangeFetcher; import io.split.engine.segments.SegmentSynchronizationTaskImp; import io.split.integrations.IntegrationsConfig; -import io.split.service.ProxyAuthScheme; import io.split.service.SplitHttpClientImpl; import io.split.service.SplitHttpClient; @@ -191,12 +190,13 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn // HttpClient _requestDecorator = new RequestDecorator(config.customHeaderDecorator()); - if (config.proxyAuthScheme() != ProxyAuthScheme.KERBEROS) { + if (config.alternativeHTTPModule() == null) { _splitHttpClient = buildSplitHttpClient(apiToken, config, _sdkMetadata, _requestDecorator); } else { - _splitHttpClient = config.proxyKerberosClient(); + _splitHttpClient = config.alternativeHTTPModule(); _splitHttpClient.setMetaData(_sdkMetadata); _splitHttpClient.setRequestDecorator(_requestDecorator); + _splitHttpClient.setApiKey(apiToken); } // Roots diff --git a/client/src/main/java/io/split/service/SplitHttpClient.java b/client/src/main/java/io/split/service/SplitHttpClient.java index 52026aaa0..eba444ec6 100644 --- a/client/src/main/java/io/split/service/SplitHttpClient.java +++ b/client/src/main/java/io/split/service/SplitHttpClient.java @@ -38,4 +38,6 @@ public SplitHttpResponse post(URI uri, public void setMetaData(SDKMetadata metadata); public void setRequestDecorator(RequestDecorator requestDecorator); + + public void setApiKey(String apiKey); } \ No newline at end of file diff --git a/client/src/main/java/io/split/service/SplitHttpClientImpl.java b/client/src/main/java/io/split/service/SplitHttpClientImpl.java index af91400ea..0bdba8bc6 100644 --- a/client/src/main/java/io/split/service/SplitHttpClientImpl.java +++ b/client/src/main/java/io/split/service/SplitHttpClientImpl.java @@ -155,5 +155,9 @@ public void setMetaData(SDKMetadata metadata) { public void setRequestDecorator(RequestDecorator requestDecorator) { // only implemented for Kerberos client } + @Override + public void setApiKey(String apiKey) { + // only implemented for Kerberos client + } } diff --git a/client/src/test/java/io/split/client/LocalhostSplitFactoryYamlTest.java b/client/src/test/java/io/split/client/LocalhostSplitFactoryYamlTest.java index abcc551fe..0a154f7d4 100644 --- a/client/src/test/java/io/split/client/LocalhostSplitFactoryYamlTest.java +++ b/client/src/test/java/io/split/client/LocalhostSplitFactoryYamlTest.java @@ -2,6 +2,7 @@ import io.split.client.utils.LocalhostUtils; import io.split.grammar.Treatments; +import io.split.service.SplitHttpClient; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; diff --git a/client/src/test/java/io/split/client/SplitClientConfigTest.java b/client/src/test/java/io/split/client/SplitClientConfigTest.java index c79e61181..1b640071c 100644 --- a/client/src/test/java/io/split/client/SplitClientConfigTest.java +++ b/client/src/test/java/io/split/client/SplitClientConfigTest.java @@ -6,7 +6,6 @@ import io.split.client.impressions.ImpressionsManager; import io.split.client.dtos.RequestContext; import io.split.integrations.IntegrationsConfig; -import io.split.service.ProxyAuthScheme; import org.junit.Assert; import org.junit.Test; import org.mockito.Mockito; @@ -255,36 +254,4 @@ public Map> getHeaderOverrides(RequestContext context) { Assert.assertNull(config2.customHeaderDecorator()); } - - @Test - public void checkExpectedAuthScheme() { - SplitClientConfig cfg = SplitClientConfig.builder() - .proxyAuthScheme(ProxyAuthScheme.KERBEROS) - .proxyKerberosPrincipalName("bilal@bilal") - .proxyHost("local") - .proxyPort(8080) - .build(); - Assert.assertEquals(ProxyAuthScheme.KERBEROS, cfg.proxyAuthScheme()); - - cfg = SplitClientConfig.builder() - .build(); - Assert.assertEquals(null, cfg.proxyAuthScheme()); - } - - @Test(expected = IllegalStateException.class) - public void testAuthSchemeWithoutProxy() { - SplitClientConfig.builder() - .proxyAuthScheme(ProxyAuthScheme.KERBEROS) - .proxyKerberosPrincipalName("bilal") - .build(); - } - - @Test(expected = IllegalStateException.class) - public void testAuthSchemeWithoutPrincipalName() { - SplitClientConfig.builder() - .proxyAuthScheme(ProxyAuthScheme.KERBEROS) - .proxyHost("local") - .proxyPort(8080) - .build(); - } } \ No newline at end of file diff --git a/client/src/test/java/io/split/client/SplitFactoryImplTest.java b/client/src/test/java/io/split/client/SplitFactoryImplTest.java index ab775553e..f2f7e3efc 100644 --- a/client/src/test/java/io/split/client/SplitFactoryImplTest.java +++ b/client/src/test/java/io/split/client/SplitFactoryImplTest.java @@ -2,11 +2,7 @@ import io.split.client.impressions.ImpressionsManager; import io.split.client.utils.FileTypeEnum; -import io.split.client.utils.SDKMetadata; import io.split.integrations.IntegrationsConfig; -import io.split.service.ProxyAuthScheme; -import io.split.service.SplitHttpClient; -import io.split.service.SplitHttpClientKerberosImpl; import io.split.storages.enums.OperationMode; import io.split.storages.pluggable.domain.UserStorageWrapper; import io.split.telemetry.storage.TelemetryStorage; @@ -14,13 +10,8 @@ import junit.framework.TestCase; import org.junit.Assert; import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.BDDMockito; import org.mockito.Mockito; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; +import static org.mockito.Mockito.when; import pluggable.CustomStorageWrapper; import java.io.FileInputStream; @@ -30,22 +21,9 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.net.InetSocketAddress; -import java.net.Proxy; import java.net.URISyntaxException; -import java.util.HashMap; -import java.util.Map; - -import okhttp3.Authenticator; -import okhttp3.OkHttpClient; -import okhttp3.logging.HttpLoggingInterceptor; -import okhttp3.Interceptor; -import static org.mockito.Mockito.when; - -@RunWith(PowerMockRunner.class) -@PrepareForTest(SplitFactoryImpl.class) public class SplitFactoryImplTest extends TestCase { public static final String API_KEY ="29013ionasdasd09u"; public static final String ENDPOINT = "https://sdk.split-stage.io"; @@ -368,96 +346,4 @@ public void testLocalhosJsonInputStreamNullAndFileTypeNull() throws URISyntaxExc Object splitChangeFetcher = method.invoke(splitFactory, splitClientConfig); Assert.assertTrue(splitChangeFetcher instanceof LegacyLocalhostSplitChangeFetcher); } - - @Test - public void testBuildKerberosClientParams() throws URISyntaxException, IOException { - PowerMockito.mockStatic(SplitFactoryImpl.class); - - ArgumentCaptor proxyCaptor = ArgumentCaptor.forClass(Proxy.class); - ArgumentCaptor configCaptor = ArgumentCaptor.forClass(SplitClientConfig.class); - ArgumentCaptor< HttpLoggingInterceptor> logCaptor = ArgumentCaptor.forClass( HttpLoggingInterceptor.class); - ArgumentCaptor authCaptor = ArgumentCaptor.forClass(Authenticator.class); - - SplitClientConfig splitClientConfig = SplitClientConfig.builder() - .setBlockUntilReadyTimeout(10000) - .proxyAuthScheme(ProxyAuthScheme.KERBEROS) - .proxyKerberosPrincipalName("bilal@localhost") - .proxyPort(6060) - .proxyHost(ENDPOINT) - .build(); - - Map kerberosOptions = new HashMap(); - kerberosOptions.put("com.sun.security.auth.module.Krb5LoginModule", "required"); - kerberosOptions.put("refreshKrb5Config", "false"); - kerberosOptions.put("doNotPrompt", "false"); - kerberosOptions.put("useTicketCache", "true"); - BDDMockito.given(SplitFactoryImpl.getProxyAuthenticator(splitClientConfig, kerberosOptions)) - .willReturn(null); - - RequestDecorator requestDecorator = new RequestDecorator(null); - SDKMetadata sdkmeta = new SDKMetadata("java-1.2.3", "1.2.3.4", "someIP"); - PowerMockito.when(SplitFactoryImpl.buildSplitHttpClient("qwer", - splitClientConfig, - sdkmeta, - requestDecorator)).thenCallRealMethod(); - - SplitHttpClient splitHttpClient = SplitFactoryImpl.buildSplitHttpClient("qwer", - splitClientConfig, - sdkmeta, - requestDecorator); - - PowerMockito.verifyStatic(); - SplitFactoryImpl.buildOkHttpClient(proxyCaptor.capture(), configCaptor.capture(),logCaptor.capture(), authCaptor.capture()); - - Assert.assertTrue(splitHttpClient instanceof SplitHttpClientKerberosImpl); - Assert.assertEquals("HTTP @ https://sdk.split-stage.io:6060", proxyCaptor.getValue().toString()); - Assert.assertTrue(logCaptor.getValue() instanceof okhttp3.logging.HttpLoggingInterceptor); - } - - @Test - public void testFactoryKerberosInstance() throws URISyntaxException, IOException { - OkHttpClient okHttpClient = new OkHttpClient.Builder().build(); - PowerMockito.stub(PowerMockito.method(SplitFactoryImpl.class, "buildOkHttpClient")).toReturn(okHttpClient); - PowerMockito.stub(PowerMockito.method(SplitFactoryImpl.class, "getProxyAuthenticator")).toReturn(null); - - SplitClientConfig splitClientConfig = SplitClientConfig.builder() - .setBlockUntilReadyTimeout(10000) - .proxyAuthScheme(ProxyAuthScheme.KERBEROS) - .proxyKerberosPrincipalName("bilal@localhost") - .proxyPort(6060) - .proxyHost(ENDPOINT) - .build(); - - Map kerberosOptions = new HashMap(); - kerberosOptions.put("com.sun.security.auth.module.Krb5LoginModule", "required"); - kerberosOptions.put("refreshKrb5Config", "false"); - kerberosOptions.put("doNotPrompt", "false"); - kerberosOptions.put("useTicketCache", "true"); - - RequestDecorator requestDecorator = new RequestDecorator(null); - SDKMetadata sdkmeta = new SDKMetadata("java-1.2.3", "1.2.3.4", "someIP"); - SplitHttpClient splitHttpClient = SplitFactoryImpl.buildSplitHttpClient("qwer", - splitClientConfig, - sdkmeta, - requestDecorator); - Assert.assertTrue(splitHttpClient instanceof SplitHttpClientKerberosImpl); - } - - @Test - public void testBuildOkHttpClient() { - SplitClientConfig splitClientConfig = SplitClientConfig.builder() - .setBlockUntilReadyTimeout(10000) - .proxyAuthScheme(ProxyAuthScheme.KERBEROS) - .proxyKerberosPrincipalName("bilal@localhost") - .proxyPort(6060) - .proxyHost(ENDPOINT) - .build(); - HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); - Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("host", 8080)); - OkHttpClient okHttpClient = SplitFactoryImpl.buildOkHttpClient(proxy, - splitClientConfig, loggingInterceptor, Authenticator.NONE); - assertEquals(Authenticator.NONE, okHttpClient.authenticator()); - assertEquals(proxy, okHttpClient.proxy()); - assertEquals(loggingInterceptor, okHttpClient.interceptors().get(0)); - } } \ No newline at end of file diff --git a/kerberos/pom.xml b/http-modules/pom.xml similarity index 95% rename from kerberos/pom.xml rename to http-modules/pom.xml index 461ac046e..3d5f4a980 100644 --- a/kerberos/pom.xml +++ b/http-modules/pom.xml @@ -9,10 +9,10 @@ 4.0.0 - kerberos + http-modules jar - Kerberos - Kerberos Authentication + http-modules + Alternative Http Modules diff --git a/kerberos/src/main/java/io/split/kerberos/HTTPKerberosAuthInterceptor.java b/http-modules/src/main/java/io/split/httpmodules/okhttp/HTTPKerberosAuthInterceptor.java similarity index 99% rename from kerberos/src/main/java/io/split/kerberos/HTTPKerberosAuthInterceptor.java rename to http-modules/src/main/java/io/split/httpmodules/okhttp/HTTPKerberosAuthInterceptor.java index b72a8fefb..26bd23ea5 100644 --- a/kerberos/src/main/java/io/split/kerberos/HTTPKerberosAuthInterceptor.java +++ b/http-modules/src/main/java/io/split/httpmodules/okhttp/HTTPKerberosAuthInterceptor.java @@ -1,4 +1,4 @@ -package io.split.kerberos; +package io.split.httpmodules.okhttp; import java.io.IOException; import java.util.Map; diff --git a/kerberos/src/main/java/io/split/kerberos/KerberosAuthException.java b/http-modules/src/main/java/io/split/httpmodules/okhttp/KerberosAuthException.java similarity index 87% rename from kerberos/src/main/java/io/split/kerberos/KerberosAuthException.java rename to http-modules/src/main/java/io/split/httpmodules/okhttp/KerberosAuthException.java index 563bcf423..06fa2672f 100644 --- a/kerberos/src/main/java/io/split/kerberos/KerberosAuthException.java +++ b/http-modules/src/main/java/io/split/httpmodules/okhttp/KerberosAuthException.java @@ -1,4 +1,4 @@ -package io.split.kerberos; +package io.split.httpmodules.okhttp; public class KerberosAuthException extends Exception { public KerberosAuthException(String message) { diff --git a/http-modules/src/main/java/io/split/httpmodules/okhttp/OkHttpModule.java b/http-modules/src/main/java/io/split/httpmodules/okhttp/OkHttpModule.java new file mode 100644 index 000000000..f344351ed --- /dev/null +++ b/http-modules/src/main/java/io/split/httpmodules/okhttp/OkHttpModule.java @@ -0,0 +1,372 @@ +package io.split.httpmodules.okhttp; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import io.split.client.RequestDecorator; +import io.split.client.dtos.SplitHttpResponse; +import io.split.client.utils.SDKMetadata; +import io.split.engine.common.FetchOptions; +import io.split.service.SplitHttpClient; + +import split.org.apache.hc.client5.http.classic.methods.HttpGet; +import split.org.apache.hc.core5.http.Header; +import split.org.apache.hc.core5.http.HttpEntity; +import split.org.apache.hc.core5.http.HttpRequest; +import split.org.apache.hc.core5.http.io.entity.EntityUtils; +import split.org.apache.hc.core5.http.message.BasicHeader; + +import okhttp3.Authenticator; +import okhttp3.OkHttpClient; +import okhttp3.OkHttpClient.Builder; +import okhttp3.logging.HttpLoggingInterceptor; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.Request.*; +import okhttp3.RequestBody; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class OkHttpModule implements SplitHttpClient { + private static final int DEFAULT_CONNECTION_TIMEOUT = 10000; + private static final int DEFAULT_READ_TIMEOUT = 10000; + private final boolean _debugEnabled; + private final int _connectionTimeout; + private final int _readTimeout; + private final Proxy _proxy; + private final ProxyAuthScheme _proxyAuthScheme; + private final String _proxyAuthKerberosPrincipalName; + public final OkHttpClient httpClient; + private static final Logger _log = LoggerFactory.getLogger(OkHttpModule.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 RequestDecorator _requestDecorator; + private String _apikey; + private SDKMetadata _metadata; + + public static Builder builder() { + return new Builder(); + } + + private OkHttpModule(ProxyAuthScheme proxyAuthScheme, + String proxyAuthKerberosPrincipalName, + Proxy proxy, + int connectionTimeout, + int readTimeout, + boolean debugEnabled) throws IOException { + _proxyAuthScheme = proxyAuthScheme; + _proxyAuthKerberosPrincipalName = proxyAuthKerberosPrincipalName; + _proxy = proxy; + _connectionTimeout = connectionTimeout; + _readTimeout = readTimeout; + _debugEnabled = debugEnabled; + + HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); + if (_debugEnabled) { + logging.setLevel(HttpLoggingInterceptor.Level.HEADERS); + } else { + logging.setLevel(HttpLoggingInterceptor.Level.NONE); + } + + Map kerberosOptions = new HashMap<>(); + kerberosOptions.put("com.sun.security.auth.module.Krb5LoginModule", "required"); + kerberosOptions.put("refreshKrb5Config", "false"); + kerberosOptions.put("doNotPrompt", "false"); + kerberosOptions.put("useTicketCache", "true"); + + Authenticator proxyAuthenticator = getProxyAuthenticator(_proxyAuthKerberosPrincipalName, kerberosOptions); + httpClient = new okhttp3.OkHttpClient.Builder() + .proxy(_proxy) + .readTimeout(_readTimeout, TimeUnit.MILLISECONDS) + .connectTimeout(_connectionTimeout, TimeUnit.MILLISECONDS) + .addInterceptor(logging) + .proxyAuthenticator(proxyAuthenticator) + .build(); + } + + public OkHttpClient httpClient() { + return httpClient; + } + public Proxy proxy() { + return _proxy; + } + public ProxyAuthScheme proxyAuthScheme() { + return _proxyAuthScheme; + } + public String proxyKerberosPrincipalName() { return _proxyAuthKerberosPrincipalName; } + public int connectionTimeout() { + return _connectionTimeout; + } + public boolean debugEnabled() { + return _debugEnabled; + } + public int readTimeout() { + return _readTimeout; + } + + public static final class Builder { + private int _connectionTimeout = 15000; + private int _readTimeout = 15000; + private String _proxyHost = "localhost"; + private int _proxyPort = -1; + private ProxyAuthScheme _proxyAuthScheme = null; + private String _proxyKerberosPrincipalName = null; + private boolean _debugEnabled = false; + + public Builder() { + } + + public Builder debugEnabled() { + _debugEnabled = true; + return this; + } + + /** + * The host location of the proxy. Default is localhost. + * + * @param proxyHost location of the proxy + * @return this builder + */ + public Builder proxyHost(String proxyHost) { + _proxyHost = proxyHost; + return this; + } + + /** + * The port of the proxy. Default is -1. + * + * @param proxyPort port for the proxy + * @return this builder + */ + public Builder proxyPort(int proxyPort) { + _proxyPort = proxyPort; + return this; + } + + Proxy proxy() { + if (_proxyPort != -1) { + return new Proxy(Proxy.Type.HTTP, new InetSocketAddress(_proxyHost, _proxyPort)); + } + // Default is no proxy. + return null; + } + + /** + * Authentication Scheme + * + * @param proxyAuthScheme + * @return this builder + */ + public Builder proxyAuthScheme(ProxyAuthScheme proxyAuthScheme) { + _proxyAuthScheme = proxyAuthScheme; + return this; + } + + /** + * Kerberos Principal Account Name + * + * @param proxyKerberosPrincipalName + * @return this builder + */ + public Builder proxyKerberosPrincipalName(String proxyKerberosPrincipalName) { + _proxyKerberosPrincipalName = proxyKerberosPrincipalName; + return this; + } + + private void verifyAuthScheme() { + if (_proxyAuthScheme == ProxyAuthScheme.KERBEROS) { + if (proxy() == null) { + throw new IllegalStateException("Kerberos mode require Proxy parameters."); + } + if (_proxyKerberosPrincipalName == null) { + throw new IllegalStateException("Kerberos mode require Kerberos Principal Name."); + } + } + } + + private void verifyTimeouts() { + if (_connectionTimeout <= 0 || _connectionTimeout > DEFAULT_CONNECTION_TIMEOUT) { + _connectionTimeout = DEFAULT_CONNECTION_TIMEOUT; + } + if (_readTimeout <= 0 || _readTimeout > DEFAULT_READ_TIMEOUT) { + _readTimeout = DEFAULT_READ_TIMEOUT; + } + } + + public OkHttpModule build() throws IOException { + verifyTimeouts(); + verifyAuthScheme(); + + return new OkHttpModule( + _proxyAuthScheme, + _proxyKerberosPrincipalName, + proxy(), + _connectionTimeout, + _readTimeout, + _debugEnabled); + } + } + + public HTTPKerberosAuthInterceptor getProxyAuthenticator(String proxyKerberosPrincipalName, + Map kerberosOptions) throws IOException { + return new HTTPKerberosAuthInterceptor(proxyKerberosPrincipalName, kerberosOptions); + } + + @Override + public void setApiKey(String apikey) { + _apikey = apikey; + } + + @Override + public void setMetaData(SDKMetadata metadata) { + _metadata = metadata; + } + + @Override + public void setRequestDecorator(RequestDecorator requestDecorator) { + _requestDecorator = requestDecorator; + } + + @Override + public SplitHttpResponse get(URI uri, FetchOptions options, Map> additionalHeaders) { + try { + okhttp3.Request.Builder requestBuilder = new okhttp3.Request.Builder(); + requestBuilder.url(uri.toString()); + setBasicHeaders(requestBuilder); + setAdditionalAndDecoratedHeaders(requestBuilder, additionalHeaders); + if (options.cacheControlHeadersEnabled()) { + requestBuilder.addHeader(HEADER_CACHE_CONTROL_NAME, HEADER_CACHE_CONTROL_VALUE); + } + + Request request = requestBuilder.build(); + _log.debug(String.format("Request Headers: %s", request.headers())); + + Response response = httpClient.newCall(request).execute(); + + int responseCode = response.code(); + + _log.debug(String.format("[GET] %s. Status code: %s", + request.url().toString(), + responseCode)); + + String statusMessage = ""; + if (responseCode < HttpURLConnection.HTTP_OK || responseCode >= HttpURLConnection.HTTP_MULT_CHOICE) { + _log.warn(String.format("Response status was: %s. Reason: %s", responseCode, + response.message())); + statusMessage = response.message(); + } + + String responseBody = response.body().string(); + response.close(); + + return new SplitHttpResponse(responseCode, + statusMessage, + responseBody, + getResponseHeaders(response)); + } catch (Exception e) { + throw new IllegalStateException(String.format("Problem in http get operation: %s", e), e); + } + } + + @Override + public SplitHttpResponse post(URI url, HttpEntity entity, + Map> additionalHeaders) { + try { + okhttp3.Request.Builder requestBuilder = getRequestBuilder(); + requestBuilder.url(url.toString()); + setBasicHeaders(requestBuilder); + setAdditionalAndDecoratedHeaders(requestBuilder, additionalHeaders); + requestBuilder.addHeader("Accept-Encoding", "gzip"); + requestBuilder.addHeader("Content-Type", "application/json"); + String post = EntityUtils.toString((HttpEntity) entity); + RequestBody postBody = RequestBody.create(post.getBytes()); + requestBuilder.post(postBody); + + Request request = getRequest(requestBuilder); + _log.debug(String.format("Request Headers: %s", request.headers())); + + Response response = httpClient.newCall(request).execute(); + + int responseCode = response.code(); + + _log.debug(String.format("[GET] %s. Status code: %s", + request.url().toString(), + responseCode)); + + String statusMessage = ""; + if (responseCode < HttpURLConnection.HTTP_OK || responseCode >= HttpURLConnection.HTTP_MULT_CHOICE) { + _log.warn(String.format("Response status was: %s. Reason: %s", responseCode, + response.message())); + statusMessage = response.message(); + } + response.close(); + + return new SplitHttpResponse(responseCode, statusMessage, "", getResponseHeaders(response)); + } catch (Exception e) { + throw new IllegalStateException(String.format("Problem in http post operation: %s", e), e); + } + } + + protected okhttp3.Request.Builder getRequestBuilder() { + return new okhttp3.Request.Builder(); + } + + protected Request getRequest(okhttp3.Request.Builder requestBuilder) { + return requestBuilder.build(); + } + protected void setBasicHeaders(okhttp3.Request.Builder requestBuilder) { + requestBuilder.addHeader(HEADER_API_KEY, "Bearer " + _apikey); + requestBuilder.addHeader(HEADER_CLIENT_VERSION, _metadata.getSdkVersion()); + requestBuilder.addHeader(HEADER_CLIENT_MACHINE_IP, _metadata.getMachineIp()); + requestBuilder.addHeader(HEADER_CLIENT_MACHINE_NAME, _metadata.getMachineName()); + requestBuilder.addHeader(HEADER_CLIENT_KEY, _apikey.length() > 4 + ? _apikey.substring(_apikey.length() - 4) + : _apikey); + } + + protected void setAdditionalAndDecoratedHeaders(okhttp3.Request.Builder requestBuilder, Map> additionalHeaders) { + if (additionalHeaders != null) { + for (Map.Entry> entry : additionalHeaders.entrySet()) { + for (String value : entry.getValue()) { + requestBuilder.addHeader(entry.getKey(), value); + } + } + } + HttpRequest request = new HttpGet(""); + _requestDecorator.decorateHeaders(request); + for (Header header : request.getHeaders()) { + requestBuilder.addHeader(header.getName(), header.getValue()); + } + } + + protected Header[] getResponseHeaders(Response response) { + List responseHeaders = new ArrayList<>(); + Map> map = response.headers().toMultimap(); + for (Map.Entry> entry : map.entrySet()) { + if (entry.getKey() != null) { + BasicHeader responseHeader = new BasicHeader(entry.getKey(), entry.getValue()); + responseHeaders.add(responseHeader); + } + } + return responseHeaders.toArray(new split.org.apache.hc.core5.http.Header[0]); + } + @Override + public void close() throws IOException { + httpClient.dispatcher().executorService().shutdown(); + } + +} diff --git a/client/src/main/java/io/split/service/ProxyAuthScheme.java b/http-modules/src/main/java/io/split/httpmodules/okhttp/ProxyAuthScheme.java similarity index 55% rename from client/src/main/java/io/split/service/ProxyAuthScheme.java rename to http-modules/src/main/java/io/split/httpmodules/okhttp/ProxyAuthScheme.java index 1d4c237bf..4340829a2 100644 --- a/client/src/main/java/io/split/service/ProxyAuthScheme.java +++ b/http-modules/src/main/java/io/split/httpmodules/okhttp/ProxyAuthScheme.java @@ -1,4 +1,4 @@ -package io.split.service; +package io.split.httpmodules.okhttp; public enum ProxyAuthScheme { KERBEROS diff --git a/http-modules/src/test/java/io/split/httpmodules/okhttp/HTTPKerberosAuthIntercepterTest.java b/http-modules/src/test/java/io/split/httpmodules/okhttp/HTTPKerberosAuthIntercepterTest.java new file mode 100644 index 000000000..b56c7bff0 --- /dev/null +++ b/http-modules/src/test/java/io/split/httpmodules/okhttp/HTTPKerberosAuthIntercepterTest.java @@ -0,0 +1,115 @@ +package io.split.httpmodules.okhttp; + +import io.split.httpmodules.okhttp.HTTPKerberosAuthInterceptor; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import javax.security.auth.Subject; +import javax.security.auth.kerberos.KerberosPrincipal; +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.internal.verification.VerificationModeFactory.times; +import static org.powermock.api.mockito.PowerMockito.*; + +import java.security.PrivilegedActionException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +@RunWith(PowerMockRunner.class) +@PrepareForTest(HTTPKerberosAuthInterceptor.class) +public class HTTPKerberosAuthIntercepterTest { +/* + @Test + public void testBasicFlow() throws Exception { + System.setProperty("java.security.krb5.conf", "src/test/resources/krb5.conf"); + + HTTPKerberosAuthInterceptor kerberosAuthInterceptor = mock(HTTPKerberosAuthInterceptor.class); + LoginContext loginContext = PowerMockito.mock(LoginContext.class); + when(kerberosAuthInterceptor.getLoginContext(any())).thenReturn((loginContext)); + + doCallRealMethod().when(kerberosAuthInterceptor).buildSubjectCredentials(); + kerberosAuthInterceptor.buildSubjectCredentials(); + verify(loginContext, times(1)).login(); + + Subject subject = new Subject(); + when(loginContext.getSubject()).thenReturn(subject); + doCallRealMethod().when(kerberosAuthInterceptor).getContextSubject(); + kerberosAuthInterceptor.getContextSubject(); + verify(loginContext, times(1)).getSubject(); + + subject.getPrincipals().add(new KerberosPrincipal("bilal")); + subject.getPublicCredentials().add(new KerberosPrincipal("name")); + subject.getPrivateCredentials().add(new KerberosPrincipal("name")); + + doCallRealMethod().when(kerberosAuthInterceptor).getClientPrincipalName(); + assertThat(kerberosAuthInterceptor.getClientPrincipalName(), is(equalTo("bilal@ATHENA.MIT.EDU"))) ; + verify(loginContext, times(2)).getSubject(); + + when(kerberosAuthInterceptor.buildAuthorizationHeader(any())).thenReturn("secured-token"); + okhttp3.Request originalRequest = new okhttp3.Request.Builder().url("http://somthing").build(); + okhttp3.Response response = new okhttp3.Response.Builder().code(200).request(originalRequest). + protocol(okhttp3.Protocol.HTTP_1_1).message("ok").build(); + doCallRealMethod().when(kerberosAuthInterceptor).authenticate(null, response); + okhttp3.Request request = kerberosAuthInterceptor.authenticate(null, response); + assertThat(request.headers("Proxy-authorization"), is(equalTo(Arrays.asList("Negotiate secured-token")))); + } + + @Test + public void testKerberosLoginConfiguration() { + Map kerberosOptions = new HashMap(); + kerberosOptions.put("com.sun.security.auth.module.Krb5LoginModule", "required"); + kerberosOptions.put("refreshKrb5Config", "false"); + kerberosOptions.put("doNotPrompt", "false"); + kerberosOptions.put("useTicketCache", "true"); + + HTTPKerberosAuthInterceptor.KerberosLoginConfiguration kerberosConfig = new HTTPKerberosAuthInterceptor.KerberosLoginConfiguration(kerberosOptions); + AppConfigurationEntry[] appConfig = kerberosConfig.getAppConfigurationEntry(""); + assertThat("com.sun.security.auth.module.Krb5LoginModule", is(equalTo(appConfig[0].getLoginModuleName()))); + assertThat(AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, is(equalTo(appConfig[0].getControlFlag()))); + } + + @Test(expected = IllegalStateException.class) + public void testKerberosLoginConfigurationException() { + HTTPKerberosAuthInterceptor.KerberosLoginConfiguration kerberosConfig = new HTTPKerberosAuthInterceptor.KerberosLoginConfiguration(); + AppConfigurationEntry[] appConfig = kerberosConfig.getAppConfigurationEntry(""); + } + + @Test + public void testBuildAuthorizationHeader() throws LoginException, PrivilegedActionException { + System.setProperty("java.security.krb5.conf", "src/test/resources/krb5.conf"); + + HTTPKerberosAuthInterceptor kerberosAuthInterceptor = mock(HTTPKerberosAuthInterceptor.class); + HTTPKerberosAuthInterceptor.CreateAuthorizationHeaderAction ahh = mock(HTTPKerberosAuthInterceptor.CreateAuthorizationHeaderAction.class); + when(ahh.getNegotiateToken()).thenReturn("secret-token"); + when(kerberosAuthInterceptor.getAuthorizationHeaderAction(any(), any())).thenReturn(ahh); + + LoginContext loginContext = PowerMockito.mock(LoginContext.class); + doCallRealMethod().when(kerberosAuthInterceptor).buildAuthorizationHeader("bilal"); + Subject subject = new Subject(); + when(loginContext.getSubject()).thenReturn(subject); + when(kerberosAuthInterceptor.getContextSubject()).thenReturn(subject); + when(kerberosAuthInterceptor.getLoginContext(subject)).thenReturn((loginContext)); + doCallRealMethod().when(kerberosAuthInterceptor).buildSubjectCredentials(); + kerberosAuthInterceptor.buildSubjectCredentials(); + + subject.getPrincipals().add(new KerberosPrincipal("bilal")); + subject.getPublicCredentials().add(new KerberosPrincipal("name")); + subject.getPrivateCredentials().add(new KerberosPrincipal("name")); + doCallRealMethod().when(kerberosAuthInterceptor).getClientPrincipalName(); + + assertThat("secret-token", is(equalTo(kerberosAuthInterceptor.buildAuthorizationHeader("bilal")))); + } + + */ +} diff --git a/http-modules/src/test/java/io/split/httpmodules/okhttp/HttpSplitClientKerberosTest.java b/http-modules/src/test/java/io/split/httpmodules/okhttp/HttpSplitClientKerberosTest.java new file mode 100644 index 000000000..75aed5a82 --- /dev/null +++ b/http-modules/src/test/java/io/split/httpmodules/okhttp/HttpSplitClientKerberosTest.java @@ -0,0 +1,316 @@ +package io.split.httpmodules.okhttp; + +import com.google.common.base.Charsets; +import com.google.common.io.Files; + +import io.split.client.CustomHeaderDecorator; +import io.split.client.RequestDecorator; +import io.split.client.dtos.*; +import io.split.client.impressions.Impression; +import io.split.client.utils.Json; +import io.split.client.utils.SDKMetadata; +import io.split.client.utils.Utils; +import io.split.engine.common.FetchOptions; + +import okhttp3.OkHttpClient; +//import okhttp3.OkHttpClient.Builder; +import okhttp3.HttpUrl; +import okhttp3.Headers; + +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; +import split.org.apache.hc.core5.http.*; +import split.org.apache.hc.core5.http.io.entity.EntityUtils; +import org.junit.Assert; +import org.junit.Test; + +import java.io.*; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.HttpURLConnection; +import java.net.Proxy; +import java.net.InetSocketAddress; +import java.util.List; +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; + +public class HttpSplitClientKerberosTest { +/* + @Test + public void testGetWithSpecialCharacters() throws IOException, InterruptedException { + MockWebServer server = new MockWebServer(); + BufferedReader br = new BufferedReader(new FileReader("src/test/resources/split-change-special-characters.json")); + String body; + try { + StringBuilder sb = new StringBuilder(); + String line = br.readLine(); + + while (line != null) { + sb.append(line); + sb.append(System.lineSeparator()); + line = br.readLine(); + } + body = sb.toString(); + } finally { + br.close(); + } + + server.enqueue(new MockResponse().setBody(body).addHeader(HttpHeaders.VIA, "HTTP/1.1 s_proxy_rio1")); + server.start(); + HttpUrl baseUrl = server.url("/v1/"); + URI rootTarget = baseUrl.uri(); + RequestDecorator decorator = new RequestDecorator(null); + OkHttpClient client = new Builder().build(); + + OkHttpModuleImpl okHttpModuleImpl = new OkHttpModuleImpl(client, "qwerty"); + okHttpModuleImpl.setMetaData(metadata()); + okHttpModuleImpl.setRequestDecorator(decorator); + + Map> additionalHeaders = Collections.singletonMap("AdditionalHeader", + Collections.singletonList("add")); + + SplitHttpResponse splitHttpResponse = okHttpModuleImpl.get(rootTarget, + new FetchOptions.Builder().cacheControlHeaders(true).build(), additionalHeaders); + + + RecordedRequest request = server.takeRequest(); + server.shutdown(); + Headers requestHeaders = request.getHeaders(); + + assertThat(splitHttpResponse.statusCode(), is(equalTo(HttpURLConnection.HTTP_OK))); + Assert.assertEquals("/v1/", request.getPath()); + assertThat(requestHeaders.get("Authorization"), is(equalTo("Bearer qwerty"))) ; + assertThat(requestHeaders.get("SplitSDKClientKey"), is(equalTo("erty"))); + assertThat(requestHeaders.get("SplitSDKVersion"), is(equalTo("java-1.2.3"))); + assertThat(requestHeaders.get("SplitSDKMachineIP"), is(equalTo("1.2.3.4"))); + assertThat(requestHeaders.get("SplitSDKMachineName"), is(equalTo("someIP"))); + assertThat(requestHeaders.get("AdditionalHeader"), is(equalTo("add"))); + + SplitChange change = Json.fromJson(splitHttpResponse.body(), SplitChange.class); + Header[] headers = splitHttpResponse.responseHeaders(); + assertThat(headers[1].getName(), is(equalTo("via"))); + assertThat(headers[1].getValue(), is(equalTo("[HTTP/1.1 s_proxy_rio1]"))); + assertThat(splitHttpResponse.statusCode(), is(equalTo(200))); + Assert.assertNotNull(change); + Assert.assertEquals(1, change.splits.size()); + Assert.assertNotNull(change.splits.get(0)); + + Split split = change.splits.get(0); + Map configs = split.configurations; + Assert.assertEquals(2, configs.size()); + Assert.assertEquals("{\"test\": \"blue\",\"grüne Straße\": 13}", configs.get("on")); + Assert.assertEquals("{\"test\": \"blue\",\"size\": 15}", configs.get("off")); + Assert.assertEquals(2, split.sets.size()); + okHttpModuleImpl.close(); + } + + @Test + public void testGetErrors() throws IOException, InterruptedException { + MockWebServer server = new MockWebServer(); + server.enqueue(new MockResponse().setBody("").setResponseCode(HttpURLConnection.HTTP_INTERNAL_ERROR)); + server.start(); + HttpUrl baseUrl = server.url("/v1/"); + URI rootTarget = baseUrl.uri(); + RequestDecorator decorator = new RequestDecorator(null); + + OkHttpClient client = new Builder().build(); + + OkHttpModuleImpl okHttpModuleImpl = new OkHttpModuleImpl(client, "qwerty"); + okHttpModuleImpl.setMetaData(metadata()); + okHttpModuleImpl.setRequestDecorator(decorator); + + Map> additionalHeaders = Collections.singletonMap("AdditionalHeader", + Collections.singletonList("add")); + + SplitHttpResponse splitHttpResponse = okHttpModuleImpl.get(rootTarget, + new FetchOptions.Builder().cacheControlHeaders(true).build(), additionalHeaders); + + + RecordedRequest request = server.takeRequest(); + server.shutdown(); + assertThat(splitHttpResponse.statusCode(), is(equalTo(HttpURLConnection.HTTP_INTERNAL_ERROR))); + okHttpModuleImpl.close(); + } + + @Test + public void testGetParameters() throws IOException, InterruptedException { + class MyCustomHeaders implements CustomHeaderDecorator { + public MyCustomHeaders() {} + @Override + public Map> getHeaderOverrides(RequestContext context) { + Map> additionalHeaders = context.headers(); + additionalHeaders.put("first", Arrays.asList("1")); + additionalHeaders.put("second", Arrays.asList("2.1", "2.2")); + additionalHeaders.put("third", Arrays.asList("3")); + return additionalHeaders; + } + } + + MockWebServer server = new MockWebServer(); + BufferedReader br = new BufferedReader(new FileReader("src/test/resources/split-change-special-characters.json")); + String body; + try { + StringBuilder sb = new StringBuilder(); + String line = br.readLine(); + + while (line != null) { + sb.append(line); + sb.append(System.lineSeparator()); + line = br.readLine(); + } + body = sb.toString(); + } finally { + br.close(); + } + + server.enqueue(new MockResponse().setBody(body).addHeader(HttpHeaders.VIA, "HTTP/1.1 s_proxy_rio1")); + server.start(); + HttpUrl baseUrl = server.url("/splitChanges?since=1234567"); + URI rootTarget = baseUrl.uri(); + RequestDecorator decorator = new RequestDecorator(new MyCustomHeaders()); + OkHttpClient client = new Builder().build(); + + OkHttpModuleImpl okHttpModuleImpl = new OkHttpModuleImpl(client, "qwerty"); + okHttpModuleImpl.setMetaData(metadata()); + okHttpModuleImpl.setRequestDecorator(decorator); + + FetchOptions options = new FetchOptions.Builder().cacheControlHeaders(true).build(); + SplitHttpResponse splitHttpResponse = okHttpModuleImpl.get(rootTarget, options, null); + + RecordedRequest request = server.takeRequest(); + server.shutdown(); + Headers requestHeaders = request.getHeaders(); + + assertThat(requestHeaders.get("Cache-Control"), is(equalTo("no-cache"))); + assertThat(requestHeaders.get("first"), is(equalTo("1"))); + assertThat(requestHeaders.values("second"), is(equalTo(Arrays.asList("2.1","2.2")))); + assertThat(requestHeaders.get("third"), is(equalTo("3"))); + Assert.assertEquals("/splitChanges?since=1234567", request.getPath()); + assertThat(request.getMethod(), is(equalTo("GET"))); + } + + @Test(expected = IllegalStateException.class) + public void testException() throws URISyntaxException, IOException { + URI uri = new URI("https://api.split.io/splitChanges?since=1234567"); + RequestDecorator decorator = null; + + ByteArrayInputStream stubInputStream = new ByteArrayInputStream(Files.asCharSource( + new File("src/test/resources/split-change-special-characters.json"), Charsets.UTF_8).read().getBytes(Charsets.UTF_8)); + + Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("1.0.0.127", 8080)); + OkHttpClient client = new OkHttpClient.Builder() + .proxy(proxy) + .build(); + + OkHttpModuleImpl okHttpModuleImpl = new OkHttpModuleImpl(client, "qwerty"); + okHttpModuleImpl.setMetaData(metadata()); + okHttpModuleImpl.setRequestDecorator(decorator); + SplitHttpResponse splitHttpResponse = splitHtpClientKerberos.get(uri, + new FetchOptions.Builder().cacheControlHeaders(true).build(), null); + } + + @Test + public void testPost() throws IOException, ParseException, InterruptedException { + MockWebServer server = new MockWebServer(); + + server.enqueue(new MockResponse().addHeader(HttpHeaders.VIA, "HTTP/1.1 s_proxy_rio1")); + server.start(); + HttpUrl baseUrl = server.url("/impressions"); + URI rootTarget = baseUrl.uri(); + RequestDecorator decorator = new RequestDecorator(null); + OkHttpClient client = new Builder().build(); + + OkHttpModuleImpl okHttpModuleImpl = new OkHttpModuleImpl(client, "qwerty"); + okHttpModuleImpl.setMetaData(metadata()); + okHttpModuleImpl.setRequestDecorator(decorator); + + FetchOptions options = new FetchOptions.Builder().cacheControlHeaders(true).build(); + // Send impressions + List toSend = Arrays.asList(new TestImpressions("t1", Arrays.asList( + KeyImpression.fromImpression(new Impression("k1", null, "t1", "on", 123L, "r1", 456L, null)), + KeyImpression.fromImpression(new Impression("k2", null, "t1", "on", 123L, "r1", 456L, null)), + KeyImpression.fromImpression(new Impression("k3", null, "t1", "on", 123L, "r1", 456L, null)))), + new TestImpressions("t2", Arrays.asList( + KeyImpression.fromImpression(new Impression("k1", null, "t2", "on", 123L, "r1", 456L, null)), + KeyImpression.fromImpression(new Impression("k2", null, "t2", "on", 123L, "r1", 456L, null)), + KeyImpression.fromImpression(new Impression("k3", null, "t2", "on", 123L, "r1", 456L, null))))); + + Map> additionalHeaders = Collections.singletonMap("SplitSDKImpressionsMode", + Collections.singletonList("OPTIMIZED")); + + SplitHttpResponse splitHttpResponse = okHttpModuleImpl.post(rootTarget, Utils.toJsonEntity(toSend), + additionalHeaders); + + RecordedRequest request = server.takeRequest(); + server.shutdown(); + Headers requestHeaders = request.getHeaders(); + String postBody = EntityUtils.toString(Utils.toJsonEntity(toSend)); + + Assert.assertEquals("POST /impressions HTTP/1.1", request.getRequestLine()); + Assert.assertEquals(postBody, request.getBody().readUtf8()); + assertThat(requestHeaders.get("Authorization"), is(equalTo("Bearer qwerty"))) ; + assertThat(requestHeaders.get("SplitSDKClientKey"), is(equalTo("erty"))); + assertThat(requestHeaders.get("SplitSDKVersion"), is(equalTo("java-1.2.3"))); + assertThat(requestHeaders.get("SplitSDKMachineIP"), is(equalTo("1.2.3.4"))); + assertThat(requestHeaders.get("SplitSDKMachineName"), is(equalTo("someIP"))); + assertThat(requestHeaders.get("SplitSDKImpressionsMode"), is(equalTo("OPTIMIZED"))); + + Header[] headers = splitHttpResponse.responseHeaders(); + assertThat(headers[1].getName(), is(equalTo("via"))); + assertThat(headers[1].getValue(), is(equalTo("[HTTP/1.1 s_proxy_rio1]"))); + assertThat(splitHttpResponse.statusCode(), is(equalTo(200))); + } + + @Test + public void testPostErrors() throws IOException, InterruptedException { + MockWebServer server = new MockWebServer(); + server.enqueue(new MockResponse().setBody("").setResponseCode(HttpURLConnection.HTTP_INTERNAL_ERROR)); + server.start(); + HttpUrl baseUrl = server.url("/v1/"); + URI rootTarget = baseUrl.uri(); + RequestDecorator decorator = new RequestDecorator(null); + OkHttpClient client = new Builder().build(); + + OkHttpModuleImpl okHttpModuleImpl = new OkHttpModuleImpl(client, "qwerty"); + okHttpModuleImpl.setMetaData(metadata()); + okHttpModuleImpl.setRequestDecorator(decorator); + + Map> additionalHeaders = Collections.singletonMap("AdditionalHeader", + Collections.singletonList("add")); + + SplitHttpResponse splitHttpResponse = okHttpModuleImpl.post(rootTarget, + Utils.toJsonEntity("<>"), additionalHeaders); + + + RecordedRequest request = server.takeRequest(); + server.shutdown(); + assertThat(splitHttpResponse.statusCode(), is(equalTo(HttpURLConnection.HTTP_INTERNAL_ERROR))); + okHttpModuleImpl.close(); + } + + @Test(expected = IllegalStateException.class) + public void testPosttException() throws URISyntaxException { + RequestDecorator decorator = null; + URI uri = new URI("https://kubernetesturl.com/split/api/testImpressions/bulk"); + + Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("1.0.0.127", 8080)); + OkHttpClient client = new OkHttpClient.Builder() + .proxy(proxy) + .build(); + OkHttpModuleImpl okHttpModuleImpl = new OkHttpModuleImpl(client, "qwerty"); + okHttpModuleImpl.setMetaData(metadata()); + okHttpModuleImpl.setRequestDecorator(decorator); + SplitHttpResponse splitHttpResponse = splitHtpClientKerberos.post(uri, + Utils.toJsonEntity(Arrays.asList(new String[] { "A", "B", "C", "D" })), null); + } +*/ + private SDKMetadata metadata() { + return new SDKMetadata("java-1.2.3", "1.2.3.4", "someIP"); + } +} diff --git a/http-modules/src/test/java/io/split/httpmodules/okhttp/SplitConfigTests.java b/http-modules/src/test/java/io/split/httpmodules/okhttp/SplitConfigTests.java new file mode 100644 index 000000000..3dca97d28 --- /dev/null +++ b/http-modules/src/test/java/io/split/httpmodules/okhttp/SplitConfigTests.java @@ -0,0 +1,48 @@ +package io.split.httpmodules.okhttp; + +import okhttp3.OkHttpClient; +//import okhttp3.OkHttpClient.Builder; + +public class SplitConfigTests { + /* + @Test + public void checkExpectedAuthScheme() { + OkHttpClient client = new Builder().build(); + OkHttpModuleImpl okHttpModuleImpl = new OkHttpModuleImpl(client, "qwerty"); + + SplitClientConfig cfg = SplitClientConfig.builder() + .proxyAuthScheme(ProxyAuthScheme.KERBEROS) + .proxyKerberosPrincipalName("bilal@bilal") + .proxyKerberosClient(okHttpModuleImpl) + .build(); + Assert.assertEquals(ProxyAuthScheme.KERBEROS, cfg.proxyAuthScheme()); + Assert.assertEquals("bilal@bilal", cfg.proxyKerberosPrincipalName()); + Assert.assertEquals(okHttpModuleImpl, cfg.proxyKerberosClient()); + + cfg = SplitClientConfig.builder() + .build(); + Assert.assertEquals(null, cfg.proxyAuthScheme()); + } + + @Test(expected = IllegalStateException.class) + public void testAuthSchemeWithoutClient() { + SplitClientConfig.builder() + .proxyAuthScheme(ProxyAuthScheme.KERBEROS) + .proxyKerberosPrincipalName("bilal") + .build(); + } + + @Test(expected = IllegalStateException.class) + public void testAuthSchemeWithoutPrincipalName() { + OkHttpClient client = new Builder().build(); + OkHttpModuleImpl okHttpModuleImpl = new OkHttpModuleImpl(client, "qwerty"); + + SplitClientConfig.builder() + .proxyAuthScheme(ProxyAuthScheme.KERBEROS) + .proxyKerberosClient(okHttpModuleImpl) + .build(); + } + + */ + +} diff --git a/http-modules/src/test/java/io/split/httpmodules/okhttp/SplitFactoryTests.java b/http-modules/src/test/java/io/split/httpmodules/okhttp/SplitFactoryTests.java new file mode 100644 index 000000000..0b623368c --- /dev/null +++ b/http-modules/src/test/java/io/split/httpmodules/okhttp/SplitFactoryTests.java @@ -0,0 +1,107 @@ +package io.split.httpmodules.okhttp; + +import io.split.client.SplitFactoryImpl; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import okhttp3.OkHttpClient; +import okhttp3.OkHttpClient.*; +import okhttp3.HttpUrl; +import okhttp3.Headers; + +@RunWith(PowerMockRunner.class) +@PrepareForTest(SplitFactoryImpl.class) +public class SplitFactoryTests { + /* + public static final String ENDPOINT = "https://sdk.split-stage.io"; + + @Test + public void testBuildKerberosClientParams() throws URISyntaxException, IOException { + PowerMockito.mockStatic(SplitFactoryImpl.class); + PowerMockito.mockStatic(OkHttpModule.class); + + ArgumentCaptor proxyCaptor = ArgumentCaptor.forClass(Proxy.class); + ArgumentCaptor configCaptor = ArgumentCaptor.forClass(SplitClientConfig.class); + ArgumentCaptor< HttpLoggingInterceptor> logCaptor = ArgumentCaptor.forClass( HttpLoggingInterceptor.class); + ArgumentCaptor authCaptor = ArgumentCaptor.forClass(Authenticator.class); + + Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(ENDPOINT, 6060)); + OkHttpClient client = OkHttpModule.buildOkHttpClient(proxy, "bilal@localhost", true, 0, 0) + OkHttpModuleImpl okHttpModuleImpl = new OkHttpModuleImpl(client, "qwerty"); + + SplitClientConfig splitClientConfig = SplitClientConfig.builder() + .setBlockUntilReadyTimeout(10000) + .proxyAuthScheme(ProxyAuthScheme.KERBEROS) + .proxyKerberosPrincipalName("bilal@localhost") + .proxyKerberosClient(okHttpModuleImpl) + .build(); + + Map kerberosOptions = new HashMap(); + kerberosOptions.put("com.sun.security.auth.module.Krb5LoginModule", "required"); + kerberosOptions.put("refreshKrb5Config", "false"); + kerberosOptions.put("doNotPrompt", "false"); + kerberosOptions.put("useTicketCache", "true"); + BDDMockito.given(OkHttpModule.getProxyAuthenticator("bilal@localhost", kerberosOptions)) + .willReturn(null); + + RequestDecorator requestDecorator = new RequestDecorator(null); + SDKMetadata sdkmeta = new SDKMetadata("java-1.2.3", "1.2.3.4", "someIP"); + + PowerMockito.verifyStatic(); + SplitFactoryImpl.buildOkHttpClient(proxyCaptor.capture(), configCaptor.capture(),logCaptor.capture(), authCaptor.capture()); + + Assert.assertEquals("HTTP @ https://sdk.split-stage.io:6060", proxyCaptor.getValue().toString()); + Assert.assertTrue(logCaptor.getValue() instanceof okhttp3.logging.HttpLoggingInterceptor); + } + + @Test + public void testFactoryKerberosInstance() throws URISyntaxException, IOException { + OkHttpClient okHttpClient = new OkHttpClient.Builder().build(); + PowerMockito.stub(PowerMockito.method(OkHttpModule.class, "buildOkHttpClient")).toReturn(okHttpClient); + PowerMockito.stub(PowerMockito.method(OkHttpModule.class, "getProxyAuthenticator")).toReturn(null); + + SplitClientConfig splitClientConfig = SplitClientConfig.builder() + .setBlockUntilReadyTimeout(10000) + .proxyAuthScheme(ProxyAuthScheme.KERBEROS) + .proxyKerberosPrincipalName("bilal@localhost") + .proxyPort(6060) + .proxyHost(ENDPOINT) + .build(); + + Map kerberosOptions = new HashMap(); + kerberosOptions.put("com.sun.security.auth.module.Krb5LoginModule", "required"); + kerberosOptions.put("refreshKrb5Config", "false"); + kerberosOptions.put("doNotPrompt", "false"); + kerberosOptions.put("useTicketCache", "true"); + + RequestDecorator requestDecorator = new RequestDecorator(null); + SDKMetadata sdkmeta = new SDKMetadata("java-1.2.3", "1.2.3.4", "someIP"); + SplitHttpClient splitHttpClient = SplitFactoryImpl.buildSplitHttpClient("qwer", + splitClientConfig, + sdkmeta, + requestDecorator); + Assert.assertTrue(splitHttpClient instanceof OkHttpModuleImpl); + } + + @Test + public void testBuildOkHttpClient() { + SplitClientConfig splitClientConfig = SplitClientConfig.builder() + .setBlockUntilReadyTimeout(10000) + .proxyAuthScheme(ProxyAuthScheme.KERBEROS) + .proxyKerberosPrincipalName("bilal@localhost") + .proxyPort(6060) + .proxyHost(ENDPOINT) + .build(); + HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); + Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("host", 8080)); + OkHttpClient okHttpClient = SplitFactoryImpl.buildOkHttpClient(proxy, + splitClientConfig, loggingInterceptor, Authenticator.NONE); + assertEquals(Authenticator.NONE, okHttpClient.authenticator()); + assertEquals(proxy, okHttpClient.proxy()); + assertEquals(loggingInterceptor, okHttpClient.interceptors().get(0)); + } + + */ + +} diff --git a/http-modules/src/test/resources/krb5.conf b/http-modules/src/test/resources/krb5.conf new file mode 100644 index 000000000..78d63ba8f --- /dev/null +++ b/http-modules/src/test/resources/krb5.conf @@ -0,0 +1,37 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +[libdefaults] + kdc_realm = ATHENA.MIT.EDU + default_realm = ATHENA.MIT.EDU + kdc_tcp_port = 88 + kdc_udp_port = 88 + dns_lookup_realm = false + dns_lookup_kdc = false + udp_preference_limit = 1 + +[logging] + default = FILE:/var/logs/krb5kdc.log + +[realms] + ATHENA.MIT.EDU = { +# kdc = 10.12.4.76:88 +# kdc = tcp/10.12.4.76:88 +# kdc = tcp/192.168.1.19:88 + kdc = 192.168.1.19:88 + } \ No newline at end of file diff --git a/http-modules/src/test/resources/org/powermock/extensions/configuration.properties b/http-modules/src/test/resources/org/powermock/extensions/configuration.properties new file mode 100644 index 000000000..a8ebaeba3 --- /dev/null +++ b/http-modules/src/test/resources/org/powermock/extensions/configuration.properties @@ -0,0 +1 @@ +powermock.global-ignore=jdk.internal.reflect.*,javax.net.ssl.* \ No newline at end of file diff --git a/kerberos/src/main/java/io/split/kerberos/SplitHttpClientKerberosBuilder.java b/kerberos/src/main/java/io/split/kerberos/SplitHttpClientKerberosBuilder.java deleted file mode 100644 index 11283e3dd..000000000 --- a/kerberos/src/main/java/io/split/kerberos/SplitHttpClientKerberosBuilder.java +++ /dev/null @@ -1,56 +0,0 @@ -package io.split.kerberos; - -import java.io.IOException; -import java.net.Proxy; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import okhttp3.Authenticator; -import okhttp3.OkHttpClient; -import okhttp3.OkHttpClient.Builder; -import okhttp3.logging.HttpLoggingInterceptor; - -public class SplitHttpClientKerberosBuilder { - private static final int DEFAULT_CONNECTION_TIMEOUT = 10000; - private static final int DEFAULT_READ_TIMEOUT = 10000; - - public static OkHttpClient buildOkHttpClient(Proxy proxy, String proxyKerberosPrincipalName, - boolean debugEnabled, int readTimeout, int connectionTimeout) throws IOException { - - HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); - if (debugEnabled) { - logging.setLevel(HttpLoggingInterceptor.Level.HEADERS); - } else { - logging.setLevel(HttpLoggingInterceptor.Level.NONE); - } - - if (connectionTimeout <= 0 || connectionTimeout > DEFAULT_CONNECTION_TIMEOUT) { - connectionTimeout = DEFAULT_CONNECTION_TIMEOUT; - } - if (readTimeout <= 0 || readTimeout > DEFAULT_READ_TIMEOUT) { - readTimeout = DEFAULT_READ_TIMEOUT; - } - - Map kerberosOptions = new HashMap<>(); - kerberosOptions.put("com.sun.security.auth.module.Krb5LoginModule", "required"); - kerberosOptions.put("refreshKrb5Config", "false"); - kerberosOptions.put("doNotPrompt", "false"); - kerberosOptions.put("useTicketCache", "true"); - - Authenticator proxyAuthenticator = getProxyAuthenticator(proxyKerberosPrincipalName, kerberosOptions); - - return new Builder() - .proxy(proxy) - .readTimeout(readTimeout, TimeUnit.MILLISECONDS) - .connectTimeout(connectionTimeout, TimeUnit.MILLISECONDS) - .addInterceptor(logging) - .proxyAuthenticator(proxyAuthenticator) - .build(); - } - - public static HTTPKerberosAuthInterceptor getProxyAuthenticator(String proxyKerberosPrincipalName, - Map kerberosOptions) throws IOException { - return new HTTPKerberosAuthInterceptor(proxyKerberosPrincipalName, kerberosOptions); - } -} diff --git a/kerberos/src/main/java/io/split/kerberos/SplitHttpClientKerberosImpl.java b/kerberos/src/main/java/io/split/kerberos/SplitHttpClientKerberosImpl.java deleted file mode 100644 index c29a84cbd..000000000 --- a/kerberos/src/main/java/io/split/kerberos/SplitHttpClientKerberosImpl.java +++ /dev/null @@ -1,196 +0,0 @@ -package io.split.kerberos; - -import io.split.client.RequestDecorator; -import io.split.client.dtos.SplitHttpResponse; -import io.split.client.utils.SDKMetadata; -import io.split.engine.common.FetchOptions; -import io.split.service.SplitHttpClient; - -import split.org.apache.hc.client5.http.classic.methods.HttpGet; -import split.org.apache.hc.core5.http.Header; -import split.org.apache.hc.core5.http.HttpEntity; -import split.org.apache.hc.core5.http.HttpRequest; -import split.org.apache.hc.core5.http.io.entity.EntityUtils; -import split.org.apache.hc.core5.http.message.BasicHeader; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import okhttp3.Request.Builder; -import okhttp3.RequestBody; - -import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class SplitHttpClientKerberosImpl implements SplitHttpClient { - - private static final Logger _log = LoggerFactory.getLogger(SplitHttpClientKerberosImpl.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 RequestDecorator _requestDecorator; - private final String _apikey; - private SDKMetadata _metadata; - private final OkHttpClient _client; - - public static SplitHttpClientKerberosImpl create(OkHttpClient client, - String apikey) { - return new SplitHttpClientKerberosImpl(client, apikey); - } - - SplitHttpClientKerberosImpl(OkHttpClient client, - String apikey) { - _apikey = apikey; - _client = client; - } - - @Override - public void setMetaData(SDKMetadata metadata) { - _metadata = metadata; - } - - @Override - public void setRequestDecorator(RequestDecorator requestDecorator) { - _requestDecorator = requestDecorator; - } - - @Override - public SplitHttpResponse get(URI uri, FetchOptions options, Map> additionalHeaders) { - try { - Builder requestBuilder = new Builder(); - requestBuilder.url(uri.toString()); - setBasicHeaders(requestBuilder); - setAdditionalAndDecoratedHeaders(requestBuilder, additionalHeaders); - if (options.cacheControlHeadersEnabled()) { - requestBuilder.addHeader(HEADER_CACHE_CONTROL_NAME, HEADER_CACHE_CONTROL_VALUE); - } - - Request request = requestBuilder.build(); - _log.debug(String.format("Request Headers: %s", request.headers())); - - Response response = _client.newCall(request).execute(); - - int responseCode = response.code(); - - _log.debug(String.format("[GET] %s. Status code: %s", - request.url().toString(), - responseCode)); - - String statusMessage = ""; - if (responseCode < HttpURLConnection.HTTP_OK || responseCode >= HttpURLConnection.HTTP_MULT_CHOICE) { - _log.warn(String.format("Response status was: %s. Reason: %s", responseCode, - response.message())); - statusMessage = response.message(); - } - - String responseBody = response.body().string(); - response.close(); - - return new SplitHttpResponse(responseCode, - statusMessage, - responseBody, - getResponseHeaders(response)); - } catch (Exception e) { - throw new IllegalStateException(String.format("Problem in http get operation: %s", e), e); - } - } - - @Override - public SplitHttpResponse post(URI url, HttpEntity entity, - Map> additionalHeaders) { - try { - Builder requestBuilder = getRequestBuilder(); - requestBuilder.url(url.toString()); - setBasicHeaders(requestBuilder); - setAdditionalAndDecoratedHeaders(requestBuilder, additionalHeaders); - requestBuilder.addHeader("Accept-Encoding", "gzip"); - requestBuilder.addHeader("Content-Type", "application/json"); - String post = EntityUtils.toString((HttpEntity) entity); - RequestBody postBody = RequestBody.create(post.getBytes()); - requestBuilder.post(postBody); - - Request request = getRequest(requestBuilder); - _log.debug(String.format("Request Headers: %s", request.headers())); - - Response response = _client.newCall(request).execute(); - - int responseCode = response.code(); - - _log.debug(String.format("[GET] %s. Status code: %s", - request.url().toString(), - responseCode)); - - String statusMessage = ""; - if (responseCode < HttpURLConnection.HTTP_OK || responseCode >= HttpURLConnection.HTTP_MULT_CHOICE) { - _log.warn(String.format("Response status was: %s. Reason: %s", responseCode, - response.message())); - statusMessage = response.message(); - } - response.close(); - - return new SplitHttpResponse(responseCode, statusMessage, "", getResponseHeaders(response)); - } catch (Exception e) { - throw new IllegalStateException(String.format("Problem in http post operation: %s", e), e); - } - } - - protected Builder getRequestBuilder() { - return new Builder(); - } - - protected Request getRequest(Builder requestBuilder) { - return requestBuilder.build(); - } - protected void setBasicHeaders(Builder requestBuilder) { - requestBuilder.addHeader(HEADER_API_KEY, "Bearer " + _apikey); - requestBuilder.addHeader(HEADER_CLIENT_VERSION, _metadata.getSdkVersion()); - requestBuilder.addHeader(HEADER_CLIENT_MACHINE_IP, _metadata.getMachineIp()); - requestBuilder.addHeader(HEADER_CLIENT_MACHINE_NAME, _metadata.getMachineName()); - requestBuilder.addHeader(HEADER_CLIENT_KEY, _apikey.length() > 4 - ? _apikey.substring(_apikey.length() - 4) - : _apikey); - } - - protected void setAdditionalAndDecoratedHeaders(Builder requestBuilder, Map> additionalHeaders) { - if (additionalHeaders != null) { - for (Map.Entry> entry : additionalHeaders.entrySet()) { - for (String value : entry.getValue()) { - requestBuilder.addHeader(entry.getKey(), value); - } - } - } - HttpRequest request = new HttpGet(""); - _requestDecorator.decorateHeaders(request); - for (Header header : request.getHeaders()) { - requestBuilder.addHeader(header.getName(), header.getValue()); - } - } - - protected Header[] getResponseHeaders(Response response) { - List responseHeaders = new ArrayList<>(); - Map> map = response.headers().toMultimap(); - for (Map.Entry> entry : map.entrySet()) { - if (entry.getKey() != null) { - BasicHeader responseHeader = new BasicHeader(entry.getKey(), entry.getValue()); - responseHeaders.add(responseHeader); - } - } - return responseHeaders.toArray(new split.org.apache.hc.core5.http.Header[0]); - } - @Override - public void close() throws IOException { - _client.dispatcher().executorService().shutdown(); - } -} diff --git a/pom.xml b/pom.xml index 3f899b8ee..d524fd74e 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ redis-wrapper testing client - kerberos + http-modules From bb877662681fe02c5ee6f6e5518bc2cace870979 Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Tue, 10 Sep 2024 19:12:57 -0700 Subject: [PATCH 3/4] separated httpclient from module --- .../io/split/client/SplitClientConfig.java | 12 +- .../io/split/client/SplitFactoryImpl.java | 15 +- .../io/split/service/CustomHttpModule.java | 13 + .../io/split/service/SplitHttpClient.java | 8 - .../io/split/service/SplitHttpClientImpl.java | 15 -- .../httpmodules/okhttp/OkHttpClientImpl.java | 211 ++++++++++++++++ .../httpmodules/okhttp/OkHttpModule.java | 235 ++---------------- 7 files changed, 248 insertions(+), 261 deletions(-) create mode 100644 client/src/main/java/io/split/service/CustomHttpModule.java create mode 100644 http-modules/src/main/java/io/split/httpmodules/okhttp/OkHttpClientImpl.java diff --git a/client/src/main/java/io/split/client/SplitClientConfig.java b/client/src/main/java/io/split/client/SplitClientConfig.java index faf243de6..2a4a70c3f 100644 --- a/client/src/main/java/io/split/client/SplitClientConfig.java +++ b/client/src/main/java/io/split/client/SplitClientConfig.java @@ -4,7 +4,7 @@ import io.split.client.impressions.ImpressionsManager; import io.split.client.utils.FileTypeEnum; import io.split.integrations.IntegrationsConfig; -import io.split.service.SplitHttpClient; +import io.split.service.CustomHttpModule; import io.split.storages.enums.OperationMode; import io.split.storages.enums.StorageMode; import org.apache.hc.core5.http.HttpHost; @@ -92,7 +92,7 @@ public class SplitClientConfig { private final HashSet _flagSetsFilter; private final int _invalidSets; private final CustomHeaderDecorator _customHeaderDecorator; - private final SplitHttpClient _alternativeHTTPModule; + private final CustomHttpModule _alternativeHTTPModule; public static Builder builder() { return new Builder(); @@ -150,7 +150,7 @@ private SplitClientConfig(String endpoint, HashSet flagSetsFilter, int invalidSets, CustomHeaderDecorator customHeaderDecorator, - SplitHttpClient alternativeHTTPModule) { + CustomHttpModule alternativeHTTPModule) { _endpoint = endpoint; _eventsEndpoint = eventsEndpoint; _featuresRefreshRate = pollForFeatureChangesEveryNSeconds; @@ -412,7 +412,7 @@ public CustomHeaderDecorator customHeaderDecorator() { return _customHeaderDecorator; } - public SplitHttpClient alternativeHTTPModule() { return _alternativeHTTPModule; } + public CustomHttpModule alternativeHTTPModule() { return _alternativeHTTPModule; } public static final class Builder { private String _endpoint = SDK_ENDPOINT; @@ -470,7 +470,7 @@ public static final class Builder { private HashSet _flagSetsFilter = new HashSet<>(); private int _invalidSetsCount = 0; private CustomHeaderDecorator _customHeaderDecorator = null; - private SplitHttpClient _alternativeHTTPModule = null; + private CustomHttpModule _alternativeHTTPModule = null; public Builder() { } @@ -971,7 +971,7 @@ public Builder customHeaderDecorator(CustomHeaderDecorator customHeaderDecorator * @param alternativeHTTPModule * @return this builder */ - public Builder alternativeHTTPModule(SplitHttpClient alternativeHTTPModule) { + public Builder alternativeHTTPModule(CustomHttpModule alternativeHTTPModule) { _alternativeHTTPModule = alternativeHTTPModule; return this; } diff --git a/client/src/main/java/io/split/client/SplitFactoryImpl.java b/client/src/main/java/io/split/client/SplitFactoryImpl.java index 41c397b6d..7d8a0aa51 100644 --- a/client/src/main/java/io/split/client/SplitFactoryImpl.java +++ b/client/src/main/java/io/split/client/SplitFactoryImpl.java @@ -165,7 +165,7 @@ public class SplitFactoryImpl implements SplitFactory { private RequestDecorator _requestDecorator; // Constructor for standalone mode - public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyntaxException { + public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyntaxException, IOException { _userStorageWrapper = null; _operationMode = config.operationMode(); _startTime = System.currentTimeMillis(); @@ -193,10 +193,7 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn if (config.alternativeHTTPModule() == null) { _splitHttpClient = buildSplitHttpClient(apiToken, config, _sdkMetadata, _requestDecorator); } else { - _splitHttpClient = config.alternativeHTTPModule(); - _splitHttpClient.setMetaData(_sdkMetadata); - _splitHttpClient.setRequestDecorator(_requestDecorator); - _splitHttpClient.setApiKey(apiToken); + _splitHttpClient = config.alternativeHTTPModule().createClient(apiToken, _sdkMetadata, _requestDecorator); } // Roots @@ -284,14 +281,6 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn } } - public RequestDecorator getRequestDecorator() { - return _requestDecorator; - } - - public SDKMetadata getSDKMetaData() { - return _sdkMetadata; - } - // Constructor for consumer mode protected SplitFactoryImpl(String apiToken, SplitClientConfig config, CustomStorageWrapper customStorageWrapper) throws URISyntaxException { diff --git a/client/src/main/java/io/split/service/CustomHttpModule.java b/client/src/main/java/io/split/service/CustomHttpModule.java new file mode 100644 index 000000000..837f42149 --- /dev/null +++ b/client/src/main/java/io/split/service/CustomHttpModule.java @@ -0,0 +1,13 @@ +package io.split.service; + +import io.split.client.RequestDecorator; +import io.split.client.utils.SDKMetadata; +import io.split.service.SplitHttpClient; + +import java.io.IOException; + +public interface CustomHttpModule { + public SplitHttpClient createClient(String apiToken, SDKMetadata sdkMetadata, RequestDecorator requestDecorator) throws IOException; + + +} diff --git a/client/src/main/java/io/split/service/SplitHttpClient.java b/client/src/main/java/io/split/service/SplitHttpClient.java index eba444ec6..7105d16b0 100644 --- a/client/src/main/java/io/split/service/SplitHttpClient.java +++ b/client/src/main/java/io/split/service/SplitHttpClient.java @@ -1,7 +1,5 @@ package io.split.service; -import io.split.client.RequestDecorator; -import io.split.client.utils.SDKMetadata; import io.split.engine.common.FetchOptions; import io.split.client.dtos.SplitHttpResponse; @@ -34,10 +32,4 @@ public interface SplitHttpClient extends Closeable { public SplitHttpResponse post(URI uri, HttpEntity entity, Map> additionalHeaders) throws IOException; - - public void setMetaData(SDKMetadata metadata); - - public void setRequestDecorator(RequestDecorator requestDecorator); - - public void setApiKey(String apiKey); } \ No newline at end of file diff --git a/client/src/main/java/io/split/service/SplitHttpClientImpl.java b/client/src/main/java/io/split/service/SplitHttpClientImpl.java index 0bdba8bc6..64ca3a55c 100644 --- a/client/src/main/java/io/split/service/SplitHttpClientImpl.java +++ b/client/src/main/java/io/split/service/SplitHttpClientImpl.java @@ -145,19 +145,4 @@ private void setBasicHeaders(HttpRequest request) { public void close() throws IOException { _client.close(); } - - @Override - public void setMetaData(SDKMetadata metadata) { - // only implemented for Kerberos client - } - - @Override - public void setRequestDecorator(RequestDecorator requestDecorator) { - // only implemented for Kerberos client - } - @Override - public void setApiKey(String apiKey) { - // only implemented for Kerberos client - } - } diff --git a/http-modules/src/main/java/io/split/httpmodules/okhttp/OkHttpClientImpl.java b/http-modules/src/main/java/io/split/httpmodules/okhttp/OkHttpClientImpl.java new file mode 100644 index 000000000..fe1ad1dc0 --- /dev/null +++ b/http-modules/src/main/java/io/split/httpmodules/okhttp/OkHttpClientImpl.java @@ -0,0 +1,211 @@ +package io.split.httpmodules.okhttp; + +import io.split.client.RequestDecorator; +import io.split.client.dtos.SplitHttpResponse; +import io.split.client.utils.SDKMetadata; +import io.split.engine.common.FetchOptions; +import io.split.service.SplitHttpClient; + +import okhttp3.*; +import okhttp3.OkHttpClient.Builder; +import okhttp3.Request.*; +import okhttp3.logging.HttpLoggingInterceptor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import split.org.apache.hc.client5.http.classic.methods.HttpGet; +import split.org.apache.hc.core5.http.Header; +import split.org.apache.hc.core5.http.HttpEntity; +import split.org.apache.hc.core5.http.HttpRequest; +import split.org.apache.hc.core5.http.io.entity.EntityUtils; +import split.org.apache.hc.core5.http.message.BasicHeader; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.Proxy; +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +public class OkHttpClientImpl implements SplitHttpClient { + public final OkHttpClient httpClient; + private static final Logger _log = LoggerFactory.getLogger(OkHttpClientImpl.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 RequestDecorator _requestDecorator; + private String _apikey; + private SDKMetadata _metadata; + + public OkHttpClientImpl(String apiToken, SDKMetadata sdkMetadata, RequestDecorator requestDecorator, + Proxy proxy, String proxyAuthKerberosPrincipalName, boolean debugEnabled, + int readTimeout, int connectionTimeout) throws IOException { + _apikey = apiToken; + _metadata = sdkMetadata; + _requestDecorator = requestDecorator; + + HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); + if (debugEnabled) { + logging.setLevel(HttpLoggingInterceptor.Level.HEADERS); + } else { + logging.setLevel(HttpLoggingInterceptor.Level.NONE); + } + + Map kerberosOptions = new HashMap<>(); + kerberosOptions.put("com.sun.security.auth.module.Krb5LoginModule", "required"); + kerberosOptions.put("refreshKrb5Config", "false"); + kerberosOptions.put("doNotPrompt", "false"); + kerberosOptions.put("useTicketCache", "true"); + + Authenticator proxyAuthenticator = getProxyAuthenticator(proxyAuthKerberosPrincipalName, kerberosOptions); + + httpClient = new okhttp3.OkHttpClient.Builder() + .proxy(proxy) + .readTimeout(readTimeout, TimeUnit.MILLISECONDS) + .connectTimeout(connectionTimeout, TimeUnit.MILLISECONDS) + .addInterceptor(logging) + .proxyAuthenticator(proxyAuthenticator) + .build(); + } + + public HTTPKerberosAuthInterceptor getProxyAuthenticator(String proxyKerberosPrincipalName, + Map kerberosOptions) throws IOException { + return new HTTPKerberosAuthInterceptor(proxyKerberosPrincipalName, kerberosOptions); + } + + @Override + public SplitHttpResponse get(URI uri, FetchOptions options, Map> additionalHeaders) { + try { + okhttp3.Request.Builder requestBuilder = new okhttp3.Request.Builder(); + requestBuilder.url(uri.toString()); + setBasicHeaders(requestBuilder); + setAdditionalAndDecoratedHeaders(requestBuilder, additionalHeaders); + if (options.cacheControlHeadersEnabled()) { + requestBuilder.addHeader(HEADER_CACHE_CONTROL_NAME, HEADER_CACHE_CONTROL_VALUE); + } + + Request request = requestBuilder.build(); + _log.debug(String.format("Request Headers: %s", request.headers())); + + Response response = httpClient.newCall(request).execute(); + + int responseCode = response.code(); + + _log.debug(String.format("[GET] %s. Status code: %s", + request.url().toString(), + responseCode)); + + String statusMessage = ""; + if (responseCode < HttpURLConnection.HTTP_OK || responseCode >= HttpURLConnection.HTTP_MULT_CHOICE) { + _log.warn(String.format("Response status was: %s. Reason: %s", responseCode, + response.message())); + statusMessage = response.message(); + } + + String responseBody = response.body().string(); + response.close(); + + return new SplitHttpResponse(responseCode, + statusMessage, + responseBody, + getResponseHeaders(response)); + } catch (Exception e) { + throw new IllegalStateException(String.format("Problem in http get operation: %s", e), e); + } + } + + @Override + public SplitHttpResponse post(URI url, HttpEntity entity, + Map> additionalHeaders) { + try { + okhttp3.Request.Builder requestBuilder = getRequestBuilder(); + requestBuilder.url(url.toString()); + setBasicHeaders(requestBuilder); + setAdditionalAndDecoratedHeaders(requestBuilder, additionalHeaders); + requestBuilder.addHeader("Accept-Encoding", "gzip"); + requestBuilder.addHeader("Content-Type", "application/json"); + String post = EntityUtils.toString((HttpEntity) entity); + RequestBody postBody = RequestBody.create(post.getBytes()); + requestBuilder.post(postBody); + + Request request = getRequest(requestBuilder); + _log.debug(String.format("Request Headers: %s", request.headers())); + + Response response = httpClient.newCall(request).execute(); + + int responseCode = response.code(); + + _log.debug(String.format("[GET] %s. Status code: %s", + request.url().toString(), + responseCode)); + + String statusMessage = ""; + if (responseCode < HttpURLConnection.HTTP_OK || responseCode >= HttpURLConnection.HTTP_MULT_CHOICE) { + _log.warn(String.format("Response status was: %s. Reason: %s", responseCode, + response.message())); + statusMessage = response.message(); + } + response.close(); + + return new SplitHttpResponse(responseCode, statusMessage, "", getResponseHeaders(response)); + } catch (Exception e) { + throw new IllegalStateException(String.format("Problem in http post operation: %s", e), e); + } + } + + protected okhttp3.Request.Builder getRequestBuilder() { + return new okhttp3.Request.Builder(); + } + + protected Request getRequest(okhttp3.Request.Builder requestBuilder) { + return requestBuilder.build(); + } + protected void setBasicHeaders(okhttp3.Request.Builder requestBuilder) { + requestBuilder.addHeader(HEADER_API_KEY, "Bearer " + _apikey); + requestBuilder.addHeader(HEADER_CLIENT_VERSION, _metadata.getSdkVersion()); + requestBuilder.addHeader(HEADER_CLIENT_MACHINE_IP, _metadata.getMachineIp()); + requestBuilder.addHeader(HEADER_CLIENT_MACHINE_NAME, _metadata.getMachineName()); + requestBuilder.addHeader(HEADER_CLIENT_KEY, _apikey.length() > 4 + ? _apikey.substring(_apikey.length() - 4) + : _apikey); + } + + protected void setAdditionalAndDecoratedHeaders(okhttp3.Request.Builder requestBuilder, Map> additionalHeaders) { + if (additionalHeaders != null) { + for (Map.Entry> entry : additionalHeaders.entrySet()) { + for (String value : entry.getValue()) { + requestBuilder.addHeader(entry.getKey(), value); + } + } + } + HttpRequest request = new HttpGet(""); + _requestDecorator.decorateHeaders(request); + for (Header header : request.getHeaders()) { + requestBuilder.addHeader(header.getName(), header.getValue()); + } + } + + protected Header[] getResponseHeaders(Response response) { + List responseHeaders = new ArrayList<>(); + Map> map = response.headers().toMultimap(); + for (Map.Entry> entry : map.entrySet()) { + if (entry.getKey() != null) { + BasicHeader responseHeader = new BasicHeader(entry.getKey(), entry.getValue()); + responseHeaders.add(responseHeader); + } + } + return responseHeaders.toArray(new split.org.apache.hc.core5.http.Header[0]); + } + @Override + public void close() throws IOException { + httpClient.dispatcher().executorService().shutdown(); + } + +} diff --git a/http-modules/src/main/java/io/split/httpmodules/okhttp/OkHttpModule.java b/http-modules/src/main/java/io/split/httpmodules/okhttp/OkHttpModule.java index f344351ed..7480f1014 100644 --- a/http-modules/src/main/java/io/split/httpmodules/okhttp/OkHttpModule.java +++ b/http-modules/src/main/java/io/split/httpmodules/okhttp/OkHttpModule.java @@ -1,42 +1,17 @@ package io.split.httpmodules.okhttp; import java.io.IOException; -import java.net.HttpURLConnection; import java.net.InetSocketAddress; import java.net.Proxy; -import java.net.URI; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; import io.split.client.RequestDecorator; -import io.split.client.dtos.SplitHttpResponse; import io.split.client.utils.SDKMetadata; -import io.split.engine.common.FetchOptions; -import io.split.service.SplitHttpClient; - -import split.org.apache.hc.client5.http.classic.methods.HttpGet; -import split.org.apache.hc.core5.http.Header; -import split.org.apache.hc.core5.http.HttpEntity; -import split.org.apache.hc.core5.http.HttpRequest; -import split.org.apache.hc.core5.http.io.entity.EntityUtils; -import split.org.apache.hc.core5.http.message.BasicHeader; - -import okhttp3.Authenticator; -import okhttp3.OkHttpClient; -import okhttp3.OkHttpClient.Builder; -import okhttp3.logging.HttpLoggingInterceptor; -import okhttp3.Request; -import okhttp3.Response; -import okhttp3.Request.*; -import okhttp3.RequestBody; +import io.split.service.CustomHttpModule; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class OkHttpModule implements SplitHttpClient { +public class OkHttpModule implements CustomHttpModule { private static final int DEFAULT_CONNECTION_TIMEOUT = 10000; private static final int DEFAULT_READ_TIMEOUT = 10000; private final boolean _debugEnabled; @@ -45,18 +20,7 @@ public class OkHttpModule implements SplitHttpClient { private final Proxy _proxy; private final ProxyAuthScheme _proxyAuthScheme; private final String _proxyAuthKerberosPrincipalName; - public final OkHttpClient httpClient; private static final Logger _log = LoggerFactory.getLogger(OkHttpModule.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 RequestDecorator _requestDecorator; - private String _apikey; - private SDKMetadata _metadata; public static Builder builder() { return new Builder(); @@ -67,40 +31,22 @@ private OkHttpModule(ProxyAuthScheme proxyAuthScheme, Proxy proxy, int connectionTimeout, int readTimeout, - boolean debugEnabled) throws IOException { + boolean debugEnabled) { _proxyAuthScheme = proxyAuthScheme; _proxyAuthKerberosPrincipalName = proxyAuthKerberosPrincipalName; _proxy = proxy; _connectionTimeout = connectionTimeout; _readTimeout = readTimeout; _debugEnabled = debugEnabled; - - HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); - if (_debugEnabled) { - logging.setLevel(HttpLoggingInterceptor.Level.HEADERS); - } else { - logging.setLevel(HttpLoggingInterceptor.Level.NONE); - } - - Map kerberosOptions = new HashMap<>(); - kerberosOptions.put("com.sun.security.auth.module.Krb5LoginModule", "required"); - kerberosOptions.put("refreshKrb5Config", "false"); - kerberosOptions.put("doNotPrompt", "false"); - kerberosOptions.put("useTicketCache", "true"); - - Authenticator proxyAuthenticator = getProxyAuthenticator(_proxyAuthKerberosPrincipalName, kerberosOptions); - httpClient = new okhttp3.OkHttpClient.Builder() - .proxy(_proxy) - .readTimeout(_readTimeout, TimeUnit.MILLISECONDS) - .connectTimeout(_connectionTimeout, TimeUnit.MILLISECONDS) - .addInterceptor(logging) - .proxyAuthenticator(proxyAuthenticator) - .build(); } - public OkHttpClient httpClient() { - return httpClient; + @Override + public OkHttpClientImpl createClient(String apiToken, SDKMetadata sdkMetadata, RequestDecorator requestDecorator) throws IOException { + return new OkHttpClientImpl(apiToken, sdkMetadata, requestDecorator, + _proxy, _proxyAuthKerberosPrincipalName, _debugEnabled, + _readTimeout, _connectionTimeout); } + public Proxy proxy() { return _proxy; } @@ -124,7 +70,7 @@ public static final class Builder { private String _proxyHost = "localhost"; private int _proxyPort = -1; private ProxyAuthScheme _proxyAuthScheme = null; - private String _proxyKerberosPrincipalName = null; + private String _proxyAuthKerberosPrincipalName = null; private boolean _debugEnabled = false; public Builder() { @@ -179,11 +125,11 @@ public Builder proxyAuthScheme(ProxyAuthScheme proxyAuthScheme) { /** * Kerberos Principal Account Name * - * @param proxyKerberosPrincipalName + * @param proxyAuthKerberosPrincipalName * @return this builder */ - public Builder proxyKerberosPrincipalName(String proxyKerberosPrincipalName) { - _proxyKerberosPrincipalName = proxyKerberosPrincipalName; + public Builder proxyAuthKerberosPrincipalName(String proxyAuthKerberosPrincipalName) { + _proxyAuthKerberosPrincipalName = proxyAuthKerberosPrincipalName; return this; } @@ -192,7 +138,7 @@ private void verifyAuthScheme() { if (proxy() == null) { throw new IllegalStateException("Kerberos mode require Proxy parameters."); } - if (_proxyKerberosPrincipalName == null) { + if (_proxyAuthKerberosPrincipalName == null) { throw new IllegalStateException("Kerberos mode require Kerberos Principal Name."); } } @@ -207,166 +153,17 @@ private void verifyTimeouts() { } } - public OkHttpModule build() throws IOException { + public OkHttpModule build() { verifyTimeouts(); verifyAuthScheme(); return new OkHttpModule( _proxyAuthScheme, - _proxyKerberosPrincipalName, + _proxyAuthKerberosPrincipalName, proxy(), _connectionTimeout, _readTimeout, _debugEnabled); } } - - public HTTPKerberosAuthInterceptor getProxyAuthenticator(String proxyKerberosPrincipalName, - Map kerberosOptions) throws IOException { - return new HTTPKerberosAuthInterceptor(proxyKerberosPrincipalName, kerberosOptions); - } - - @Override - public void setApiKey(String apikey) { - _apikey = apikey; - } - - @Override - public void setMetaData(SDKMetadata metadata) { - _metadata = metadata; - } - - @Override - public void setRequestDecorator(RequestDecorator requestDecorator) { - _requestDecorator = requestDecorator; - } - - @Override - public SplitHttpResponse get(URI uri, FetchOptions options, Map> additionalHeaders) { - try { - okhttp3.Request.Builder requestBuilder = new okhttp3.Request.Builder(); - requestBuilder.url(uri.toString()); - setBasicHeaders(requestBuilder); - setAdditionalAndDecoratedHeaders(requestBuilder, additionalHeaders); - if (options.cacheControlHeadersEnabled()) { - requestBuilder.addHeader(HEADER_CACHE_CONTROL_NAME, HEADER_CACHE_CONTROL_VALUE); - } - - Request request = requestBuilder.build(); - _log.debug(String.format("Request Headers: %s", request.headers())); - - Response response = httpClient.newCall(request).execute(); - - int responseCode = response.code(); - - _log.debug(String.format("[GET] %s. Status code: %s", - request.url().toString(), - responseCode)); - - String statusMessage = ""; - if (responseCode < HttpURLConnection.HTTP_OK || responseCode >= HttpURLConnection.HTTP_MULT_CHOICE) { - _log.warn(String.format("Response status was: %s. Reason: %s", responseCode, - response.message())); - statusMessage = response.message(); - } - - String responseBody = response.body().string(); - response.close(); - - return new SplitHttpResponse(responseCode, - statusMessage, - responseBody, - getResponseHeaders(response)); - } catch (Exception e) { - throw new IllegalStateException(String.format("Problem in http get operation: %s", e), e); - } - } - - @Override - public SplitHttpResponse post(URI url, HttpEntity entity, - Map> additionalHeaders) { - try { - okhttp3.Request.Builder requestBuilder = getRequestBuilder(); - requestBuilder.url(url.toString()); - setBasicHeaders(requestBuilder); - setAdditionalAndDecoratedHeaders(requestBuilder, additionalHeaders); - requestBuilder.addHeader("Accept-Encoding", "gzip"); - requestBuilder.addHeader("Content-Type", "application/json"); - String post = EntityUtils.toString((HttpEntity) entity); - RequestBody postBody = RequestBody.create(post.getBytes()); - requestBuilder.post(postBody); - - Request request = getRequest(requestBuilder); - _log.debug(String.format("Request Headers: %s", request.headers())); - - Response response = httpClient.newCall(request).execute(); - - int responseCode = response.code(); - - _log.debug(String.format("[GET] %s. Status code: %s", - request.url().toString(), - responseCode)); - - String statusMessage = ""; - if (responseCode < HttpURLConnection.HTTP_OK || responseCode >= HttpURLConnection.HTTP_MULT_CHOICE) { - _log.warn(String.format("Response status was: %s. Reason: %s", responseCode, - response.message())); - statusMessage = response.message(); - } - response.close(); - - return new SplitHttpResponse(responseCode, statusMessage, "", getResponseHeaders(response)); - } catch (Exception e) { - throw new IllegalStateException(String.format("Problem in http post operation: %s", e), e); - } - } - - protected okhttp3.Request.Builder getRequestBuilder() { - return new okhttp3.Request.Builder(); - } - - protected Request getRequest(okhttp3.Request.Builder requestBuilder) { - return requestBuilder.build(); - } - protected void setBasicHeaders(okhttp3.Request.Builder requestBuilder) { - requestBuilder.addHeader(HEADER_API_KEY, "Bearer " + _apikey); - requestBuilder.addHeader(HEADER_CLIENT_VERSION, _metadata.getSdkVersion()); - requestBuilder.addHeader(HEADER_CLIENT_MACHINE_IP, _metadata.getMachineIp()); - requestBuilder.addHeader(HEADER_CLIENT_MACHINE_NAME, _metadata.getMachineName()); - requestBuilder.addHeader(HEADER_CLIENT_KEY, _apikey.length() > 4 - ? _apikey.substring(_apikey.length() - 4) - : _apikey); - } - - protected void setAdditionalAndDecoratedHeaders(okhttp3.Request.Builder requestBuilder, Map> additionalHeaders) { - if (additionalHeaders != null) { - for (Map.Entry> entry : additionalHeaders.entrySet()) { - for (String value : entry.getValue()) { - requestBuilder.addHeader(entry.getKey(), value); - } - } - } - HttpRequest request = new HttpGet(""); - _requestDecorator.decorateHeaders(request); - for (Header header : request.getHeaders()) { - requestBuilder.addHeader(header.getName(), header.getValue()); - } - } - - protected Header[] getResponseHeaders(Response response) { - List responseHeaders = new ArrayList<>(); - Map> map = response.headers().toMultimap(); - for (Map.Entry> entry : map.entrySet()) { - if (entry.getKey() != null) { - BasicHeader responseHeader = new BasicHeader(entry.getKey(), entry.getValue()); - responseHeaders.add(responseHeader); - } - } - return responseHeaders.toArray(new split.org.apache.hc.core5.http.Header[0]); - } - @Override - public void close() throws IOException { - httpClient.dispatcher().executorService().shutdown(); - } - } From 4d888cbcf98ae0ac48e2f2b9dd342051065b9fac Mon Sep 17 00:00:00 2001 From: Bilal Al Date: Wed, 11 Sep 2024 07:00:26 -0700 Subject: [PATCH 4/4] renamed module to okhttp-modules --- client/src/main/java/io/split/client/SplitFactoryImpl.java | 3 --- client/src/main/java/io/split/service/CustomHttpModule.java | 3 --- {http-modules => okhttp-modules}/pom.xml | 2 +- .../split/httpmodules/okhttp/HTTPKerberosAuthInterceptor.java | 0 .../io/split/httpmodules/okhttp/KerberosAuthException.java | 0 .../java/io/split/httpmodules/okhttp/OkHttpClientImpl.java | 0 .../main/java/io/split/httpmodules/okhttp/OkHttpModule.java | 0 .../main/java/io/split/httpmodules/okhttp/ProxyAuthScheme.java | 0 .../httpmodules/okhttp/HTTPKerberosAuthIntercepterTest.java | 0 .../split/httpmodules/okhttp/HttpSplitClientKerberosTest.java | 0 .../java/io/split/httpmodules/okhttp/SplitConfigTests.java | 0 .../java/io/split/httpmodules/okhttp/SplitFactoryTests.java | 0 {http-modules => okhttp-modules}/src/test/resources/krb5.conf | 0 .../org/powermock/extensions/configuration.properties | 0 pom.xml | 2 +- 15 files changed, 2 insertions(+), 8 deletions(-) rename {http-modules => okhttp-modules}/pom.xml (98%) rename {http-modules => okhttp-modules}/src/main/java/io/split/httpmodules/okhttp/HTTPKerberosAuthInterceptor.java (100%) rename {http-modules => okhttp-modules}/src/main/java/io/split/httpmodules/okhttp/KerberosAuthException.java (100%) rename {http-modules => okhttp-modules}/src/main/java/io/split/httpmodules/okhttp/OkHttpClientImpl.java (100%) rename {http-modules => okhttp-modules}/src/main/java/io/split/httpmodules/okhttp/OkHttpModule.java (100%) rename {http-modules => okhttp-modules}/src/main/java/io/split/httpmodules/okhttp/ProxyAuthScheme.java (100%) rename {http-modules => okhttp-modules}/src/test/java/io/split/httpmodules/okhttp/HTTPKerberosAuthIntercepterTest.java (100%) rename {http-modules => okhttp-modules}/src/test/java/io/split/httpmodules/okhttp/HttpSplitClientKerberosTest.java (100%) rename {http-modules => okhttp-modules}/src/test/java/io/split/httpmodules/okhttp/SplitConfigTests.java (100%) rename {http-modules => okhttp-modules}/src/test/java/io/split/httpmodules/okhttp/SplitFactoryTests.java (100%) rename {http-modules => okhttp-modules}/src/test/resources/krb5.conf (100%) rename {http-modules => okhttp-modules}/src/test/resources/org/powermock/extensions/configuration.properties (100%) diff --git a/client/src/main/java/io/split/client/SplitFactoryImpl.java b/client/src/main/java/io/split/client/SplitFactoryImpl.java index 7d8a0aa51..b4877708d 100644 --- a/client/src/main/java/io/split/client/SplitFactoryImpl.java +++ b/client/src/main/java/io/split/client/SplitFactoryImpl.java @@ -497,9 +497,6 @@ public boolean isDestroyed() { return isTerminated; } - public void setSplitHttpClient(SplitHttpClient splitHttpClient) { - _splitHttpClient = splitHttpClient; - } protected static SplitHttpClient buildSplitHttpClient(String apiToken, SplitClientConfig config, SDKMetadata sdkMetadata, RequestDecorator requestDecorator) throws URISyntaxException { diff --git a/client/src/main/java/io/split/service/CustomHttpModule.java b/client/src/main/java/io/split/service/CustomHttpModule.java index 837f42149..20e8b3de8 100644 --- a/client/src/main/java/io/split/service/CustomHttpModule.java +++ b/client/src/main/java/io/split/service/CustomHttpModule.java @@ -2,12 +2,9 @@ import io.split.client.RequestDecorator; import io.split.client.utils.SDKMetadata; -import io.split.service.SplitHttpClient; import java.io.IOException; public interface CustomHttpModule { public SplitHttpClient createClient(String apiToken, SDKMetadata sdkMetadata, RequestDecorator requestDecorator) throws IOException; - - } diff --git a/http-modules/pom.xml b/okhttp-modules/pom.xml similarity index 98% rename from http-modules/pom.xml rename to okhttp-modules/pom.xml index 3d5f4a980..53943f24c 100644 --- a/http-modules/pom.xml +++ b/okhttp-modules/pom.xml @@ -9,7 +9,7 @@ 4.0.0 - http-modules + okhttp-modules jar http-modules Alternative Http Modules diff --git a/http-modules/src/main/java/io/split/httpmodules/okhttp/HTTPKerberosAuthInterceptor.java b/okhttp-modules/src/main/java/io/split/httpmodules/okhttp/HTTPKerberosAuthInterceptor.java similarity index 100% rename from http-modules/src/main/java/io/split/httpmodules/okhttp/HTTPKerberosAuthInterceptor.java rename to okhttp-modules/src/main/java/io/split/httpmodules/okhttp/HTTPKerberosAuthInterceptor.java diff --git a/http-modules/src/main/java/io/split/httpmodules/okhttp/KerberosAuthException.java b/okhttp-modules/src/main/java/io/split/httpmodules/okhttp/KerberosAuthException.java similarity index 100% rename from http-modules/src/main/java/io/split/httpmodules/okhttp/KerberosAuthException.java rename to okhttp-modules/src/main/java/io/split/httpmodules/okhttp/KerberosAuthException.java diff --git a/http-modules/src/main/java/io/split/httpmodules/okhttp/OkHttpClientImpl.java b/okhttp-modules/src/main/java/io/split/httpmodules/okhttp/OkHttpClientImpl.java similarity index 100% rename from http-modules/src/main/java/io/split/httpmodules/okhttp/OkHttpClientImpl.java rename to okhttp-modules/src/main/java/io/split/httpmodules/okhttp/OkHttpClientImpl.java diff --git a/http-modules/src/main/java/io/split/httpmodules/okhttp/OkHttpModule.java b/okhttp-modules/src/main/java/io/split/httpmodules/okhttp/OkHttpModule.java similarity index 100% rename from http-modules/src/main/java/io/split/httpmodules/okhttp/OkHttpModule.java rename to okhttp-modules/src/main/java/io/split/httpmodules/okhttp/OkHttpModule.java diff --git a/http-modules/src/main/java/io/split/httpmodules/okhttp/ProxyAuthScheme.java b/okhttp-modules/src/main/java/io/split/httpmodules/okhttp/ProxyAuthScheme.java similarity index 100% rename from http-modules/src/main/java/io/split/httpmodules/okhttp/ProxyAuthScheme.java rename to okhttp-modules/src/main/java/io/split/httpmodules/okhttp/ProxyAuthScheme.java diff --git a/http-modules/src/test/java/io/split/httpmodules/okhttp/HTTPKerberosAuthIntercepterTest.java b/okhttp-modules/src/test/java/io/split/httpmodules/okhttp/HTTPKerberosAuthIntercepterTest.java similarity index 100% rename from http-modules/src/test/java/io/split/httpmodules/okhttp/HTTPKerberosAuthIntercepterTest.java rename to okhttp-modules/src/test/java/io/split/httpmodules/okhttp/HTTPKerberosAuthIntercepterTest.java diff --git a/http-modules/src/test/java/io/split/httpmodules/okhttp/HttpSplitClientKerberosTest.java b/okhttp-modules/src/test/java/io/split/httpmodules/okhttp/HttpSplitClientKerberosTest.java similarity index 100% rename from http-modules/src/test/java/io/split/httpmodules/okhttp/HttpSplitClientKerberosTest.java rename to okhttp-modules/src/test/java/io/split/httpmodules/okhttp/HttpSplitClientKerberosTest.java diff --git a/http-modules/src/test/java/io/split/httpmodules/okhttp/SplitConfigTests.java b/okhttp-modules/src/test/java/io/split/httpmodules/okhttp/SplitConfigTests.java similarity index 100% rename from http-modules/src/test/java/io/split/httpmodules/okhttp/SplitConfigTests.java rename to okhttp-modules/src/test/java/io/split/httpmodules/okhttp/SplitConfigTests.java diff --git a/http-modules/src/test/java/io/split/httpmodules/okhttp/SplitFactoryTests.java b/okhttp-modules/src/test/java/io/split/httpmodules/okhttp/SplitFactoryTests.java similarity index 100% rename from http-modules/src/test/java/io/split/httpmodules/okhttp/SplitFactoryTests.java rename to okhttp-modules/src/test/java/io/split/httpmodules/okhttp/SplitFactoryTests.java diff --git a/http-modules/src/test/resources/krb5.conf b/okhttp-modules/src/test/resources/krb5.conf similarity index 100% rename from http-modules/src/test/resources/krb5.conf rename to okhttp-modules/src/test/resources/krb5.conf diff --git a/http-modules/src/test/resources/org/powermock/extensions/configuration.properties b/okhttp-modules/src/test/resources/org/powermock/extensions/configuration.properties similarity index 100% rename from http-modules/src/test/resources/org/powermock/extensions/configuration.properties rename to okhttp-modules/src/test/resources/org/powermock/extensions/configuration.properties diff --git a/pom.xml b/pom.xml index d524fd74e..5afc4f3f7 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ redis-wrapper testing client - http-modules + okhttp-modules