From 6a023ca75bd9a8dfb4a8d3507d35b7008a6650f8 Mon Sep 17 00:00:00 2001 From: kvosper Date: Fri, 6 Jul 2018 13:59:08 +0100 Subject: [PATCH] New 1.0 API: Tidy up Address outstanding issues. (#204) --- .../java/com/hotels/styx/api/HttpRequest.java | 26 --- .../com/hotels/styx/api/HttpResponse.java | 42 +---- .../hotels/styx/api/StreamingHttpMessage.java | 26 +++ .../hotels/styx/api/StyxCoreObservable.java | 4 + .../com/hotels/styx/api/StyxObservable.java | 7 - .../CustomHttpResponseStatus.java | 4 +- .../styx/api/messages/HttpResponseStatus.java | 4 +- .../hotels/styx/api/FullHttpRequestTest.java | 18 -- .../com/hotels/styx/api/HttpRequestTest.java | 20 ++- .../com/hotels/styx/api/HttpResponseTest.java | 28 +++- .../java/com/hotels/styx/api/MockContext.java | 42 ----- .../metrics/HttpErrorStatusMetricsTest.java | 6 +- .../styx/client/OriginsInventoryTest.java | 4 - .../origincommands/DisableOriginTest.java | 37 ----- .../origincommands/EnableOriginTest.java | 37 ----- .../styx/common/CompletableFutures.java | 3 +- .../styx/proxy/BackendServicesRouter.java | 1 - .../styx/proxy/RouteHandlerAdapter.java | 5 +- .../StyxBackendServiceClientFactory.java | 1 + .../styx/proxy/plugin/InstrumentedPlugin.java | 4 +- .../hotels/styx/startup/AdminServerSetUp.java | 4 - .../resources/admin/dashboard/css/styx.css | 4 - .../routing/routes/ProxyToBackendRoute.java | 1 - docs/user-guide/basic-usage.md | 2 +- docs/user-guide/metrics-reference.md | 13 +- docs/user-guide/metrics.md | 12 -- pom.xml | 2 - support/api-testsupport/pom.xml | 1 - support/testsupport/pom.xml | 1 - .../com/hotels/styx/support/EqualsTester.java | 136 --------------- .../styx/support/RelationshipTester.java | 157 ------------------ .../plugins/ContentDecodeFailurePlugin.java | 3 +- .../plugins/PluginErrorHandlingSpec.scala | 4 +- .../scripts/load-test-tool/load_test.py | 2 +- .../performance/scripts/load-test-tool/wrk.py | 2 +- 35 files changed, 99 insertions(+), 564 deletions(-) rename components/api/src/main/java/com/hotels/styx/api/{netty => messages}/CustomHttpResponseStatus.java (93%) delete mode 100644 components/client/src/test/unit/java/com/hotels/styx/client/origincommands/DisableOriginTest.java delete mode 100644 components/client/src/test/unit/java/com/hotels/styx/client/origincommands/EnableOriginTest.java delete mode 100644 support/testsupport/src/main/java/com/hotels/styx/support/EqualsTester.java delete mode 100644 support/testsupport/src/main/java/com/hotels/styx/support/RelationshipTester.java diff --git a/components/api/src/main/java/com/hotels/styx/api/HttpRequest.java b/components/api/src/main/java/com/hotels/styx/api/HttpRequest.java index 1087be9ae8..bb7bb50f2c 100644 --- a/components/api/src/main/java/com/hotels/styx/api/HttpRequest.java +++ b/components/api/src/main/java/com/hotels/styx/api/HttpRequest.java @@ -21,14 +21,12 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.CompositeByteBuf; import rx.Observable; -import rx.Subscriber; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.concurrent.CompletableFuture; import static com.google.common.base.Objects.toStringHelper; import static com.google.common.base.Preconditions.checkArgument; @@ -378,30 +376,6 @@ public String toString() { .toString(); } - // TODO: Mikko: Identical content to the HttpResponse one. Consider moving to base class. - public CompletableFuture releaseContentBuffers() { - CompletableFuture future = new CompletableFuture<>(); - - ((StyxCoreObservable) body).delegate().subscribe(new Subscriber() { - @Override - public void onCompleted() { - future.complete(true); - } - - @Override - public void onError(Throwable e) { - - } - - @Override - public void onNext(ByteBuf byteBuf) { - byteBuf.release(); - } - }); - - return future; - } - /** * Builder. */ diff --git a/components/api/src/main/java/com/hotels/styx/api/HttpResponse.java b/components/api/src/main/java/com/hotels/styx/api/HttpResponse.java index 36bd83a002..6f0421db66 100644 --- a/components/api/src/main/java/com/hotels/styx/api/HttpResponse.java +++ b/components/api/src/main/java/com/hotels/styx/api/HttpResponse.java @@ -24,13 +24,11 @@ import io.netty.buffer.CompositeByteBuf; import io.netty.util.ReferenceCountUtil; import rx.Observable; -import rx.Subscriber; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.concurrent.CompletableFuture; import static com.google.common.base.Objects.toStringHelper; import static com.google.common.base.Preconditions.checkArgument; @@ -208,30 +206,6 @@ public boolean equals(Object obj) { && Objects.equal(this.cookies, other.cookies); } - public CompletableFuture releaseContentBuffers() { - CompletableFuture future = new CompletableFuture<>(); - - ((StyxCoreObservable) body).delegate() - .subscribe(new Subscriber() { - @Override - public void onCompleted() { - future.complete(true); - } - - @Override - public void onError(Throwable e) { - - } - - @Override - public void onNext(ByteBuf byteBuf) { - byteBuf.release(); - } - }); - - return future; - } - /** * Builder. */ @@ -293,11 +267,10 @@ public Builder body(StyxObservable content) { } /** - * Sets the message body. As the content length is known, this header will also be set. - *

- * TODO: Mikko: Styx 2.0 API: Missing test: + * Sets the message body by encoding a {@link StyxObservable} of {@link String}s into bytes. * * @param contentObservable message body content. + * @param charset character set * @return {@code this} */ public Builder body(StyxObservable contentObservable, Charset charset) { @@ -416,16 +389,11 @@ public Builder removeHeader(CharSequence name) { } /** - * Removes body of the request - *

- * TODO: Mikko: Styx 2.0 API: Ensure that reference counting works well with the new API. - * Most importantly it should be safe to use without consumers accidentally using the API - * in a dangerous way that might cause buffer leaks. - *

- * Especially when transforming a response to another, etc. + * Removes body of the request. * - * @return + * @return {@code this} */ + // TODO: See https://github.com/HotelsDotCom/styx/issues/201 public Builder removeBody() { Observable delegate = ((StyxCoreObservable) body) .delegate() diff --git a/components/api/src/main/java/com/hotels/styx/api/StreamingHttpMessage.java b/components/api/src/main/java/com/hotels/styx/api/StreamingHttpMessage.java index abfea4d5fa..3f0c7f63c5 100644 --- a/components/api/src/main/java/com/hotels/styx/api/StreamingHttpMessage.java +++ b/components/api/src/main/java/com/hotels/styx/api/StreamingHttpMessage.java @@ -17,9 +17,11 @@ import com.hotels.styx.api.messages.HttpVersion; import io.netty.buffer.ByteBuf; +import rx.Subscriber; import java.util.List; import java.util.Optional; +import java.util.concurrent.CompletableFuture; import static com.hotels.styx.api.HttpHeaderNames.CONTENT_LENGTH; import static com.hotels.styx.api.HttpHeaderNames.CONTENT_TYPE; @@ -113,4 +115,28 @@ default Optional contentType() { default boolean chunked() { return HttpMessageSupport.chunked(headers()); } + + default CompletableFuture releaseContentBuffers() { + CompletableFuture future = new CompletableFuture<>(); + + ((StyxCoreObservable) body()).delegate() + .subscribe(new Subscriber() { + @Override + public void onCompleted() { + future.complete(true); + } + + @Override + public void onError(Throwable e) { + future.completeExceptionally(e); + } + + @Override + public void onNext(ByteBuf byteBuf) { + byteBuf.release(); + } + }); + + return future; + } } diff --git a/components/api/src/main/java/com/hotels/styx/api/StyxCoreObservable.java b/components/api/src/main/java/com/hotels/styx/api/StyxCoreObservable.java index b6f74c0d23..13408def29 100644 --- a/components/api/src/main/java/com/hotels/styx/api/StyxCoreObservable.java +++ b/components/api/src/main/java/com/hotels/styx/api/StyxCoreObservable.java @@ -50,6 +50,10 @@ private static Observable toObservable(CompletionStage future) { })); } + public static StyxObservable empty() { + return new StyxCoreObservable<>(Observable.empty()); + } + public static StyxObservable of(T item) { return new StyxCoreObservable(Observable.just(item)); } diff --git a/components/api/src/main/java/com/hotels/styx/api/StyxObservable.java b/components/api/src/main/java/com/hotels/styx/api/StyxObservable.java index 9374feee23..837c1ea3ec 100644 --- a/components/api/src/main/java/com/hotels/styx/api/StyxObservable.java +++ b/components/api/src/main/java/com/hotels/styx/api/StyxObservable.java @@ -36,8 +36,6 @@ public interface StyxObservable { StyxObservable reduce(BiFunction accumulator, U initialValue); - // TODO: Mikko: Styx 2.0 Api: `onError`: is more flexible type signature possible? Such as: - // StyxObservable onError(Function> errorHandler); StyxObservable onError(Function> errorHandler); /** @@ -64,11 +62,6 @@ static StyxObservable of(T value) { return new StyxCoreObservable<>(Observable.just(value)); } - // TODO: Mikko: Only required for testing? - static StyxObservable empty() { - return new StyxCoreObservable(Observable.empty()); - } - static StyxObservable from(Iterable values) { return new StyxCoreObservable<>(Observable.from(values)); } diff --git a/components/api/src/main/java/com/hotels/styx/api/netty/CustomHttpResponseStatus.java b/components/api/src/main/java/com/hotels/styx/api/messages/CustomHttpResponseStatus.java similarity index 93% rename from components/api/src/main/java/com/hotels/styx/api/netty/CustomHttpResponseStatus.java rename to components/api/src/main/java/com/hotels/styx/api/messages/CustomHttpResponseStatus.java index fe1d8f0163..ff28607aa0 100644 --- a/components/api/src/main/java/com/hotels/styx/api/netty/CustomHttpResponseStatus.java +++ b/components/api/src/main/java/com/hotels/styx/api/messages/CustomHttpResponseStatus.java @@ -13,9 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package com.hotels.styx.api.netty; - -import com.hotels.styx.api.messages.HttpResponseStatus; +package com.hotels.styx.api.messages; /** * Custom HTTP response status codes. diff --git a/components/api/src/main/java/com/hotels/styx/api/messages/HttpResponseStatus.java b/components/api/src/main/java/com/hotels/styx/api/messages/HttpResponseStatus.java index c34c684369..148815893c 100644 --- a/components/api/src/main/java/com/hotels/styx/api/messages/HttpResponseStatus.java +++ b/components/api/src/main/java/com/hotels/styx/api/messages/HttpResponseStatus.java @@ -117,9 +117,7 @@ public static HttpResponseStatus statusWithCode(int code) { return STATUSES[code]; } - // TODO: Mikko: Styx 2.0 API: Had to relax visibility due to CustomHttpResponseStatus class. - // Check with Kyle. - public HttpResponseStatus(int code, String description) { + HttpResponseStatus(int code, String description) { this.code = code; this.description = description; } diff --git a/components/api/src/test/java/com/hotels/styx/api/FullHttpRequestTest.java b/components/api/src/test/java/com/hotels/styx/api/FullHttpRequestTest.java index b3f5038a7e..f8d56388fb 100644 --- a/components/api/src/test/java/com/hotels/styx/api/FullHttpRequestTest.java +++ b/components/api/src/test/java/com/hotels/styx/api/FullHttpRequestTest.java @@ -249,24 +249,6 @@ public void requestBodyCannotBeChangedViaStreamingRequest() { assertThat(original.bodyAs(UTF_8), is("original")); } - // TODO: Mikko: Styx 2.0 API: Ought to move to HttpRequest class? - @Test(expectedExceptions = io.netty.util.IllegalReferenceCountException.class) - public void toFullReqestReleasesOriginalRefCountedBuffers() throws ExecutionException, InterruptedException { - ByteBuf content = Unpooled.copiedBuffer("original", UTF_8); - - HttpRequest original = HttpRequest.get("/foo") - .body(StyxObservable.of(content)) - .build(); - - FullHttpRequest fullRequest = original.toFullRequest(100) - .asCompletableFuture() - .get(); - - content.array()[0] = 'A'; - - assertThat(fullRequest.bodyAs(UTF_8), is("original")); - } - @Test public void transformedBodyIsNewCopy() { FullHttpRequest request = get("/foo") diff --git a/components/api/src/test/java/com/hotels/styx/api/HttpRequestTest.java b/components/api/src/test/java/com/hotels/styx/api/HttpRequestTest.java index 1f619f27e7..5a7feda97f 100644 --- a/components/api/src/test/java/com/hotels/styx/api/HttpRequestTest.java +++ b/components/api/src/test/java/com/hotels/styx/api/HttpRequestTest.java @@ -17,6 +17,7 @@ import com.google.common.collect.ImmutableMap; import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -81,6 +82,23 @@ public void decodesToFullHttpRequest() throws Exception { assertThat(full.body(), is(bytes("foobar"))); } + @Test(expectedExceptions = io.netty.util.IllegalReferenceCountException.class) + public void toFullRequestReleasesOriginalReferenceCountedBuffers() throws ExecutionException, InterruptedException { + ByteBuf content = Unpooled.copiedBuffer("original", UTF_8); + + HttpRequest original = HttpRequest.get("/foo") + .body(StyxObservable.of(content)) + .build(); + + FullHttpRequest fullRequest = original.toFullRequest(100) + .asCompletableFuture() + .get(); + + content.array()[0] = 'A'; + + assertThat(fullRequest.bodyAs(UTF_8), is("original")); + } + @Test(dataProvider = "emptyBodyRequests") public void encodesToStreamingHttpRequestWithEmptyBody(HttpRequest streamingRequest) throws Exception { FullHttpRequest full = streamingRequest.toFullRequest(0x1000) @@ -95,7 +113,7 @@ public void encodesToStreamingHttpRequestWithEmptyBody(HttpRequest streamingRequ private Object[][] emptyBodyRequests() { return new Object[][]{ {get("/foo/bar").build()}, - {post("/foo/bar", StyxObservable.empty()).build()}, + {post("/foo/bar", StyxCoreObservable.empty()).build()}, }; } diff --git a/components/api/src/test/java/com/hotels/styx/api/HttpResponseTest.java b/components/api/src/test/java/com/hotels/styx/api/HttpResponseTest.java index 1d45b0a917..77623d657b 100644 --- a/components/api/src/test/java/com/hotels/styx/api/HttpResponseTest.java +++ b/components/api/src/test/java/com/hotels/styx/api/HttpResponseTest.java @@ -22,6 +22,8 @@ import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; import java.util.stream.Stream; import static com.hotels.styx.api.HttpCookie.cookie; @@ -45,7 +47,9 @@ import static com.hotels.styx.api.messages.HttpVersion.HTTP_1_0; import static com.hotels.styx.api.messages.HttpVersion.HTTP_1_1; import static com.hotels.styx.support.matchers.IsOptional.isValue; +import static java.nio.charset.StandardCharsets.UTF_16; import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.stream.Collectors.toList; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; @@ -91,7 +95,7 @@ public void encodesToFullHttpResponseWithEmptyBody(HttpResponse response) throws private Object[][] emptyBodyResponses() { return new Object[][]{ {response().build()}, - {response().body(StyxObservable.empty()).build()}, + {response().body(StyxCoreObservable.empty()).build()}, }; } @@ -310,6 +314,28 @@ public void rejectsInvalidContentLength() { .build(); } + @Test + public void encodesBodyWithCharset() throws InterruptedException, ExecutionException, TimeoutException { + StyxObservable o = StyxObservable.of("Hello, World!"); + + FullHttpResponse responseUtf8 = response() + .body(o, UTF_8) + .build() + .toFullResponse(1_000_000) + .asCompletableFuture() + .get(1, SECONDS); + + FullHttpResponse responseUtf16 = response() + .body(o, UTF_16) + .build() + .toFullResponse(1_000_000) + .asCompletableFuture() + .get(1, SECONDS); + + assertThat(responseUtf8.body(), is("Hello, World!".getBytes(UTF_8))); + assertThat(responseUtf16.body(), is("Hello, World!".getBytes(UTF_16))); + } + private static HttpResponse.Builder response() { return HttpResponse.response(); } diff --git a/components/api/src/test/java/com/hotels/styx/api/MockContext.java b/components/api/src/test/java/com/hotels/styx/api/MockContext.java index d911ab08e9..8bc58404c8 100644 --- a/components/api/src/test/java/com/hotels/styx/api/MockContext.java +++ b/components/api/src/test/java/com/hotels/styx/api/MockContext.java @@ -15,10 +15,6 @@ */ package com.hotels.styx.api; -import java.util.concurrent.CompletableFuture; -import java.util.function.BiFunction; -import java.util.function.Function; - public class MockContext implements HttpInterceptor.Context { public static final HttpInterceptor.Context MOCK_CONTEXT = new MockContext(); @@ -32,42 +28,4 @@ public void add(String key, Object value) { public T get(String key, Class clazz) { return null; } - - // TODO: Mikko: Styx 2.0 API: MockObservable support for `onError`. - static class MockObservable implements StyxObservable { - private final T value; - - MockObservable(T value) { - this.value = value; - } - - @Override - public StyxObservable map(Function transformation) { - return new MockObservable<>(transformation.apply(value)); - } - - @Override - public StyxObservable flatMap(Function> transformation) { - return transformation.apply(value); - } - - @Override - public StyxObservable reduce(BiFunction accumulator, U initialValue) { - throw new UnsupportedOperationException(); - } - - @Override - public StyxObservable onError(Function> errorHandler) { - return new MockObservable<>(value); - } - - @Override - public CompletableFuture asCompletableFuture() { - return CompletableFuture.completedFuture(this.value); - } - - public T value() { - return value; - } - } } diff --git a/components/api/src/test/java/com/hotels/styx/api/metrics/HttpErrorStatusMetricsTest.java b/components/api/src/test/java/com/hotels/styx/api/metrics/HttpErrorStatusMetricsTest.java index 093f42d01b..a0bcfc25b3 100644 --- a/components/api/src/test/java/com/hotels/styx/api/metrics/HttpErrorStatusMetricsTest.java +++ b/components/api/src/test/java/com/hotels/styx/api/metrics/HttpErrorStatusMetricsTest.java @@ -54,9 +54,9 @@ import static com.hotels.styx.api.messages.HttpResponseStatus.UNAUTHORIZED; import static com.hotels.styx.api.messages.HttpResponseStatus.UNSUPPORTED_MEDIA_TYPE; import static com.hotels.styx.api.metrics.HttpErrorStatusMetrics.formattedExceptionName; -import static com.hotels.styx.api.netty.CustomHttpResponseStatus.ORIGIN_CONNECTION_REFUSED; -import static com.hotels.styx.api.netty.CustomHttpResponseStatus.ORIGIN_CONNECTION_TIMED_OUT; -import static com.hotels.styx.api.netty.CustomHttpResponseStatus.ORIGIN_SERVER_TIMED_OUT; +import static com.hotels.styx.api.messages.CustomHttpResponseStatus.ORIGIN_CONNECTION_REFUSED; +import static com.hotels.styx.api.messages.CustomHttpResponseStatus.ORIGIN_CONNECTION_TIMED_OUT; +import static com.hotels.styx.api.messages.CustomHttpResponseStatus.ORIGIN_SERVER_TIMED_OUT; import static java.lang.System.arraycopy; import static java.util.stream.Collectors.toList; import static org.hamcrest.MatcherAssert.assertThat; diff --git a/components/client/src/test/unit/java/com/hotels/styx/client/OriginsInventoryTest.java b/components/client/src/test/unit/java/com/hotels/styx/client/OriginsInventoryTest.java index 45a4d2fa49..b9d1e1888d 100644 --- a/components/client/src/test/unit/java/com/hotels/styx/client/OriginsInventoryTest.java +++ b/components/client/src/test/unit/java/com/hotels/styx/client/OriginsInventoryTest.java @@ -262,10 +262,6 @@ public void shutsConnectionPoolForRemovedOrigin() { verify(pool1).close(); } - - - // TODO: Mikko: ignore any origin updates with wrong application-ID. - @Test public void willNotDisableOriginsNotBelongingToTheApp() { inventory.setOrigins(ORIGIN_1); diff --git a/components/client/src/test/unit/java/com/hotels/styx/client/origincommands/DisableOriginTest.java b/components/client/src/test/unit/java/com/hotels/styx/client/origincommands/DisableOriginTest.java deleted file mode 100644 index ea64f794c0..0000000000 --- a/components/client/src/test/unit/java/com/hotels/styx/client/origincommands/DisableOriginTest.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - Copyright (C) 2013-2018 Expedia Inc. - - Licensed 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. - */ -package com.hotels.styx.client.origincommands; - -import com.hotels.styx.api.Id; -import com.hotels.styx.support.EqualsTester; -import org.testng.annotations.Test; - -import static com.hotels.styx.api.Id.GENERIC_APP; -import static com.hotels.styx.api.Id.id; - -public class DisableOriginTest { - @Test - public void checkEquality() { - new EqualsTester() - .addEqualityGroup(new DisableOrigin(id("same"), id("same")), new DisableOrigin(id("same"), id("same"))) - .addEqualityGroup(disableOrigin(id("same")), disableOrigin(id("same"))) - .testEquals(); - } - - private static DisableOrigin disableOrigin(Id originId) { - return new DisableOrigin(GENERIC_APP, originId); - } -} diff --git a/components/client/src/test/unit/java/com/hotels/styx/client/origincommands/EnableOriginTest.java b/components/client/src/test/unit/java/com/hotels/styx/client/origincommands/EnableOriginTest.java deleted file mode 100644 index 2116a3b72b..0000000000 --- a/components/client/src/test/unit/java/com/hotels/styx/client/origincommands/EnableOriginTest.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - Copyright (C) 2013-2018 Expedia Inc. - - Licensed 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. - */ -package com.hotels.styx.client.origincommands; - -import com.hotels.styx.api.Id; -import com.hotels.styx.support.EqualsTester; -import org.testng.annotations.Test; - -import static com.hotels.styx.api.Id.GENERIC_APP; -import static com.hotels.styx.api.Id.id; - -public class EnableOriginTest { - @Test - public void checkEquality() { - new EqualsTester() - .addEqualityGroup(new EnableOrigin(id("same"), id("same")), new EnableOrigin(id("same"), id("same"))) - .addEqualityGroup(enableOrigin(id("same")), enableOrigin(id("same"))) - .testEquals(); - } - - private static EnableOrigin enableOrigin(Id originId) { - return new EnableOrigin(GENERIC_APP, originId); - } -} diff --git a/components/common/src/main/java/com/hotels/styx/common/CompletableFutures.java b/components/common/src/main/java/com/hotels/styx/common/CompletableFutures.java index 7113df2b3f..969c90569c 100644 --- a/components/common/src/main/java/com/hotels/styx/common/CompletableFutures.java +++ b/components/common/src/main/java/com/hotels/styx/common/CompletableFutures.java @@ -26,9 +26,8 @@ public final class CompletableFutures { private CompletableFutures() { } - // TODO: We need to substitute this in favour of Styx APIs public static CompletableFuture fromSingleObservable(Observable observable) { - final CompletableFuture future = new CompletableFuture<>(); + CompletableFuture future = new CompletableFuture<>(); observable.single().subscribe(future::complete, future::completeExceptionally); return future; } diff --git a/components/proxy/src/main/java/com/hotels/styx/proxy/BackendServicesRouter.java b/components/proxy/src/main/java/com/hotels/styx/proxy/BackendServicesRouter.java index c88d61b5d0..2c41b3e4b4 100644 --- a/components/proxy/src/main/java/com/hotels/styx/proxy/BackendServicesRouter.java +++ b/components/proxy/src/main/java/com/hotels/styx/proxy/BackendServicesRouter.java @@ -143,7 +143,6 @@ public void onChange(Registry.Changes changes) { return StyxHostHttpClient.create(backendService.id(), connectionPool.getOrigin().id(), headerConfig.originIdHeaderName(), connectionPool); }; - //TODO: origins inventory builder assumes that appId/originId tuple is unique and it will fail on metrics registration. OriginsInventory inventory = new OriginsInventory.Builder(backendService.id()) .eventBus(environment.eventBus()) .metricsRegistry(environment.metricRegistry()) diff --git a/components/proxy/src/main/java/com/hotels/styx/proxy/RouteHandlerAdapter.java b/components/proxy/src/main/java/com/hotels/styx/proxy/RouteHandlerAdapter.java index 6a6d074f9a..dc677b1519 100644 --- a/components/proxy/src/main/java/com/hotels/styx/proxy/RouteHandlerAdapter.java +++ b/components/proxy/src/main/java/com/hotels/styx/proxy/RouteHandlerAdapter.java @@ -17,11 +17,11 @@ import com.hotels.styx.api.HttpHandler; import com.hotels.styx.api.HttpInterceptor; +import com.hotels.styx.api.HttpRequest; import com.hotels.styx.api.HttpResponse; import com.hotels.styx.api.NoServiceConfiguredException; import com.hotels.styx.api.StyxObservable; import com.hotels.styx.server.HttpRouter; -import com.hotels.styx.api.HttpRequest; /** * A {@link HttpHandler} implementation. @@ -37,9 +37,6 @@ public RouteHandlerAdapter(HttpRouter router) { public StyxObservable handle(HttpRequest request, HttpInterceptor.Context context) { return router.route(request) .map(pipeline -> pipeline.handle(request, context)) - // TODO: NoServiceConfiguredException happens *after* routing. Therefore it doesn't contain - // any helpful information about the state of the router as to why service was not configured. - // It might be useful to think if there is a better way of addressing this issue. .orElse(StyxObservable.error(new NoServiceConfiguredException(request.path()))); } } 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 86ace6f222..a0b5bb9b81 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 @@ -61,6 +61,7 @@ public HttpClient createClient(BackendService backendService, OriginsInventory o .orElseGet(() -> new BusyConnectionsStrategy(originsInventory)); // TODO: Ensure that listeners are also unregistered: + // We are going to revamp how we handle origins, https://github.com/HotelsDotCom/styx/issues/197 originsInventory.addOriginsChangeListener(configuredLbStrategy); LoadBalancer loadBalancingStrategy = decorateLoadBalancer( diff --git a/components/proxy/src/main/java/com/hotels/styx/proxy/plugin/InstrumentedPlugin.java b/components/proxy/src/main/java/com/hotels/styx/proxy/plugin/InstrumentedPlugin.java index 9ed9c14f83..b687a112c8 100644 --- a/components/proxy/src/main/java/com/hotels/styx/proxy/plugin/InstrumentedPlugin.java +++ b/components/proxy/src/main/java/com/hotels/styx/proxy/plugin/InstrumentedPlugin.java @@ -18,6 +18,7 @@ import com.codahale.metrics.Meter; import com.hotels.styx.api.Environment; import com.hotels.styx.api.HttpHandler; +import com.hotels.styx.api.HttpRequest; import com.hotels.styx.api.HttpResponse; import com.hotels.styx.api.StyxObservable; import com.hotels.styx.api.messages.HttpResponseStatus; @@ -36,7 +37,6 @@ import static java.util.Objects.requireNonNull; import static org.slf4j.LoggerFactory.getLogger; import static rx.Observable.error; -import com.hotels.styx.api.HttpRequest; /** * Collects metrics on plugin. @@ -87,8 +87,6 @@ public Map adminInterfaceHandlers() { public StyxObservable intercept(HttpRequest request, Chain originalChain) { StatusRecordingChain chain = new StatusRecordingChain(originalChain); try { - // TODO: Mikko: Styx 2.0 API: - // Check this: return fromRxObservable( toRxObservable(plugin.intercept(request, chain)) .doOnNext(response -> recordStatusCode(chain, response)) diff --git a/components/proxy/src/main/java/com/hotels/styx/startup/AdminServerSetUp.java b/components/proxy/src/main/java/com/hotels/styx/startup/AdminServerSetUp.java index a9cba76885..a90a8fd174 100644 --- a/components/proxy/src/main/java/com/hotels/styx/startup/AdminServerSetUp.java +++ b/components/proxy/src/main/java/com/hotels/styx/startup/AdminServerSetUp.java @@ -28,10 +28,6 @@ private AdminServerSetUp() { } public static HttpServer createAdminServer(StyxServerComponents config) { - // This comment was originally in the class StyxServer - // TODO: Pass all backend Service Registries to AdminServerBuilder: - // - only one backendServicesRegistry is passed in to the admin interface. Instead we - // should pass all of them: return new AdminServerBuilder(config.environment()) .backendServicesRegistry((Registry) config.services().get("backendServiceRegistry")) .plugins(config.plugins()) diff --git a/components/proxy/src/main/resources/admin/dashboard/css/styx.css b/components/proxy/src/main/resources/admin/dashboard/css/styx.css index 39d6996e8e..c2739a6659 100644 --- a/components/proxy/src/main/resources/admin/dashboard/css/styx.css +++ b/components/proxy/src/main/resources/admin/dashboard/css/styx.css @@ -47,10 +47,6 @@ table { border-spacing: 0; } - -/* #TODO icons for side and error states */ -/* #TODO navigation */ - /* overrides */ body { font-family: 'Helvetica Neue', Helvetica, Arial, “Liberation Sans”, sans-serif; diff --git a/components/server/src/main/java/com/hotels/styx/server/routing/routes/ProxyToBackendRoute.java b/components/server/src/main/java/com/hotels/styx/server/routing/routes/ProxyToBackendRoute.java index 8e52650851..012ad95bb4 100644 --- a/components/server/src/main/java/com/hotels/styx/server/routing/routes/ProxyToBackendRoute.java +++ b/components/server/src/main/java/com/hotels/styx/server/routing/routes/ProxyToBackendRoute.java @@ -41,7 +41,6 @@ public static ProxyToBackendRoute proxyToBackend(HttpClient client) { @Override public StyxObservable handle(HttpRequest request, HttpInterceptor.Context context) { - // TODO: Mikko: Styx 2.0 API: return fromRxObservable(client.sendRequest(request)); } } diff --git a/docs/user-guide/basic-usage.md b/docs/user-guide/basic-usage.md index 728484df68..94df20c776 100644 --- a/docs/user-guide/basic-usage.md +++ b/docs/user-guide/basic-usage.md @@ -43,7 +43,7 @@ This creates a subdirectory called `styx-` that contains Styx binaries * `logback.xml` - Logging configuration file. There are more examples in the `conf/logback` subdirectory. -* `styx-env.sh` - JVM settings file. See section below. +* `styx-env.sh` - JVM settings file. See "Configuring JVM Settings" section below. diff --git a/docs/user-guide/metrics-reference.md b/docs/user-guide/metrics-reference.md index c3b83ac216..25b50403aa 100644 --- a/docs/user-guide/metrics-reference.md +++ b/docs/user-guide/metrics-reference.md @@ -26,8 +26,7 @@ * Total number or responses for each status code class (1xx, 2xx, ...) * Total number of responses for each error status code (code >= 400) * Total number of unrecognised status codes (`` is `unrecognised`) - -TODO: Mikko are these generated by styx, or responded by an origin? +* This metric combines statuses from origins with statuses from Styx-generated responses. **requests.received** @@ -38,8 +37,7 @@ TODO: Mikko are these generated by styx, or responded by an origin? * Meter * The rate of 500 Internal Server Error - -TODO: Mikko are these generated by styx, or responded by an origin? +* This metric combines statuses from origins with statuses from Styx-generated responses. **requests.latency** @@ -55,20 +53,15 @@ TODO: Mikko are these generated by styx, or responded by an origin? **connections.eventloop.``.registered-channel-count** * Counter -* Number of TCP connections registered against Styx IO thread, where +* Number of TCP connections registered against the Styx server IO thread, where `` is the IO thread name. -TODO: Mikko, check if this is only for the server side connections, or -also includes client side connections? - **connections.total-connections** * Counter * Total number of TCP connections active on Styx server side. * Does not count client side TCP connections. -TODO: Mikko verify the claims above! - **connections.eventloop.``.channels** diff --git a/docs/user-guide/metrics.md b/docs/user-guide/metrics.md index 3fc9236a2c..df395bb6ed 100644 --- a/docs/user-guide/metrics.md +++ b/docs/user-guide/metrics.md @@ -253,18 +253,6 @@ Here is an example configuration: * `intervalMillis` - A metrics reporting interval, in milliseconds. -## JMX Reporter - -TODO: Is this still supported, or ever used? Perhaps it is better to leave undocumented for now? - - services: - factories: - jmx: - class: "com.hotels.styx.metrics.reporting.jmx.JmxReporterServiceFactory" - config: - domain: "com.hotels.styx" - - # Styx Metrics Reference A [Styx Metrics Reference](./metrics-reference.md) has a detailed description for each metric. diff --git a/pom.xml b/pom.xml index 369bffeffb..1c814892fd 100755 --- a/pom.xml +++ b/pom.xml @@ -62,8 +62,6 @@ - - bom components diff --git a/support/api-testsupport/pom.xml b/support/api-testsupport/pom.xml index 25bda06f73..6c91d1a425 100755 --- a/support/api-testsupport/pom.xml +++ b/support/api-testsupport/pom.xml @@ -42,7 +42,6 @@ com.github.tomakehurst wiremock - compile diff --git a/support/testsupport/pom.xml b/support/testsupport/pom.xml index 81ddea27db..f18d46c473 100755 --- a/support/testsupport/pom.xml +++ b/support/testsupport/pom.xml @@ -62,7 +62,6 @@ com.github.tomakehurst wiremock - compile diff --git a/support/testsupport/src/main/java/com/hotels/styx/support/EqualsTester.java b/support/testsupport/src/main/java/com/hotels/styx/support/EqualsTester.java deleted file mode 100644 index 94c786c10a..0000000000 --- a/support/testsupport/src/main/java/com/hotels/styx/support/EqualsTester.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.hotels.styx.support; - -/* - * Copyright (C) 2007 The Guava Authors - * - * Licensed 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. - */ - - -import com.google.common.annotations.Beta; -import com.google.common.annotations.GwtCompatible; -import com.google.common.base.Equivalence; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; - -import java.util.List; - -import static com.google.common.base.Preconditions.checkNotNull; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; - -/** - * Tester for equals() and hashCode() methods of a class. - *

- *

To use, create a new EqualsTester and add equality groups where each group - * contains objects that are supposed to be equal to each other, and objects of - * different groups are expected to be unequal. For example: - *

- * new EqualsTester()
- *     .addEqualityGroup("hello", "h" + "ello")
- *     .addEqualityGroup("world", "wor" + "ld")
- *     .addEqualityGroup(2, 1 + 1)
- *     .testEquals();
- * 
- *

This tests: - *

    - *
  • comparing each object against itself returns true - *
  • comparing each object against null returns false - *
  • comparing each object against an instance of an incompatible class - * returns false - *
  • comparing each pair of objects within the same equality group returns - * true - *
  • comparing each pair of objects from different equality groups returns - * false - *
  • the hash codes of any two equal objects are equal - *
- *

- *

When a test fails, the error message labels the objects involved in - * the failed comparison as follows: - *

    - *
  • "{@code [group }i{@code , item }j{@code ]}" refers to the - * jth item in the ith equality group, - * where both equality groups and the items within equality groups are - * numbered starting from 1. When either a constructor argument or an - * equal object is provided, that becomes group 1. - *
- * - * @author Jim McMaster - * @author Jige Yu - * @since 10.0 - */ -@Beta -@GwtCompatible -public final class EqualsTester { - private static final int REPETITIONS = 3; - - private final List> equalityGroups = Lists.newArrayList(); - private final RelationshipTester.ItemReporter itemReporter; - - /** - * Constructs an empty EqualsTester instance - */ - public EqualsTester() { - this(new RelationshipTester.ItemReporter()); - } - - EqualsTester(RelationshipTester.ItemReporter itemReporter) { - this.itemReporter = checkNotNull(itemReporter); - } - - /** - * Adds {@code equalityGroup} with objects that are supposed to be equal to - * each other and not equal to any other equality groups added to this tester. - */ - public EqualsTester addEqualityGroup(Object... equalityGroup) { - checkNotNull(equalityGroup); - equalityGroups.add(ImmutableList.copyOf(equalityGroup)); - return this; - } - - /** - * Run tests on equals method, throwing a failure on an invalid test - */ - public EqualsTester testEquals() { - RelationshipTester delegate = new RelationshipTester( - Equivalence.equals(), "Object#equals", "Object#hashCode", itemReporter); - for (List group : equalityGroups) { - delegate.addRelatedGroup(group); - } - for (int run = 0; run < REPETITIONS; run++) { - testItems(); - delegate.test(); - } - return this; - } - - private void testItems() { - for (Object item : Iterables.concat(equalityGroups)) { - assertThat(item + " must not be Object#equals to null", !item.equals(null)); - assertThat(item + " must not be Object#equals to an arbitrary object of another class", - !item.equals(NotAnInstance.EQUAL_TO_NOTHING)); - assertThat(item + " must be Object#equals to itself", item, is(item)); - assertThat("the Object#hashCode of " + item + " must be consistent", item.hashCode(), is(item.hashCode())); - } - } - - /** - * Class used to test whether equals() correctly handles an instance - * of an incompatible class. Since it is a private inner class, the - * invoker can never pass in an instance to the tester - */ - private enum NotAnInstance { - EQUAL_TO_NOTHING; - } -} diff --git a/support/testsupport/src/main/java/com/hotels/styx/support/RelationshipTester.java b/support/testsupport/src/main/java/com/hotels/styx/support/RelationshipTester.java deleted file mode 100644 index 497b0fb97d..0000000000 --- a/support/testsupport/src/main/java/com/hotels/styx/support/RelationshipTester.java +++ /dev/null @@ -1,157 +0,0 @@ -package com.hotels.styx.support; - -/* - * Copyright (C) 2011 The Guava Authors - * - * Licensed 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. - */ - - -import com.google.common.annotations.GwtCompatible; -import com.google.common.base.Equivalence; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; - -import java.util.List; - -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * Implementation helper for {@link EqualsTester} and {@link EquivalenceTester} that tests for - * equivalence classes. - * - * @author Gregory Kick - */ -@GwtCompatible -final class RelationshipTester { - - static class ItemReporter { - String reportItem(Item item) { - return item.toString(); - } - } - - /** - * A word about using {@link Equivalence}, which automatically checks for {@code null} and - * identical inputs: This sounds like it ought to be a problem here, since the goals of this class - * include testing that {@code equals()} is reflexive and is tolerant of {@code null}. However, - * there's no problem. The reason: {@link EqualsTester} tests {@code null} and identical inputs - * directly against {@code equals()} rather than through the {@code Equivalence}. - */ - private final Equivalence equivalence; - private final String relationshipName; - private final String hashName; - private final ItemReporter itemReporter; - private final List> groups = Lists.newArrayList(); - - RelationshipTester(Equivalence equivalence, String relationshipName, String hashName, - ItemReporter itemReporter) { - this.equivalence = checkNotNull(equivalence); - this.relationshipName = checkNotNull(relationshipName); - this.hashName = checkNotNull(hashName); - this.itemReporter = checkNotNull(itemReporter); - } - - // TODO(cpovirk): should we reject null items, since the tests already check null automatically? - public RelationshipTester addRelatedGroup(Iterable group) { - groups.add(ImmutableList.copyOf(group)); - return this; - } - - public void test() { - for (int groupNumber = 0; groupNumber < groups.size(); groupNumber++) { - ImmutableList group = groups.get(groupNumber); - for (int itemNumber = 0; itemNumber < group.size(); itemNumber++) { - // check related items in same group - for (int relatedItemNumber = 0; relatedItemNumber < group.size(); relatedItemNumber++) { - if (itemNumber != relatedItemNumber) { - assertRelated(groupNumber, itemNumber, relatedItemNumber); - } - } - // check unrelated items in all other groups - for (int unrelatedGroupNumber = 0; unrelatedGroupNumber < groups.size(); - unrelatedGroupNumber++) { - if (groupNumber != unrelatedGroupNumber) { - ImmutableList unrelatedGroup = groups.get(unrelatedGroupNumber); - for (int unrelatedItemNumber = 0; unrelatedItemNumber < unrelatedGroup.size(); - unrelatedItemNumber++) { - assertUnrelated(groupNumber, itemNumber, unrelatedGroupNumber, unrelatedItemNumber); - } - } - } - } - } - } - - private void assertRelated(int groupNumber, int itemNumber, int relatedItemNumber) { - Item itemInfo = getItem(groupNumber, itemNumber); - Item relatedInfo = getItem(groupNumber, relatedItemNumber); - - T item = itemInfo.value; - T related = relatedInfo.value; - assertWithTemplate("$ITEM must be $RELATIONSHIP to $OTHER", itemInfo, relatedInfo, - equivalence.equivalent(item, related)); - - int itemHash = equivalence.hash(item); - int relatedHash = equivalence.hash(related); - assertWithTemplate("the $HASH (" + itemHash + ") of $ITEM must be equal to the $HASH (" - + relatedHash + ") of $OTHER", itemInfo, relatedInfo, itemHash == relatedHash); - } - - private void assertUnrelated(int groupNumber, int itemNumber, int unrelatedGroupNumber, - int unrelatedItemNumber) { - Item itemInfo = getItem(groupNumber, itemNumber); - Item unrelatedInfo = getItem(unrelatedGroupNumber, unrelatedItemNumber); - - assertWithTemplate("$ITEM must not be $RELATIONSHIP to $OTHER", itemInfo, unrelatedInfo, - !equivalence.equivalent(itemInfo.value, unrelatedInfo.value)); - } - - private void assertWithTemplate(String template, Item item, Item other, boolean condition) { - if (!condition) { - throw new AssertionError(template - .replace("$RELATIONSHIP", relationshipName) - .replace("$HASH", hashName) - .replace("$ITEM", itemReporter.reportItem(item)) - .replace("$OTHER", itemReporter.reportItem(other))); - } - } - - private Item getItem(int groupNumber, int itemNumber) { - return new Item(groups.get(groupNumber).get(itemNumber), groupNumber, itemNumber); - } - - static final class Item { - final T value; - final int groupNumber; - final int itemNumber; - - Item(T value, int groupNumber, int itemNumber) { - this.value = value; - this.groupNumber = groupNumber; - this.itemNumber = itemNumber; - } - - @Override - public String toString() { - return new StringBuilder() - .append(value) - .append(" [group ") - .append(groupNumber + 1) - .append(", item ") - .append(itemNumber + 1) - .append(']') - .toString(); - } - } -} diff --git a/system-tests/e2e-suite/src/test/java/com/hotels/styx/plugins/ContentDecodeFailurePlugin.java b/system-tests/e2e-suite/src/test/java/com/hotels/styx/plugins/ContentDecodeFailurePlugin.java index c9e1c10711..77822c9ee1 100644 --- a/system-tests/e2e-suite/src/test/java/com/hotels/styx/plugins/ContentDecodeFailurePlugin.java +++ b/system-tests/e2e-suite/src/test/java/com/hotels/styx/plugins/ContentDecodeFailurePlugin.java @@ -45,8 +45,7 @@ public StyxObservable intercept(HttpRequest request, Chain chain) .map(FullHttpResponse::toStreamingResponse); } - // TODO: Mikko: Styx 2.0 Api: - // Can we still test these types of scenarios? + // TODO: See https://github.com/HotelsDotCom/styx/issues/202 private Function decodeOrFailOperation(HttpRequest request) { return (byteBuf) -> { if (request.header("Fail_during_decoder").isPresent()) { diff --git a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/plugins/PluginErrorHandlingSpec.scala b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/plugins/PluginErrorHandlingSpec.scala index 0a03097acc..fc3f2a3fe4 100644 --- a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/plugins/PluginErrorHandlingSpec.scala +++ b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/plugins/PluginErrorHandlingSpec.scala @@ -34,7 +34,7 @@ class PluginErrorHandlingSpec extends FunSpec val normalBackend = FakeHttpServer.HttpStartupConfig().start() - // TODO: Mikko: Styx 2.0 API: Can we e2e test content decode failues any more? Does it make sense to try to e2e test them? + // TODO: See https://github.com/HotelsDotCom/styx/issues/202 override val styxConfig = StyxConfig(plugins = List( "failBeforeInterceptor" -> new FailBeforeHandleInterceptor(), @@ -90,7 +90,7 @@ class PluginErrorHandlingSpec extends FunSpec } } - // TODO: Mikko: Styx 2.0 Api: Probably not possible to test under the new API. + // TODO: See https://github.com/HotelsDotCom/styx/issues/202 ignore("Catches exceptions from plugins performing content decoding, and maps them to INTERNAL_SERVER_ERRORs") { for (i <- 1 to 2) { val request = get(styxServer.routerURL("/foo")) diff --git a/system-tests/performance/scripts/load-test-tool/load_test.py b/system-tests/performance/scripts/load-test-tool/load_test.py index cf9addd142..69f410fae3 100644 --- a/system-tests/performance/scripts/load-test-tool/load_test.py +++ b/system-tests/performance/scripts/load-test-tool/load_test.py @@ -1,5 +1,5 @@ # -# Copyright (C) 2013-2017 Expedia Inc. +# Copyright (C) 2013-2018 Expedia Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/system-tests/performance/scripts/load-test-tool/wrk.py b/system-tests/performance/scripts/load-test-tool/wrk.py index 872573e79a..9791f67f3a 100644 --- a/system-tests/performance/scripts/load-test-tool/wrk.py +++ b/system-tests/performance/scripts/load-test-tool/wrk.py @@ -1,5 +1,5 @@ # -# Copyright (C) 2013-2017 Expedia Inc. +# Copyright (C) 2013-2018 Expedia Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License.