diff --git a/components/client/src/main/java/com/hotels/styx/client/StyxHttpClient.java b/components/client/src/main/java/com/hotels/styx/client/StyxHttpClient.java index 5b7487a84f..ff118d73da 100644 --- a/components/client/src/main/java/com/hotels/styx/client/StyxHttpClient.java +++ b/components/client/src/main/java/com/hotels/styx/client/StyxHttpClient.java @@ -29,8 +29,8 @@ import com.hotels.styx.api.HttpResponseStatus; import com.hotels.styx.api.MetricRegistry; import com.hotels.styx.api.metrics.codahale.CodaHaleMetricRegistry; -import com.hotels.styx.api.extension.service.BackendService; import com.hotels.styx.api.extension.service.RewriteRule; +import com.hotels.styx.api.extension.service.StickySessionConfig; import com.hotels.styx.client.retry.RetryNTimes; import com.hotels.styx.client.stickysession.StickySessionLoadBalancingStrategy; import com.hotels.styx.server.HttpInterceptorContext; @@ -49,6 +49,7 @@ import static com.hotels.styx.api.HttpHeaderNames.CONTENT_LENGTH; import static com.hotels.styx.api.HttpHeaderNames.TRANSFER_ENCODING; import static com.hotels.styx.api.StyxInternalObservables.toRxObservable; +import static com.hotels.styx.api.extension.service.StickySessionConfig.stickySessionDisabled; import static com.hotels.styx.client.stickysession.StickySessionCookie.newStickySessionCookie; import static io.netty.handler.codec.http.HttpMethod.HEAD; import static java.util.Collections.emptyList; @@ -70,14 +71,15 @@ public final class StyxHttpClient implements HttpClient { private final LoadBalancer loadBalancer; private final RetryPolicy retryPolicy; private final OriginStatsFactory originStatsFactory; - private final BackendService backendService; private final MetricRegistry metricsRegistry; private final boolean contentValidation; private final String originsRestrictionCookieName; + private final StickySessionConfig stickySessionConfig; private StyxHttpClient(Builder builder) { - this.backendService = requireNonNull(builder.backendService); - this.id = backendService.id(); + this.id = requireNonNull(builder.backendServiceId); + + this.stickySessionConfig = requireNonNull(builder.stickySessionConfig); this.originStatsFactory = requireNonNull(builder.originStatsFactory); @@ -99,17 +101,13 @@ public Observable sendRequest(HttpRequest request) { return sendRequest(rewriteUrl(request), new ArrayList<>(), 0); } - public boolean isHttps() { - return backendService.tlsSettings().isPresent(); - } - /** * Create a new builder. * * @return a new builder */ - public static Builder newHttpClientBuilder(BackendService backendService) { - return new Builder(backendService); + public static Builder newHttpClientBuilder(Id backendServiceId) { + return new Builder(backendServiceId); } private static boolean isError(HttpResponseStatus status) { @@ -191,7 +189,6 @@ public List avoidOrigins() { } else { return Observable.error(cause); } - } private static final class RetryPolicyContext implements RetryPolicy.Context { @@ -307,7 +304,7 @@ public List avoidOrigins() { private HttpResponse addStickySessionIdentifier(HttpResponse httpResponse, Origin origin) { if (this.loadBalancer instanceof StickySessionLoadBalancingStrategy) { - int maxAge = backendService.stickySessionConfig().stickySessionTimeoutSeconds(); + int maxAge = stickySessionConfig.stickySessionTimeoutSeconds(); return httpResponse.newBuilder() .addCookies(newStickySessionCookie(id, origin.id(), maxAge)) .build(); @@ -324,7 +321,7 @@ private HttpRequest rewriteUrl(HttpRequest request) { public String toString() { return toStringHelper(this) .add("id", id) - .add("stickySessionConfig", backendService.stickySessionConfig()) + .add("stickySessionConfig", stickySessionConfig) .add("retryPolicy", retryPolicy) .add("rewriteRuleset", rewriteRuleset) .add("loadBalancingStrategy", loadBalancer) @@ -335,7 +332,8 @@ public String toString() { * A builder for {@link com.hotels.styx.client.StyxHttpClient}. */ public static class Builder { - private final BackendService backendService; + + private final Id backendServiceId; private MetricRegistry metricsRegistry = new CodaHaleMetricRegistry(); private List rewriteRules = emptyList(); private RetryPolicy retryPolicy = new RetryNTimes(3); @@ -343,9 +341,15 @@ public static class Builder { private boolean contentValidation; private OriginStatsFactory originStatsFactory; private String originsRestrictionCookieName; + private StickySessionConfig stickySessionConfig = stickySessionDisabled(); - public Builder(BackendService backendService) { - this.backendService = requireNonNull(backendService); + public Builder(Id backendServiceId) { + this.backendServiceId = requireNonNull(backendServiceId); + } + + public Builder stickySessionConfig(StickySessionConfig stickySessionConfig) { + this.stickySessionConfig = requireNonNull(stickySessionConfig); + return this; } public Builder metricsRegistry(MetricRegistry metricsRegistry) { @@ -390,5 +394,6 @@ public StyxHttpClient build() { } return new StyxHttpClient(this); } + } } diff --git a/components/client/src/test/integration/scala/com/hotels/styx/client/HttpClientSpec.scala b/components/client/src/test/integration/scala/com/hotels/styx/client/HttpClientSpec.scala index 741c161a17..3d69faa2c1 100644 --- a/components/client/src/test/integration/scala/com/hotels/styx/client/HttpClientSpec.scala +++ b/components/client/src/test/integration/scala/com/hotels/styx/client/HttpClientSpec.scala @@ -74,7 +74,7 @@ class HttpClientSpec extends FunSuite with BeforeAndAfterAll with ShouldMatchers .responseTimeoutMillis(responseTimeout) .build() - client = newHttpClientBuilder(backendService) + client = newHttpClientBuilder(backendService.id()) .loadBalancer(busyConnectionStrategy(activeOrigins(backendService))) .build } diff --git a/components/client/src/test/integration/scala/com/hotels/styx/client/RetryHandlingSpec.scala b/components/client/src/test/integration/scala/com/hotels/styx/client/RetryHandlingSpec.scala index fae63f0e6e..ac4dd0628a 100644 --- a/components/client/src/test/integration/scala/com/hotels/styx/client/RetryHandlingSpec.scala +++ b/components/client/src/test/integration/scala/com/hotels/styx/client/RetryHandlingSpec.scala @@ -126,7 +126,7 @@ class RetryHandlingSpec extends FunSuite with BeforeAndAfterAll with Matchers wi .origins(unhealthyOriginOne, unhealthyOriginTwo, unhealthyOriginThree, healthyOriginTwo) .build() - val client: StyxHttpClient = newHttpClientBuilder(backendService) + val client: StyxHttpClient = newHttpClientBuilder(backendService.id) .retryPolicy(new RetryNTimes(3)) .loadBalancer(stickySessionStrategy(activeOrigins(backendService))) .build @@ -139,7 +139,7 @@ class RetryHandlingSpec extends FunSuite with BeforeAndAfterAll with Matchers wi val backendService = new BackendService.Builder() .origins(unhealthyOriginOne, unhealthyOriginTwo, unhealthyOriginThree) .build() - val client: StyxHttpClient = newHttpClientBuilder(backendService) + val client: StyxHttpClient = newHttpClientBuilder(backendService.id) .loadBalancer(stickySessionStrategy(activeOrigins(backendService))) .retryPolicy(new RetryNTimes(2)) .build @@ -155,7 +155,7 @@ class RetryHandlingSpec extends FunSuite with BeforeAndAfterAll with Matchers wi .stickySessionConfig(StickySessionEnabled) .build() - val client: StyxHttpClient = newHttpClientBuilder(backendService) + val client: StyxHttpClient = newHttpClientBuilder(backendService.id) .retryPolicy(new RetryNTimes(3)) .loadBalancer(stickySessionStrategy(activeOrigins(backendService))) .build diff --git a/components/client/src/test/integration/scala/com/hotels/styx/client/StickySessionSpec.scala b/components/client/src/test/integration/scala/com/hotels/styx/client/StickySessionSpec.scala index b2c101c81c..2e2d9ce9d5 100644 --- a/components/client/src/test/integration/scala/com/hotels/styx/client/StickySessionSpec.scala +++ b/components/client/src/test/integration/scala/com/hotels/styx/client/StickySessionSpec.scala @@ -16,6 +16,7 @@ package com.hotels.styx.client import java.nio.charset.Charset +import java.util.concurrent.TimeUnit import com.github.tomakehurst.wiremock.client.WireMock.aResponse import com.hotels.styx.api.HttpHeaderNames.CONTENT_LENGTH @@ -97,9 +98,13 @@ class StickySessionSpec extends FunSuite with BeforeAndAfter with ShouldMatchers def stickySessionStrategy(activeOrigins: ActiveOrigins) = new StickySessionLoadBalancingStrategy(activeOrigins, roundRobinStrategy(activeOrigins)) + test("Responds with sticky session cookie when STICKY_SESSION_ENABLED=true") { - val client = newHttpClientBuilder(backendService) + val stickySessionConfig = StickySessionConfig.newStickySessionConfigBuilder().timeout(100, TimeUnit.SECONDS).build() + + val client = newHttpClientBuilder(backendService.id) .loadBalancer(stickySessionStrategy(activeOrigins(backendService))) + .stickySessionConfig(stickySessionConfig) .build val request: HttpRequest = HttpRequest.get("/") @@ -113,10 +118,12 @@ class StickySessionSpec extends FunSuite with BeforeAndAfter with ShouldMatchers cookie.path().get() should be("/") cookie.httpOnly() should be(true) cookie.maxAge().isPresent should be(true) + + cookie.maxAge().get() should be (100L) } test("Responds without sticky session cookie when sticky session is not enabled") { - val client: StyxHttpClient = newHttpClientBuilder(backendService) + val client: StyxHttpClient = newHttpClientBuilder(backendService.id) .loadBalancer(roundRobinStrategy(activeOrigins(backendService))) .build @@ -129,7 +136,7 @@ class StickySessionSpec extends FunSuite with BeforeAndAfter with ShouldMatchers } test("Routes to origins indicated by sticky session cookie.") { - val client: StyxHttpClient = newHttpClientBuilder(backendService) + val client: StyxHttpClient = newHttpClientBuilder(backendService.id) .loadBalancer(stickySessionStrategy(activeOrigins(backendService))) .build @@ -147,7 +154,7 @@ class StickySessionSpec extends FunSuite with BeforeAndAfter with ShouldMatchers } test("Routes to origins indicated by sticky session cookie when other cookies are provided.") { - val client: StyxHttpClient = newHttpClientBuilder(backendService) + val client: StyxHttpClient = newHttpClientBuilder(backendService.id) .loadBalancer(stickySessionStrategy(activeOrigins(backendService))) .build @@ -169,7 +176,7 @@ class StickySessionSpec extends FunSuite with BeforeAndAfter with ShouldMatchers } test("Routes to new origin when the origin indicated by sticky session cookie does not exist.") { - val client: StyxHttpClient = newHttpClientBuilder(backendService) + val client: StyxHttpClient = newHttpClientBuilder(backendService.id) .loadBalancer(stickySessionStrategy(activeOrigins(backendService))) .build @@ -194,7 +201,7 @@ class StickySessionSpec extends FunSuite with BeforeAndAfter with ShouldMatchers test("Routes to new origin when the origin indicated by sticky session cookie is no longer available.") { server1.stop() - val client: StyxHttpClient = newHttpClientBuilder(backendService) + val client: StyxHttpClient = newHttpClientBuilder(backendService.id) .loadBalancer(stickySessionStrategy(activeOrigins(backendService))) .build diff --git a/components/client/src/test/unit/java/com/hotels/styx/client/StyxHttpClientTest.java b/components/client/src/test/unit/java/com/hotels/styx/client/StyxHttpClientTest.java index e933b565c7..ca69d9a979 100644 --- a/components/client/src/test/unit/java/com/hotels/styx/client/StyxHttpClientTest.java +++ b/components/client/src/test/unit/java/com/hotels/styx/client/StyxHttpClientTest.java @@ -101,7 +101,7 @@ public void setUp() { public void sendsRequestToHostChosenByLoadBalancer() { StyxHostHttpClient hostClient = mockHostClient(just(response(OK).build())); - StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendService) + StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendService.id()) .metricsRegistry(metricRegistry) .loadBalancer( mockLoadBalancer( @@ -120,7 +120,7 @@ public void constructsRetryContextWhenLoadBalancerDoesNotFindAvailableOrigins() RetryPolicy retryPolicy = mockRetryPolicy(true, true, true); StyxHostHttpClient hostClient = mockHostClient(just(response(OK).build())); - StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendService) + StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendService.id()) .metricsRegistry(metricRegistry) .loadBalancer( mockLoadBalancer( @@ -158,7 +158,7 @@ public void retriesWhenRetryPolicyTellsToRetry() { StyxHostHttpClient secondClient = mockHostClient(just(response(OK).build())); - StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendService) + StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendService.id()) .metricsRegistry(metricRegistry) .loadBalancer( mockLoadBalancer( @@ -198,7 +198,7 @@ public void stopsRetriesWhenRetryPolicyTellsToStop() { StyxHostHttpClient secondClient = mockHostClient(Observable.error(new OriginUnreachableException(ORIGIN_2, new RuntimeException("An error occurred")))); StyxHostHttpClient thirdClient = mockHostClient(Observable.error(new OriginUnreachableException(ORIGIN_2, new RuntimeException("An error occurred")))); - StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendService) + StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendService.id()) .metricsRegistry(metricRegistry) .loadBalancer( mockLoadBalancer( @@ -230,7 +230,7 @@ public void retriesAtMost3Times() { StyxHostHttpClient thirdClient = mockHostClient(Observable.error(new OriginUnreachableException(ORIGIN_3, new RuntimeException("An error occurred")))); StyxHostHttpClient fourthClient = mockHostClient(Observable.error(new OriginUnreachableException(ORIGIN_4, new RuntimeException("An error occurred")))); - StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendService) + StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendService.id()) .metricsRegistry(metricRegistry) .loadBalancer( mockLoadBalancer( @@ -262,7 +262,7 @@ public void retriesAtMost3Times() { public void incrementsResponseStatusMetricsForBadResponse() { StyxHostHttpClient hostClient = mockHostClient(just(response(BAD_REQUEST).build())); - StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendService) + StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendService.id()) .metricsRegistry(metricRegistry) .loadBalancer( mockLoadBalancer(Optional.of(remoteHost(SOME_ORIGIN, toHandler(hostClient), hostClient)))) @@ -279,7 +279,7 @@ public void incrementsResponseStatusMetricsForBadResponse() { public void incrementsResponseStatusMetricsFor401() { StyxHostHttpClient hostClient = mockHostClient(just(response(UNAUTHORIZED).build())); - StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendService) + StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendService.id()) .metricsRegistry(metricRegistry) .loadBalancer( mockLoadBalancer(Optional.of(remoteHost(SOME_ORIGIN, toHandler(hostClient), hostClient))) @@ -297,7 +297,7 @@ public void incrementsResponseStatusMetricsFor401() { public void incrementsResponseStatusMetricsFor500() { StyxHostHttpClient hostClient = mockHostClient(just(response(INTERNAL_SERVER_ERROR).build())); - StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendService) + StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendService.id()) .metricsRegistry(metricRegistry) .loadBalancer( mockLoadBalancer(Optional.of(remoteHost(SOME_ORIGIN, toHandler(hostClient), hostClient))) @@ -315,7 +315,7 @@ public void incrementsResponseStatusMetricsFor500() { public void incrementsResponseStatusMetricsFor501() { StyxHostHttpClient hostClient = mockHostClient(just(response(NOT_IMPLEMENTED).build())); - StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendService) + StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendService.id()) .metricsRegistry(metricRegistry) .loadBalancer( mockLoadBalancer(Optional.of(remoteHost(SOME_ORIGIN, toHandler(hostClient), hostClient))) @@ -336,7 +336,7 @@ public void removesBadContentLength() { .addHeader(TRANSFER_ENCODING, CHUNKED) .build())); - StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendService) + StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendService.id()) .metricsRegistry(metricRegistry) .loadBalancer( mockLoadBalancer(Optional.of(remoteHost(SOME_ORIGIN, toHandler(hostClient), hostClient))) @@ -358,7 +358,7 @@ public void updatesCountersWhenTransactionIsCancelled() { PublishSubject responseSubject = PublishSubject.create(); StyxHostHttpClient hostClient = mockHostClient(responseSubject); - StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendWithOrigins(origin.host().getPort())) + StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendService.id()) .loadBalancer( mockLoadBalancer(Optional.of(remoteHost(origin, toHandler(hostClient), hostClient))) ) @@ -382,7 +382,7 @@ public void prefersStickyOrigins() { LoadBalancer loadBalancer = mockLoadBalancer(Optional.of(remoteHost(origin, toHandler(hostClient), hostClient))); - StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendWithOrigins(origin.host().getPort())) + StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendService.id()) .loadBalancer(loadBalancer) .metricsRegistry(metricRegistry) .build(); @@ -408,7 +408,7 @@ public void prefersRestrictedOrigins() { LoadBalancer loadBalancer = mockLoadBalancer(Optional.of(remoteHost(origin, toHandler(hostClient), hostClient))); - StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendWithOrigins(origin.host().getPort())) + StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendService.id()) .loadBalancer(loadBalancer) .metricsRegistry(metricRegistry) .originsRestrictionCookieName("restrictedOrigin") @@ -434,7 +434,7 @@ public void prefersRestrictedOriginsOverStickyOriginsWhenBothAreConfigured() { StyxHostHttpClient hostClient = mockHostClient(just(response(OK).build())); LoadBalancer loadBalancer = mockLoadBalancer(Optional.of(remoteHost(origin, toHandler(hostClient), hostClient))); - StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendWithOrigins(origin.host().getPort())) + StyxHttpClient styxHttpClient = new StyxHttpClient.Builder(backendService.id()) .originsRestrictionCookieName("restrictedOrigin") .loadBalancer(loadBalancer) .metricsRegistry(metricRegistry) diff --git a/components/proxy/src/main/java/com/hotels/styx/proxy/StyxBackendServiceClientFactory.java b/components/proxy/src/main/java/com/hotels/styx/proxy/StyxBackendServiceClientFactory.java index 3b224f7ff2..5542f82e81 100644 --- a/components/proxy/src/main/java/com/hotels/styx/proxy/StyxBackendServiceClientFactory.java +++ b/components/proxy/src/main/java/com/hotels/styx/proxy/StyxBackendServiceClientFactory.java @@ -71,8 +71,9 @@ public HttpClient createClient(BackendService backendService, OriginsInventory o originRestrictionCookie ); - return new StyxHttpClient.Builder(backendService) + return new StyxHttpClient.Builder(backendService.id()) .loadBalancer(loadBalancingStrategy) + .stickySessionConfig(backendService.stickySessionConfig()) .metricsRegistry(environment.metricRegistry()) .retryPolicy(retryPolicy) .enableContentValidation() diff --git a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/HttpResponseSpec.scala b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/HttpResponseSpec.scala index 8fe9ea8450..c59af74a51 100644 --- a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/HttpResponseSpec.scala +++ b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/HttpResponseSpec.scala @@ -18,6 +18,7 @@ package com.hotels.styx import java.nio.charset.StandardCharsets.UTF_8 import com.hotels.styx.api.HttpRequest.get +import com.hotels.styx.api.Id.id import com.hotels.styx.api.extension.ActiveOrigins import com.hotels.styx.api.extension.loadbalancing.spi.LoadBalancer import com.hotels.styx.api.HttpResponseStatus._ @@ -67,7 +68,7 @@ class HttpResponseSpec extends FunSuite origins = Origins(originOne), responseTimeout = responseTimeout) - client = newHttpClientBuilder(backendService.asJava) + client = newHttpClientBuilder(id(backendService.appId)) .loadBalancer(busyConnectionStrategy(activeOrigins(backendService.asJava))) .build } diff --git a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/client/ExpiringConnectionSpec.scala b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/client/ExpiringConnectionSpec.scala index 8c05789cdd..1de870f0ee 100644 --- a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/client/ExpiringConnectionSpec.scala +++ b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/client/ExpiringConnectionSpec.scala @@ -61,7 +61,7 @@ class ExpiringConnectionSpec extends FunSpec .origins(newOriginBuilder("localhost", styxServer.httpPort).build()) .build() - pooledClient = newHttpClientBuilder(backendService) + pooledClient = newHttpClientBuilder(backendService.id) .loadBalancer(roundRobinStrategy(activeOrigins(backendService))) .build } diff --git a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/client/OriginClosesConnectionSpec.scala b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/client/OriginClosesConnectionSpec.scala index 8873044b1f..a3f62d19e6 100644 --- a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/client/OriginClosesConnectionSpec.scala +++ b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/client/OriginClosesConnectionSpec.scala @@ -112,7 +112,7 @@ class OriginClosesConnectionSpec extends FunSuite origins = Origins(originOne), responseTimeout = timeout.milliseconds).asJava val styxClient = com.hotels.styx.client.StyxHttpClient.newHttpClientBuilder( - backendService) + backendService.id) .loadBalancer(busyConnectionStrategy(activeOrigins(backendService))) .build