From 293814dec9f6f33d80b94ba16f404c899a8c4b5e Mon Sep 17 00:00:00 2001 From: Mikko Karjalainen Date: Wed, 22 May 2019 11:27:53 +0100 Subject: [PATCH 1/5] Add WebServiceHandler interface. Convert admin interface to use the new WebServiceHandler. Refactor MetricsHandler. --- .../hotels/styx/api/WebServiceHandler.java | 31 ++++ .../common/http/handler/BaseHttpHandler.java | 15 +- .../http/handler/CachedBodyHttpHandler.java | 122 ------------- .../common/http/handler/HttpAggregator.java | 64 +++++++ .../handler/HttpMethodFilteringHandler.java | 20 +-- .../common/http/handler/HttpStreamer.java | 61 +++++++ .../common/http/handler/NotFoundHandler.java | 15 +- .../http/handler/StaticBodyHttpHandler.java | 11 +- .../http/handler/HttpAggregatorTest.java | 58 +++++++ .../HttpMethodFilteringHandlerTest.java | 20 +-- .../common/http/handler/HttpStreamerTest.java | 51 ++++++ .../handler/StaticBodyHttpHandlerTest.java | 16 +- .../hotels/styx/admin/AdminServerBuilder.java | 130 +++++++------- .../handlers/CurrentRequestsHandler.java | 11 +- .../styx/admin/handlers/IndexHandler.java | 14 +- .../styx/admin/handlers/JsonHandler.java | 19 +-- .../handlers/LoggingConfigurationHandler.java | 17 +- .../styx/admin/handlers/MetricsHandler.java | 123 ++++++------- .../styx/admin/handlers/OriginsHandler.java | 25 ++- .../handlers/OriginsInventoryHandler.java | 28 +-- .../styx/admin/handlers/PingHandler.java | 14 +- .../admin/handlers/PluginListHandler.java | 13 +- .../admin/handlers/PluginToggleHandler.java | 141 ++++++++------- .../admin/handlers/RoutingObjectHandler.java | 30 ++-- .../handlers/StyxConfigurationHandler.java | 28 +-- .../styx/admin/handlers/ThreadsHandler.java | 11 +- .../styx/admin/handlers/UrlPatternRouter.java | 26 +-- .../admin/tasks/OriginsCommandHandler.java | 27 ++- .../tasks/OriginsReloadCommandHandler.java | 29 ++-- .../handlers/CurrentRequestsHandlerTest.java | 19 ++- .../styx/admin/handlers/IndexHandlerTest.java | 15 +- .../admin/handlers/JVMMetricsHandlerTest.java | 27 ++- .../styx/admin/handlers/JsonHandlerTest.java | 11 +- .../LoggingConfigurationHandlerTest.java | 10 +- .../admin/handlers/MetricsHandlerTest.java | 18 +- .../admin/handlers/OriginsHandlerTest.java | 12 +- .../handlers/OriginsInventoryHandlerTest.java | 14 +- .../styx/admin/handlers/PingHandlerTest.java | 8 +- .../admin/handlers/PluginListHandlerTest.java | 6 +- .../handlers/PluginToggleHandlerTest.java | 36 ++-- .../handlers/StartupConfigHandlerTest.java | 8 +- .../StyxConfigurationHandlerTest.java | 51 +++--- .../admin/handlers/ThreadsHandlerTest.java | 8 +- .../handlers/VersionTextHandlerTest.java | 14 +- .../tasks/OriginsCommandHandlerTest.java | 43 +++-- .../OriginsReloadCommandHandlerTest.java | 16 +- .../com/hotels/styx/proxy/StyxProxyTest.java | 10 +- .../admin/handlers/UrlPatternRouterTest.kt | 161 +++++++++--------- .../com/hotels/styx/server/HttpServers.java | 2 +- .../styx/server/StandardHttpRouter.java | 19 ++- .../handlers/ClassPathResourceHandler.java | 38 ++--- .../handlers/ReturnResponseHandler.java | 50 ------ .../server/handlers/StaticFileHandler.java | 5 +- .../ClassPathResourceHandlerTest.java | 20 +-- .../styx/support/api/BlockingObservables.java | 48 ------ .../styx/support/origins/AppHandler.java | 14 +- .../origins/StyxOriginsStarterApp.java | 6 +- .../scala/com/hotels/styx/MockServer.scala | 8 +- .../plugins/PluginAdminInterfaceSpec.scala | 14 +- .../hotels/styx/proxy/ChunkedUploadSpec.scala | 6 +- .../utils/handlers/ContentDigestHandler.java | 14 +- 61 files changed, 953 insertions(+), 948 deletions(-) create mode 100644 components/api/src/main/java/com/hotels/styx/api/WebServiceHandler.java delete mode 100644 components/common/src/main/java/com/hotels/styx/common/http/handler/CachedBodyHttpHandler.java create mode 100644 components/common/src/main/java/com/hotels/styx/common/http/handler/HttpAggregator.java create mode 100644 components/common/src/main/java/com/hotels/styx/common/http/handler/HttpStreamer.java create mode 100644 components/common/src/test/java/com/hotels/styx/common/http/handler/HttpAggregatorTest.java create mode 100644 components/common/src/test/java/com/hotels/styx/common/http/handler/HttpStreamerTest.java delete mode 100644 components/server/src/main/java/com/hotels/styx/server/handlers/ReturnResponseHandler.java delete mode 100644 support/api-testsupport/src/main/java/com/hotels/styx/support/api/BlockingObservables.java diff --git a/components/api/src/main/java/com/hotels/styx/api/WebServiceHandler.java b/components/api/src/main/java/com/hotels/styx/api/WebServiceHandler.java new file mode 100644 index 0000000000..03a9c5b61c --- /dev/null +++ b/components/api/src/main/java/com/hotels/styx/api/WebServiceHandler.java @@ -0,0 +1,31 @@ +/* + Copyright (C) 2013-2019 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.api; + +/** + * A web service handler that handles a {@link HttpRequest}, returning an {@link Eventual} that is expected to publish + * a single {@link HttpResponse} value. + */ +@FunctionalInterface +public interface WebServiceHandler { + /** + * Processes an incoming request. + * + * @param request the current incoming request + * @return an {@link Eventual} that is expected to publish a single response + */ + Eventual handle(HttpRequest request, HttpInterceptor.Context context); +} diff --git a/components/common/src/main/java/com/hotels/styx/common/http/handler/BaseHttpHandler.java b/components/common/src/main/java/com/hotels/styx/common/http/handler/BaseHttpHandler.java index 21fe7cdb99..7e86122351 100644 --- a/components/common/src/main/java/com/hotels/styx/common/http/handler/BaseHttpHandler.java +++ b/components/common/src/main/java/com/hotels/styx/common/http/handler/BaseHttpHandler.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,18 +18,19 @@ import com.hotels.styx.api.Eventual; import com.hotels.styx.api.HttpHandler; import com.hotels.styx.api.HttpInterceptor; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; +import com.hotels.styx.api.HttpRequest; +import com.hotels.styx.api.HttpResponse; +import com.hotels.styx.api.WebServiceHandler; /** * This class provides a skeleton implementation of the {@link HttpHandler} interface, that can be used when no * complex {@link Eventual} mechanism is required. */ -public abstract class BaseHttpHandler implements HttpHandler { +public abstract class BaseHttpHandler implements WebServiceHandler { @Override - public Eventual handle(LiveHttpRequest request, HttpInterceptor.Context context) { - return Eventual.of(doHandle(request)); + public Eventual handle(HttpRequest request, HttpInterceptor.Context context) { + return Eventual.of(doHandle(request, context)); } /** @@ -38,5 +39,5 @@ public Eventual handle(LiveHttpRequest request, HttpIntercepto * @param request request * @return response */ - protected abstract LiveHttpResponse doHandle(LiveHttpRequest request); + protected abstract HttpResponse doHandle(HttpRequest request, HttpInterceptor.Context context); } diff --git a/components/common/src/main/java/com/hotels/styx/common/http/handler/CachedBodyHttpHandler.java b/components/common/src/main/java/com/hotels/styx/common/http/handler/CachedBodyHttpHandler.java deleted file mode 100644 index a63ab942ab..0000000000 --- a/components/common/src/main/java/com/hotels/styx/common/http/handler/CachedBodyHttpHandler.java +++ /dev/null @@ -1,122 +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.common.http.handler; - -import com.google.common.net.MediaType; -import com.hotels.styx.api.HttpResponse; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; - -import java.nio.charset.Charset; -import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; - -import static com.google.common.base.Suppliers.memoizeWithExpiration; -import static com.google.common.net.HttpHeaders.CONTENT_LENGTH; -import static com.google.common.net.HttpHeaders.CONTENT_TYPE; -import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8; -import static com.hotels.styx.api.HttpResponseStatus.OK; -import static java.nio.charset.StandardCharsets.UTF_8; -import static java.util.Objects.requireNonNull; -import static java.util.concurrent.TimeUnit.SECONDS; - -/** - * HTTP handler that responds with a static body. - */ -public class CachedBodyHttpHandler extends BaseHttpHandler { - private final String contentType; - private final Supplier bodySupplier; - - /** - * Constructs a new instance with a configurable charset. - * - * @param builder builder - */ - public CachedBodyHttpHandler(Builder builder) { - this.contentType = builder.contentType.toString(); - - // extracted externally because we don't want the lambda to have a reference to the builder (which is mutable). - Supplier contentSupplier = builder.contentSupplier; - Charset charset = builder.charset; - Supplier bodySupplier = () -> new Body(contentSupplier.get(), charset); - - this.bodySupplier = memoizeWithExpiration(bodySupplier::get, builder.expiration, builder.expirationUnit)::get; - } - - @Override - public LiveHttpResponse doHandle(LiveHttpRequest request) { - return bodySupplier.get().toResponse(); - } - - private class Body { - private final String content; - private final int contentLength; - - Body(String content, Charset charset) { - this.content = content; - this.contentLength = content.getBytes(charset).length; - } - - LiveHttpResponse toResponse() { - return HttpResponse.response(OK) - .header(CONTENT_TYPE, contentType) - .header(CONTENT_LENGTH, contentLength) - .body(content, UTF_8) - .build() - .stream(); - } - } - - /** - * A builder for CachedBodyHttpHandler class. - */ - public static final class Builder { - private final Supplier contentSupplier; - - private MediaType contentType = PLAIN_TEXT_UTF_8; - private Charset charset = UTF_8; - private long expiration = 1; - private TimeUnit expirationUnit = SECONDS; - - public Builder(Supplier contentSupplier) { - this.contentSupplier = requireNonNull(contentSupplier); - } - - public Builder contentType(MediaType contentType) { - this.contentType = requireNonNull(contentType); - return this; - } - - public Builder charset(Charset charset) { - this.charset = requireNonNull(charset); - return this; - } - - public Builder expiration(long expiration) { - this.expiration = requireNonNull(expiration); - return this; - } - - public Builder expirationUnit(TimeUnit expirationUnit) { - this.expirationUnit = requireNonNull(expirationUnit); - return this; - } - - public CachedBodyHttpHandler build() { - return new CachedBodyHttpHandler(this); - } - } -} diff --git a/components/common/src/main/java/com/hotels/styx/common/http/handler/HttpAggregator.java b/components/common/src/main/java/com/hotels/styx/common/http/handler/HttpAggregator.java new file mode 100644 index 0000000000..ebcd444df5 --- /dev/null +++ b/components/common/src/main/java/com/hotels/styx/common/http/handler/HttpAggregator.java @@ -0,0 +1,64 @@ +/* + Copyright (C) 2013-2019 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.common.http.handler; + +import com.hotels.styx.api.Eventual; +import com.hotels.styx.api.HttpHandler; +import com.hotels.styx.api.HttpInterceptor; +import com.hotels.styx.api.HttpResponse; +import com.hotels.styx.api.LiveHttpRequest; +import com.hotels.styx.api.LiveHttpResponse; +import com.hotels.styx.api.WebServiceHandler; + +import static java.util.Objects.requireNonNull; + +/** + * Adapts a static WebServiceHandler to streaming HttpHandler interface. + */ +public class HttpAggregator implements HttpHandler { + + private static final int KILOBYTE = 1024; + + private final WebServiceHandler delegate; + private final int bytes; + + /** + * HttpAggregator Constructor. + * + * @param bytes max number of bytes to aggregate + * @param delegate adapted WebServiceHandler endpoint + */ + public HttpAggregator(int bytes, WebServiceHandler delegate) { + this.delegate = requireNonNull(delegate); + this.bytes = bytes; + } + + /** + * HttpAggregator constructor. + * + * @param delegate adapted WebServiceHandler endpoint + */ + public HttpAggregator(WebServiceHandler delegate) { + this(120 * KILOBYTE, delegate); + } + + @Override + public Eventual handle(LiveHttpRequest request, HttpInterceptor.Context context) { + return request.aggregate(bytes) + .flatMap(aggregated -> this.delegate.handle(aggregated, context)) + .map(HttpResponse::stream); + } +} diff --git a/components/common/src/main/java/com/hotels/styx/common/http/handler/HttpMethodFilteringHandler.java b/components/common/src/main/java/com/hotels/styx/common/http/handler/HttpMethodFilteringHandler.java index 12708dc13b..2ed18d99dd 100644 --- a/components/common/src/main/java/com/hotels/styx/common/http/handler/HttpMethodFilteringHandler.java +++ b/components/common/src/main/java/com/hotels/styx/common/http/handler/HttpMethodFilteringHandler.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,13 +15,12 @@ */ package com.hotels.styx.common.http.handler; -import com.hotels.styx.api.HttpResponse; -import com.hotels.styx.api.HttpHandler; -import com.hotels.styx.api.HttpInterceptor; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; import com.hotels.styx.api.Eventual; +import com.hotels.styx.api.HttpInterceptor; import com.hotels.styx.api.HttpMethod; +import com.hotels.styx.api.HttpRequest; +import com.hotels.styx.api.HttpResponse; +import com.hotels.styx.api.WebServiceHandler; import java.nio.charset.StandardCharsets; @@ -33,25 +32,24 @@ * A handler that checks whether incoming messages have the expected HTTP method. If the method is correct, this handler * delegates to its child handler. Otherwise, it responds with a 405 error. */ -public class HttpMethodFilteringHandler implements HttpHandler { +public class HttpMethodFilteringHandler implements WebServiceHandler { private final HttpMethod method; - private final HttpHandler httpHandler; + private final WebServiceHandler httpHandler; private final String errorBody; - public HttpMethodFilteringHandler(HttpMethod method, HttpHandler httpHandler) { + public HttpMethodFilteringHandler(HttpMethod method, WebServiceHandler httpHandler) { this.method = requireNonNull(method); this.httpHandler = requireNonNull(httpHandler); this.errorBody = format("%s. Only [%s] is allowed for this request.", METHOD_NOT_ALLOWED.description(), method); } @Override - public Eventual handle(LiveHttpRequest request, HttpInterceptor.Context context) { + public Eventual handle(HttpRequest request, HttpInterceptor.Context context) { if (!method.equals(request.method())) { return Eventual.of( HttpResponse.response(METHOD_NOT_ALLOWED) .body(errorBody, StandardCharsets.UTF_8) .build() - .stream() ); } diff --git a/components/common/src/main/java/com/hotels/styx/common/http/handler/HttpStreamer.java b/components/common/src/main/java/com/hotels/styx/common/http/handler/HttpStreamer.java new file mode 100644 index 0000000000..03dd7ce236 --- /dev/null +++ b/components/common/src/main/java/com/hotels/styx/common/http/handler/HttpStreamer.java @@ -0,0 +1,61 @@ +/* + Copyright (C) 2013-2019 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.common.http.handler; + +import com.hotels.styx.api.Eventual; +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.WebServiceHandler; + +import static java.util.Objects.requireNonNull; + +/** + * Adapts Streaming HttpHandler API to STATIC WebServiceHandler interface. + */ +public class HttpStreamer implements WebServiceHandler { + private static final int KILOBYTE = 1024; + + private int bytes; + private HttpHandler delegate; + + /** + * HttpStreamer constructor. + * + * @param bytes max number of content bytes to aggregate + * @param delegate adapted HttpHandler instance + */ + public HttpStreamer(int bytes, HttpHandler delegate) { + this.bytes = bytes; + this.delegate = requireNonNull(delegate); + } + + /** + * HttpStreamer constructor. + * + * @param delegate adapted HttpHandler instance + */ + public HttpStreamer(HttpHandler delegate) { + this(125 * KILOBYTE, delegate); + } + + @Override + public Eventual handle(HttpRequest request, HttpInterceptor.Context context) { + return delegate.handle(request.stream(), context) + .flatMap(live -> live.aggregate(bytes)); + } +} diff --git a/components/common/src/main/java/com/hotels/styx/common/http/handler/NotFoundHandler.java b/components/common/src/main/java/com/hotels/styx/common/http/handler/NotFoundHandler.java index e9b29dae7b..dbbe0d93de 100644 --- a/components/common/src/main/java/com/hotels/styx/common/http/handler/NotFoundHandler.java +++ b/components/common/src/main/java/com/hotels/styx/common/http/handler/NotFoundHandler.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,10 +15,10 @@ */ package com.hotels.styx.common.http.handler; +import com.hotels.styx.api.HttpInterceptor; +import com.hotels.styx.api.HttpRequest; import com.hotels.styx.api.HttpResponse; -import com.hotels.styx.api.HttpHandler; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; +import com.hotels.styx.api.WebServiceHandler; import static com.hotels.styx.api.HttpResponseStatus.NOT_FOUND; import static java.nio.charset.StandardCharsets.UTF_8; @@ -27,7 +27,7 @@ * Returns a 404 Not Found response. */ public class NotFoundHandler extends BaseHttpHandler { - public static final HttpHandler NOT_FOUND_HANDLER = new NotFoundHandler(); + public static final WebServiceHandler NOT_FOUND_HANDLER = new NotFoundHandler(); private static final String NOT_FOUND_MESSAGE = "\n" + "\n" @@ -39,10 +39,9 @@ public class NotFoundHandler extends BaseHttpHandler { + "

The requested URL was not found on this server.\n"; @Override - public LiveHttpResponse doHandle(LiveHttpRequest request) { + public HttpResponse doHandle(HttpRequest request, HttpInterceptor.Context context) { return HttpResponse.response(NOT_FOUND) .body(NOT_FOUND_MESSAGE, UTF_8) - .build() - .stream(); + .build(); } } diff --git a/components/common/src/main/java/com/hotels/styx/common/http/handler/StaticBodyHttpHandler.java b/components/common/src/main/java/com/hotels/styx/common/http/handler/StaticBodyHttpHandler.java index ecbda53efb..717a70b714 100644 --- a/components/common/src/main/java/com/hotels/styx/common/http/handler/StaticBodyHttpHandler.java +++ b/components/common/src/main/java/com/hotels/styx/common/http/handler/StaticBodyHttpHandler.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,9 +17,9 @@ import com.google.common.base.Charsets; import com.google.common.net.MediaType; +import com.hotels.styx.api.HttpInterceptor; +import com.hotels.styx.api.HttpRequest; import com.hotels.styx.api.HttpResponse; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; @@ -61,12 +61,11 @@ public StaticBodyHttpHandler(MediaType contentType, String body, Charset charset } @Override - public LiveHttpResponse doHandle(LiveHttpRequest request) { + public HttpResponse doHandle(HttpRequest request, HttpInterceptor.Context context) { return HttpResponse.response(OK) .header(CONTENT_TYPE, this.contentType.toString()) .header(CONTENT_LENGTH, this.contentLength) .body(this.body, StandardCharsets.UTF_8) - .build() - .stream(); + .build(); } } diff --git a/components/common/src/test/java/com/hotels/styx/common/http/handler/HttpAggregatorTest.java b/components/common/src/test/java/com/hotels/styx/common/http/handler/HttpAggregatorTest.java new file mode 100644 index 0000000000..198c2bfe46 --- /dev/null +++ b/components/common/src/test/java/com/hotels/styx/common/http/handler/HttpAggregatorTest.java @@ -0,0 +1,58 @@ +/* + Copyright (C) 2013-2019 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.common.http.handler; + +import com.hotels.styx.api.ByteStream; +import com.hotels.styx.api.Eventual; +import com.hotels.styx.api.LiveHttpRequest; +import com.hotels.styx.api.LiveHttpResponse; +import com.hotels.styx.api.WebServiceHandler; +import com.hotels.styx.server.HttpInterceptorContext; +import org.testng.annotations.Test; +import reactor.core.publisher.Mono; + +import java.util.concurrent.atomic.AtomicReference; + +import static com.hotels.styx.api.HttpResponse.response; +import static com.hotels.styx.api.HttpResponseStatus.OK; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class HttpAggregatorTest { + @Test + public void aggregatesRequestAndStreamsResponses() { + AtomicReference result = new AtomicReference<>(); + + WebServiceHandler webServiceHandler = (request, ctx) -> { + result.set(request.bodyAs(UTF_8)); + return Eventual.of(response(OK) + .body("abcdef", UTF_8) + .build()); + }; + + LiveHttpResponse response = Mono.from( + new HttpAggregator(500, webServiceHandler) + .handle(LiveHttpRequest.post("/") + .body(ByteStream.from("ABCDEF", UTF_8)) + .build(), + HttpInterceptorContext.create())) + .block(); + + assertThat(result.get(), is("ABCDEF")); + assertThat(Mono.from(response.aggregate(500)).block().bodyAs(UTF_8), is("abcdef")); + } +} diff --git a/components/common/src/test/java/com/hotels/styx/common/http/handler/HttpMethodFilteringHandlerTest.java b/components/common/src/test/java/com/hotels/styx/common/http/handler/HttpMethodFilteringHandlerTest.java index c91e65d09d..cb7cabff30 100644 --- a/components/common/src/test/java/com/hotels/styx/common/http/handler/HttpMethodFilteringHandlerTest.java +++ b/components/common/src/test/java/com/hotels/styx/common/http/handler/HttpMethodFilteringHandlerTest.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,18 +15,18 @@ */ package com.hotels.styx.common.http.handler; -import com.hotels.styx.api.HttpHandler; import com.hotels.styx.api.HttpInterceptor; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; +import com.hotels.styx.api.HttpRequest; +import com.hotels.styx.api.HttpResponse; +import com.hotels.styx.api.WebServiceHandler; import com.hotels.styx.server.HttpInterceptorContext; import org.testng.annotations.Test; import reactor.core.publisher.Mono; import static com.hotels.styx.api.HttpMethod.GET; import static com.hotels.styx.api.HttpMethod.POST; +import static com.hotels.styx.api.HttpRequest.post; import static com.hotels.styx.api.HttpResponseStatus.METHOD_NOT_ALLOWED; -import static com.hotels.styx.api.LiveHttpRequest.post; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.mockito.Matchers.any; @@ -37,10 +37,10 @@ public class HttpMethodFilteringHandlerTest { @Test public void delegatesTheRequestIfRequestMethodIsSupported() { - HttpHandler handler = mock(HttpHandler.class); + WebServiceHandler handler = mock(WebServiceHandler.class); HttpMethodFilteringHandler post = new HttpMethodFilteringHandler(POST, handler); - LiveHttpRequest request = post("/some-uri").build(); + HttpRequest request = post("/some-uri").build(); post.handle(request, mock(HttpInterceptor.Context.class)); verify(handler).handle(eq(request), any(HttpInterceptor.Context.class)); @@ -48,11 +48,11 @@ public void delegatesTheRequestIfRequestMethodIsSupported() { @Test public void failsIfRequestMethodIsNotSupported() throws Exception { - HttpHandler handler = mock(HttpHandler.class); + WebServiceHandler handler = mock(WebServiceHandler.class); HttpMethodFilteringHandler post = new HttpMethodFilteringHandler(GET, handler); - LiveHttpRequest request = post("/some-uri").build(); - LiveHttpResponse response = Mono.from(post.handle(request, HttpInterceptorContext.create())).block(); + HttpRequest request = post("/some-uri").build(); + HttpResponse response = Mono.from(post.handle(request, HttpInterceptorContext.create())).block(); assertThat(response.status(), is(METHOD_NOT_ALLOWED)); } diff --git a/components/common/src/test/java/com/hotels/styx/common/http/handler/HttpStreamerTest.java b/components/common/src/test/java/com/hotels/styx/common/http/handler/HttpStreamerTest.java new file mode 100644 index 0000000000..084d1f9cf4 --- /dev/null +++ b/components/common/src/test/java/com/hotels/styx/common/http/handler/HttpStreamerTest.java @@ -0,0 +1,51 @@ +/* + Copyright (C) 2013-2019 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.common.http.handler; + +import com.hotels.styx.api.ByteStream; +import com.hotels.styx.api.Eventual; +import com.hotels.styx.api.HttpHandler; +import com.hotels.styx.api.HttpRequest; +import com.hotels.styx.api.HttpResponse; +import com.hotels.styx.api.LiveHttpResponse; +import com.hotels.styx.server.HttpInterceptorContext; +import org.testng.annotations.Test; +import reactor.core.publisher.Mono; + +import static com.hotels.styx.api.HttpResponseStatus.OK; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class HttpStreamerTest { + @Test + public void streamsRequestAndAggregatesResponses() { + HttpHandler httpHandler = (request, ctx) -> Eventual.of( + LiveHttpResponse.response(OK) + .body(ByteStream.from("abcdef", UTF_8)) + .build()); + + HttpResponse response = Mono.from( + new HttpStreamer(500, httpHandler) + .handle(HttpRequest.post("/") + .body("ABCDEF", UTF_8) + .build(), + HttpInterceptorContext.create())) + .block(); + + assertThat(response.bodyAs(UTF_8), is("abcdef")); + } +} \ No newline at end of file diff --git a/components/common/src/test/java/com/hotels/styx/common/http/handler/StaticBodyHttpHandlerTest.java b/components/common/src/test/java/com/hotels/styx/common/http/handler/StaticBodyHttpHandlerTest.java index e5ce061942..c06adba5a1 100644 --- a/components/common/src/test/java/com/hotels/styx/common/http/handler/StaticBodyHttpHandlerTest.java +++ b/components/common/src/test/java/com/hotels/styx/common/http/handler/StaticBodyHttpHandlerTest.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,14 +17,13 @@ import com.hotels.styx.api.HttpResponse; -import com.hotels.styx.api.LiveHttpResponse; import com.hotels.styx.server.HttpInterceptorContext; import org.testng.annotations.Test; import reactor.core.publisher.Mono; import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8; +import static com.hotels.styx.api.HttpRequest.get; import static com.hotels.styx.api.HttpResponseStatus.OK; -import static com.hotels.styx.api.LiveHttpRequest.get; import static com.hotels.styx.support.matchers.IsOptional.isValue; import static java.nio.charset.StandardCharsets.UTF_8; import static org.hamcrest.MatcherAssert.assertThat; @@ -35,13 +34,12 @@ public class StaticBodyHttpHandlerTest { public void respondsWithStaticBody() { StaticBodyHttpHandler handler = new StaticBodyHttpHandler(PLAIN_TEXT_UTF_8, "foo", UTF_8); - LiveHttpResponse response = Mono.from(handler.handle(get("/").build(), HttpInterceptorContext.create())).block(); - HttpResponse fullResponse = Mono.from(response.aggregate(1024)).block(); + HttpResponse response = Mono.from(handler.handle(get("/").build(), HttpInterceptorContext.create())).block(); - assertThat(fullResponse.status(), is(OK)); - assertThat(fullResponse.contentType(), isValue(PLAIN_TEXT_UTF_8.toString())); - assertThat(fullResponse.contentLength(), isValue(length("foo"))); - assertThat(fullResponse.bodyAs(UTF_8), is("foo")); + assertThat(response.status(), is(OK)); + assertThat(response.contentType(), isValue(PLAIN_TEXT_UTF_8.toString())); + assertThat(response.contentLength(), isValue(length("foo"))); + assertThat(response.bodyAs(UTF_8), is("foo")); } private Long length(String string) { diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/AdminServerBuilder.java b/components/proxy/src/main/java/com/hotels/styx/admin/AdminServerBuilder.java index 09c430033e..50e730d9a4 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/AdminServerBuilder.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/AdminServerBuilder.java @@ -40,10 +40,13 @@ import com.hotels.styx.admin.tasks.OriginsCommandHandler; import com.hotels.styx.admin.tasks.OriginsReloadCommandHandler; import com.hotels.styx.api.HttpHandler; +import com.hotels.styx.api.WebServiceHandler; import com.hotels.styx.api.configuration.Configuration; import com.hotels.styx.api.extension.service.BackendService; import com.hotels.styx.api.extension.service.spi.Registry; +import com.hotels.styx.common.http.handler.HttpAggregator; import com.hotels.styx.common.http.handler.HttpMethodFilteringHandler; +import com.hotels.styx.common.http.handler.HttpStreamer; import com.hotels.styx.common.http.handler.StaticBodyHttpHandler; import com.hotels.styx.proxy.plugin.NamedPlugin; import com.hotels.styx.routing.RoutingObjectRecord; @@ -80,6 +83,7 @@ */ public class AdminServerBuilder { private static final Logger LOG = getLogger(AdminServerBuilder.class); + private static final int MEGABYTE = 1024 * 1024; private final Environment environment; private final Configuration configuration; @@ -100,6 +104,63 @@ public AdminServerBuilder backendServicesRegistry(Registry backe return this; } + private static List routesForPlugin(NamedPlugin namedPlugin) { + List routes = pluginAdminEndpointRoutes(namedPlugin); + + List endpointLinks = routes.stream() + .map(PluginAdminEndpointRoute::link) + .collect(toList()); + + WebServiceHandler handler = endpointLinks.isEmpty() + ? new StaticBodyHttpHandler(HTML_UTF_8, format("This plugin (%s) does not expose any admin interfaces", namedPlugin.name())) + : new IndexHandler(endpointLinks); + + Route indexRoute = new Route(pluginPath(namedPlugin), handler); + + return concatenate(indexRoute, routes); + } + + private static List pluginAdminEndpointRoutes(NamedPlugin namedPlugin) { + Map adminInterfaceHandlers = namedPlugin.adminInterfaceHandlers(); + + return mapToList(adminInterfaceHandlers, (relativePath, handler) -> + new PluginAdminEndpointRoute(namedPlugin, relativePath, new HttpStreamer(MEGABYTE, handler))); + } + + private static Iterable indexLinkPaths() { + return ImmutableSortedSet.of( + link("version.txt", "/version.txt"), + link("Ping", "/admin/ping"), + link("Threads", "/admin/threads"), + link("Current Requests", "/admin/current_requests?withStackTrace=true"), + link("Metrics", "/admin/metrics?pretty"), + link("Configuration", "/admin/configuration?pretty"), + link("Log Configuration", "/admin/configuration/logging"), + link("Origins Configuration", "/admin/configuration/origins?pretty"), + link("Startup Configuration", "/admin/configuration/startup"), + link("JVM", "/admin/jvm?pretty"), + link("Origins Status", "/admin/origins/status?pretty"), + link("Dashboard", "/admin/dashboard/index.html"), + link("Plugins", "/admin/plugins")); + } + + private static List concatenate(T item, List items) { + List list = new ArrayList<>(items.size() + 1); + list.add(item); + list.addAll(items); + return list; + } + + private static String pluginPath(NamedPlugin namedPlugin) { + return "/admin/plugins/" + namedPlugin.name(); + } + + private JsonHandler dashboardDataHandler(StyxConfig styxConfig) { + return new JsonHandler<>(new DashboardDataSupplier(backendServicesRegistry, environment, styxConfig), + Optional.of(Duration.ofSeconds(10)), + new MetricsModule(SECONDS, MILLISECONDS, false)); + } + public HttpServer build() { LOG.info("event bus that will be used is {}", environment.eventBus()); StyxConfig styxConfig = environment.configuration(); @@ -107,11 +168,11 @@ public HttpServer build() { return new NettyServerBuilderSpec("Admin", environment.serverEnvironment(), new WebServerConnectorFactory()) .toNettyServerBuilder(adminServerConfig) - .handlerFactory(() -> adminEndpoints(styxConfig)) + .handlerFactory(() -> new HttpAggregator(adminEndpoints(styxConfig))) .build(); } - private StandardHttpRouter adminEndpoints(StyxConfig styxConfig) { + private WebServiceHandler adminEndpoints(StyxConfig styxConfig) { Optional metricsCacheExpiration = styxConfig.adminServerConfig().metricsCacheExpiration(); StandardHttpRouter httpRouter = new StandardHttpRouter(); @@ -158,63 +219,6 @@ private StandardHttpRouter adminEndpoints(StyxConfig styxConfig) { return httpRouter; } - private JsonHandler dashboardDataHandler(StyxConfig styxConfig) { - return new JsonHandler<>(new DashboardDataSupplier(backendServicesRegistry, environment, styxConfig), - Optional.of(Duration.ofSeconds(10)), - new MetricsModule(SECONDS, MILLISECONDS, false)); - } - - private static Iterable indexLinkPaths() { - return ImmutableSortedSet.of( - link("version.txt", "/version.txt"), - link("Ping", "/admin/ping"), - link("Threads", "/admin/threads"), - link("Current Requests", "/admin/current_requests?withStackTrace=true"), - link("Metrics", "/admin/metrics?pretty"), - link("Configuration", "/admin/configuration?pretty"), - link("Log Configuration", "/admin/configuration/logging"), - link("Origins Configuration", "/admin/configuration/origins?pretty"), - link("Startup Configuration", "/admin/configuration/startup"), - link("JVM", "/admin/jvm?pretty"), - link("Origins Status", "/admin/origins/status?pretty"), - link("Dashboard", "/admin/dashboard/index.html"), - link("Plugins", "/admin/plugins")); - } - - private static List routesForPlugin(NamedPlugin namedPlugin) { - List routes = pluginAdminEndpointRoutes(namedPlugin); - - List endpointLinks = routes.stream() - .map(PluginAdminEndpointRoute::link) - .collect(toList()); - - HttpHandler handler = endpointLinks.isEmpty() - ? new StaticBodyHttpHandler(HTML_UTF_8, format("This plugin (%s) does not expose any admin interfaces", namedPlugin.name())) - : new IndexHandler(endpointLinks); - - Route indexRoute = new Route(pluginPath(namedPlugin), handler); - - return concatenate(indexRoute, routes); - } - - private static List concatenate(T item, List items) { - List list = new ArrayList<>(items.size() + 1); - list.add(item); - list.addAll(items); - return list; - } - - private static String pluginPath(NamedPlugin namedPlugin) { - return "/admin/plugins/" + namedPlugin.name(); - } - - private static List pluginAdminEndpointRoutes(NamedPlugin namedPlugin) { - Map adminInterfaceHandlers = namedPlugin.adminInterfaceHandlers(); - - return mapToList(adminInterfaceHandlers, (relativePath, handler) -> - new PluginAdminEndpointRoute(namedPlugin, relativePath, handler)); - } - // allows key and value to be labelled in lambda instead of having to use Entry.getKey, Entry.getValue private static List mapToList(Map map, BiFunction function) { return map.entrySet().stream() @@ -224,9 +228,9 @@ private static List mapToList(Map map, BiFunction fu private static class Route { private final String path; - private final HttpHandler handler; + private final WebServiceHandler handler; - Route(String path, HttpHandler handler) { + Route(String path, WebServiceHandler handler) { this.path = path; this.handler = handler; } @@ -235,7 +239,7 @@ String path() { return path; } - HttpHandler handler() { + WebServiceHandler handler() { return handler; } } @@ -243,7 +247,7 @@ HttpHandler handler() { private static class PluginAdminEndpointRoute extends Route { private final NamedPlugin namedPlugin; - PluginAdminEndpointRoute(NamedPlugin namedPlugin, String relativePath, HttpHandler handler) { + PluginAdminEndpointRoute(NamedPlugin namedPlugin, String relativePath, WebServiceHandler handler) { super(pluginAdminEndpointPath(namedPlugin, relativePath), handler); this.namedPlugin = namedPlugin; diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/CurrentRequestsHandler.java b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/CurrentRequestsHandler.java index 6ffa2bf9af..9f3e4289fa 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/CurrentRequestsHandler.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/CurrentRequestsHandler.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,9 +15,9 @@ */ package com.hotels.styx.admin.handlers; +import com.hotels.styx.api.HttpInterceptor; +import com.hotels.styx.api.HttpRequest; import com.hotels.styx.api.HttpResponse; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; import com.hotels.styx.common.http.handler.BaseHttpHandler; import com.hotels.styx.server.track.CurrentRequestTracker; @@ -48,7 +48,7 @@ public CurrentRequestsHandler(CurrentRequestTracker tracker) { } @Override - public LiveHttpResponse doHandle(LiveHttpRequest request) { + public HttpResponse doHandle(HttpRequest request, HttpInterceptor.Context context) { boolean withStackTrace = request.queryParam("withStackTrace") .map("true"::equals) .orElse(false); @@ -58,8 +58,7 @@ public LiveHttpResponse doHandle(LiveHttpRequest request) { .disableCaching() .header(CONTENT_TYPE, PLAIN_TEXT_UTF_8) .body(getCurrentRequestContent(withStackTrace), UTF_8, true) - .build() - .stream(); + .build(); } private String getCurrentRequestContent(boolean withStackTrace) { diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/IndexHandler.java b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/IndexHandler.java index 71067ef581..cba3b07016 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/IndexHandler.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/IndexHandler.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,14 +15,15 @@ */ package com.hotels.styx.admin.handlers; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; +import com.hotels.styx.api.HttpInterceptor; +import com.hotels.styx.api.HttpRequest; +import com.hotels.styx.api.HttpResponse; import com.hotels.styx.common.http.handler.BaseHttpHandler; import static com.google.common.net.HttpHeaders.CONTENT_LANGUAGE; import static com.google.common.net.MediaType.HTML_UTF_8; -import static com.hotels.styx.api.HttpResponse.response; import static com.hotels.styx.api.HttpHeaderNames.CONTENT_TYPE; +import static com.hotels.styx.api.HttpResponse.response; import static com.hotels.styx.api.HttpResponseStatus.OK; import static java.lang.String.format; import static java.nio.charset.StandardCharsets.UTF_8; @@ -56,13 +57,12 @@ private static String generateHtml(Iterable links) { } @Override - protected LiveHttpResponse doHandle(LiveHttpRequest request) { + protected HttpResponse doHandle(HttpRequest request, HttpInterceptor.Context context) { return response(OK) .addHeader(CONTENT_TYPE, HTML_UTF_8.toString()) .header(CONTENT_LANGUAGE, "en") .body(html, UTF_8) - .build() - .stream(); + .build(); } private static String buildIndexContent(Iterable links) { diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/JsonHandler.java b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/JsonHandler.java index a1e7735250..df8d227a8a 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/JsonHandler.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/JsonHandler.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -21,8 +21,9 @@ import com.hotels.styx.admin.dashboard.JsonSupplier; import com.hotels.styx.admin.handlers.json.JsonReformatter; import com.hotels.styx.api.Clock; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; +import com.hotels.styx.api.HttpInterceptor; +import com.hotels.styx.api.HttpRequest; +import com.hotels.styx.api.HttpResponse; import com.hotels.styx.common.http.handler.BaseHttpHandler; import org.slf4j.Logger; @@ -32,8 +33,8 @@ import static com.google.common.net.MediaType.JSON_UTF_8; import static com.hotels.styx.api.Clocks.systemClock; -import static com.hotels.styx.api.HttpResponse.response; import static com.hotels.styx.api.HttpHeaderNames.CONTENT_TYPE; +import static com.hotels.styx.api.HttpResponse.response; import static com.hotels.styx.api.HttpResponseStatus.INTERNAL_SERVER_ERROR; import static com.hotels.styx.api.HttpResponseStatus.OK; import static java.nio.charset.StandardCharsets.UTF_8; @@ -88,7 +89,7 @@ public JsonHandler(Supplier dataSupplier, Optional cacheExpiration, } @Override - protected LiveHttpResponse doHandle(LiveHttpRequest request) { + protected HttpResponse doHandle(HttpRequest request, HttpInterceptor.Context context) { try { String jsonContent = jsonSupplier(request).get(); @@ -96,18 +97,16 @@ protected LiveHttpResponse doHandle(LiveHttpRequest request) { .disableCaching() .addHeader(CONTENT_TYPE, JSON_UTF_8.toString()) .body(jsonContent, UTF_8) - .build() - .stream(); + .build(); } catch (Exception e) { return response(INTERNAL_SERVER_ERROR) .body(e.getMessage(), UTF_8) - .build() - .stream(); + .build(); } } - private Supplier jsonSupplier(LiveHttpRequest request) { + private Supplier jsonSupplier(HttpRequest request) { if (request.queryParam("reformat").isPresent()) { return reformatSupplier; } diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/LoggingConfigurationHandler.java b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/LoggingConfigurationHandler.java index 8d74b6bc2f..f8e6132d78 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/LoggingConfigurationHandler.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/LoggingConfigurationHandler.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,11 +17,11 @@ import com.google.common.net.MediaType; import com.hotels.styx.api.Eventual; -import com.hotels.styx.api.HttpHandler; import com.hotels.styx.api.HttpInterceptor; -import com.hotels.styx.api.LiveHttpResponse; -import com.hotels.styx.api.LiveHttpRequest; +import com.hotels.styx.api.HttpRequest; +import com.hotels.styx.api.HttpResponse; import com.hotels.styx.api.Resource; +import com.hotels.styx.api.WebServiceHandler; import org.slf4j.Logger; import java.io.IOException; @@ -42,7 +42,7 @@ /** * Displays contents of logging configuration file. */ -public class LoggingConfigurationHandler implements HttpHandler { +public class LoggingConfigurationHandler implements WebServiceHandler { private static final Logger LOG = getLogger(LoggingConfigurationHandler.class); private final Resource logConfigLocation; @@ -54,19 +54,18 @@ public LoggingConfigurationHandler(Resource logConfigLocation) { } @Override - public Eventual handle(LiveHttpRequest request, HttpInterceptor.Context context) { + public Eventual handle(HttpRequest request, HttpInterceptor.Context context) { return Eventual.of(generateResponse()); } - private LiveHttpResponse generateResponse() { + private HttpResponse generateResponse() { Content content = contentSupplier.get(); return response(OK) .header(CONTENT_TYPE, content.type) .header(CONTENT_LENGTH, content.length) .body(content.content, UTF_8) - .build() - .stream(); + .build(); } private Content loadContent() { diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/MetricsHandler.java b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/MetricsHandler.java index 8df9b9fa68..795597693b 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/MetricsHandler.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/MetricsHandler.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,22 +18,22 @@ import com.codahale.metrics.Metric; import com.codahale.metrics.json.MetricsModule; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.module.SimpleModule; -import com.hotels.styx.api.HttpResponse; +import com.hotels.styx.api.Eventual; import com.hotels.styx.api.HttpInterceptor; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; +import com.hotels.styx.api.HttpRequest; +import com.hotels.styx.api.HttpResponse; import com.hotels.styx.api.MetricRegistry; -import com.hotels.styx.api.Eventual; +import com.hotels.styx.api.WebServiceHandler; import com.hotels.styx.api.metrics.codahale.CodaHaleMetricRegistry; import com.hotels.styx.infrastructure.configuration.json.mixins.CodaHaleMetricRegistryMixin; import java.time.Duration; import java.util.Map; import java.util.Optional; -import java.util.function.BiPredicate; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -42,14 +42,13 @@ import static com.hotels.styx.api.HttpResponseStatus.OK; import static com.hotels.styx.common.MapStream.stream; import static java.nio.charset.StandardCharsets.UTF_8; -import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; /** * Handler for showing all registered metrics for styx server. Can cache page content. */ -public class MetricsHandler extends JsonHandler { +public class MetricsHandler implements WebServiceHandler { private static final Pattern SPECIFIC_METRICS_PATH_PATTERN = Pattern.compile(".*/metrics/(.+)/?"); private static final boolean DO_NOT_SHOW_SAMPLES = false; private static final String FILTER_PARAM = "filter"; @@ -60,6 +59,7 @@ public class MetricsHandler extends JsonHandler { .addMixIn(CodaHaleMetricRegistry.class, CodaHaleMetricRegistryMixin.class); private final MetricRegistry metricRegistry; + private final UrlPatternRouter urlMatcher; /** * Constructs a new handler. @@ -68,50 +68,55 @@ public class MetricsHandler extends JsonHandler { * @param cacheExpiration duration for which generated page content should be cached */ public MetricsHandler(MetricRegistry metricRegistry, Optional cacheExpiration) { - super(requireNonNull(metricRegistry), cacheExpiration, - new MetricsModule(SECONDS, MILLISECONDS, DO_NOT_SHOW_SAMPLES), - new FullMetricsModule()); + this.urlMatcher = new UrlPatternRouter.Builder() + .get(".*/metrics", new RootMetricsHandler( + metricRegistry, + cacheExpiration, + new MetricsModule(SECONDS, MILLISECONDS, DO_NOT_SHOW_SAMPLES), + new FullMetricsModule())) + .get(".*/metrics/.*", (request, context) -> Eventual.of(filteredMetricResponse(request))) + .build(); this.metricRegistry = metricRegistry; } - private static class FullMetricsModule extends SimpleModule { - FullMetricsModule() { - setMixInAnnotation(CodaHaleMetricRegistry.class, CodaHaleMetricRegistryMixin.class); - } - } - - @Override - public Eventual handle(LiveHttpRequest request, HttpInterceptor.Context context) { - MetricRequest metricRequest = new MetricRequest(request); - - return metricRequest.fullMetrics() - ? super.handle(request, context) - : Eventual.of(restrictedMetricsResponse(metricRequest).build().stream()); + private static boolean matchesRoot(String metricName, String root) { + return root == null || metricName.equals(root) || metricName.startsWith(root + "."); } - private HttpResponse.Builder restrictedMetricsResponse(MetricRequest request) { - Map fullMetrics = metricRegistry.getMetrics(); - - Map restricted = filter(fullMetrics, (name, metric) -> request.matchesRoot(name)); - - return restricted.isEmpty() - ? response(NOT_FOUND) - : search(request, restricted); + private static boolean containsSearchTerm(String name, String searchTerm) { + return searchTerm == null || name.contains(searchTerm); } - private HttpResponse.Builder search(MetricRequest request, Map metrics) { - Map searched = filter(metrics, (name, metric) -> request.containsSearchTerm(name)); - - String body = serialise(searched, request.prettyPrint); - - return response(OK) - .body(body, UTF_8) - .disableCaching(); + @Override + public Eventual handle(HttpRequest request, HttpInterceptor.Context context) { + return this.urlMatcher.handle(request, context); } - private static Map filter(Map map, BiPredicate predicate) { - return stream(map).filter(predicate).toMap(); + private HttpResponse filteredMetricResponse(HttpRequest request) { + String root = Optional.of(SPECIFIC_METRICS_PATH_PATTERN.matcher(request.path())) + .filter(Matcher::matches) + .map(matcher -> matcher.group(1)) + .orElse(null); + + boolean prettyPrint = request.queryParam(PRETTY_PRINT_PARAM).isPresent(); + String searchTerm = request.queryParam(FILTER_PARAM).orElse(null); + + Map result = stream(metricRegistry.getMetrics()) + .filter((name, metric) -> matchesRoot(name, root)) + .toMap(); + + if (result.isEmpty()) { + return response(NOT_FOUND).build(); + } else { + return response(OK) + .body(serialise( + stream(result) + .filter((name, metric) -> containsSearchTerm(name, searchTerm)) + .toMap(), prettyPrint), UTF_8) + .disableCaching() + .build(); + } } private String serialise(Object object, boolean pretty) { @@ -124,35 +129,17 @@ private String serialise(Object object, boolean pretty) { } } - private static class MetricRequest { - private final String root; - private final String searchTerm; - private final boolean prettyPrint; - private final String prefix; - - MetricRequest(LiveHttpRequest request) { - this.root = metricName(request.path()).orElse(null); - this.searchTerm = request.queryParam(FILTER_PARAM).orElse(null); - this.prettyPrint = request.queryParam(PRETTY_PRINT_PARAM).isPresent(); - this.prefix = root + "."; - } - - boolean fullMetrics() { - return root == null && searchTerm == null; + static class RootMetricsHandler extends JsonHandler { + public RootMetricsHandler(MetricRegistry data, Optional cacheExpiration, Module... modules) { + super(data, cacheExpiration, modules); } + } - private static Optional metricName(String path) { - return Optional.of(SPECIFIC_METRICS_PATH_PATTERN.matcher(path)) - .filter(Matcher::matches) - .map(matcher -> matcher.group(1)); + private static class FullMetricsModule extends SimpleModule { + FullMetricsModule() { + setMixInAnnotation(CodaHaleMetricRegistry.class, CodaHaleMetricRegistryMixin.class); } + } - private boolean matchesRoot(String name) { - return root == null || name.equals(root) || name.startsWith(prefix); - } - private boolean containsSearchTerm(String name) { - return searchTerm == null || name.contains(searchTerm); - } - } } diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/OriginsHandler.java b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/OriginsHandler.java index 63d6936e46..b6b077ba43 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/OriginsHandler.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/OriginsHandler.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,19 +18,20 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; -import com.hotels.styx.common.http.handler.BaseHttpHandler; +import com.hotels.styx.api.HttpInterceptor; +import com.hotels.styx.api.HttpRequest; +import com.hotels.styx.api.HttpResponse; import com.hotels.styx.api.extension.service.BackendService; import com.hotels.styx.api.extension.service.spi.Registry; +import com.hotels.styx.common.http.handler.BaseHttpHandler; import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL; import static com.google.common.net.MediaType.JSON_UTF_8; -import static com.hotels.styx.api.HttpResponse.response; import static com.hotels.styx.api.HttpHeaderNames.CONTENT_TYPE; -import static com.hotels.styx.infrastructure.configuration.json.ObjectMappers.addStyxMixins; +import static com.hotels.styx.api.HttpResponse.response; import static com.hotels.styx.api.HttpResponseStatus.INTERNAL_SERVER_ERROR; import static com.hotels.styx.api.HttpResponseStatus.OK; +import static com.hotels.styx.infrastructure.configuration.json.ObjectMappers.addStyxMixins; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Objects.requireNonNull; @@ -47,27 +48,25 @@ public OriginsHandler(Registry backendServicesRegistry) { } @Override - protected LiveHttpResponse doHandle(LiveHttpRequest request) { + protected HttpResponse doHandle(HttpRequest request, HttpInterceptor.Context context) { Iterable backendServices = backendServicesRegistry.get(); return jsonResponse(backendServices, isPrettyPrint(request)); } - private LiveHttpResponse jsonResponse(Object object, boolean prettyPrint) { + private HttpResponse jsonResponse(Object object, boolean prettyPrint) { try { String jsonContent = marshal(object, prettyPrint); return response(OK) .disableCaching() .addHeader(CONTENT_TYPE, JSON_UTF_8.toString()) .body(jsonContent, UTF_8) - .build() - .stream(); + .build(); } catch (JsonProcessingException e) { return response(INTERNAL_SERVER_ERROR) .body(e.getMessage(), UTF_8) - .build() - .stream(); + .build(); } } @@ -81,7 +80,7 @@ private ObjectWriter writer(boolean prettyPrint) { : this.mapper.writer(); } - private boolean isPrettyPrint(LiveHttpRequest request) { + private boolean isPrettyPrint(HttpRequest request) { return request.queryParam("pretty").isPresent(); } } diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/OriginsInventoryHandler.java b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/OriginsInventoryHandler.java index 0a6a71eadb..c6a368f0e3 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/OriginsInventoryHandler.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/OriginsInventoryHandler.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,8 +20,9 @@ import com.fasterxml.jackson.databind.ObjectWriter; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; +import com.hotels.styx.api.HttpInterceptor; +import com.hotels.styx.api.HttpRequest; +import com.hotels.styx.api.HttpResponse; import com.hotels.styx.api.Id; import com.hotels.styx.api.extension.OriginsChangeListener; import com.hotels.styx.api.extension.OriginsSnapshot; @@ -35,8 +36,8 @@ import static com.fasterxml.jackson.databind.SerializationFeature.FAIL_ON_EMPTY_BEANS; import static com.google.common.net.MediaType.JSON_UTF_8; import static com.hotels.styx.admin.support.Json.PRETTY_PRINTER; -import static com.hotels.styx.api.HttpResponse.response; import static com.hotels.styx.api.HttpHeaderNames.CONTENT_TYPE; +import static com.hotels.styx.api.HttpResponse.response; import static com.hotels.styx.api.HttpResponseStatus.OK; import static com.hotels.styx.infrastructure.configuration.json.ObjectMappers.addStyxMixins; import static java.nio.charset.StandardCharsets.UTF_8; @@ -63,14 +64,8 @@ public OriginsInventoryHandler(EventBus eventBus) { eventBus.post(new GetOriginsInventorySnapshot()); } - @Override - protected LiveHttpResponse doHandle(LiveHttpRequest request) { - return response(OK) - .addHeader(CONTENT_TYPE, JSON_UTF_8.toString()) - .disableCaching() - .body(content(isPrettyPrint(request)), UTF_8) - .build() - .stream(); + private static boolean isPrettyPrint(HttpRequest request) { + return request.queryParam("pretty").isPresent(); } private String content(boolean pretty) { @@ -91,8 +86,13 @@ private ObjectWriter writer(boolean prettyPrint) { : this.mapper.writer(); } - private static boolean isPrettyPrint(LiveHttpRequest request) { - return request.queryParam("pretty").isPresent(); + @Override + protected HttpResponse doHandle(HttpRequest request, HttpInterceptor.Context context) { + return response(OK) + .addHeader(CONTENT_TYPE, JSON_UTF_8.toString()) + .disableCaching() + .body(content(isPrettyPrint(request)), UTF_8) + .build(); } @Subscribe diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/PingHandler.java b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/PingHandler.java index 6f2f51cede..101ab62e8d 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/PingHandler.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/PingHandler.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,13 +15,14 @@ */ package com.hotels.styx.admin.handlers; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; +import com.hotels.styx.api.HttpInterceptor; +import com.hotels.styx.api.HttpRequest; +import com.hotels.styx.api.HttpResponse; import com.hotels.styx.common.http.handler.BaseHttpHandler; import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8; -import static com.hotels.styx.api.HttpResponse.response; import static com.hotels.styx.api.HttpHeaderNames.CONTENT_TYPE; +import static com.hotels.styx.api.HttpResponse.response; import static com.hotels.styx.api.HttpResponseStatus.OK; import static java.nio.charset.StandardCharsets.UTF_8; @@ -31,12 +32,11 @@ */ public class PingHandler extends BaseHttpHandler { @Override - protected LiveHttpResponse doHandle(LiveHttpRequest request) { + protected HttpResponse doHandle(HttpRequest request, HttpInterceptor.Context context) { return response(OK) .disableCaching() .addHeader(CONTENT_TYPE, PLAIN_TEXT_UTF_8.toString()) .body("pong", UTF_8) - .build() - .stream(); + .build(); } } diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/PluginListHandler.java b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/PluginListHandler.java index 7dc2aaf8bf..9fe46ddd37 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/PluginListHandler.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/PluginListHandler.java @@ -16,10 +16,10 @@ package com.hotels.styx.admin.handlers; import com.hotels.styx.api.Eventual; -import com.hotels.styx.api.HttpHandler; import com.hotels.styx.api.HttpInterceptor; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; +import com.hotels.styx.api.HttpRequest; +import com.hotels.styx.api.HttpResponse; +import com.hotels.styx.api.WebServiceHandler; import com.hotels.styx.configstore.ConfigStore; import com.hotels.styx.proxy.plugin.NamedPlugin; @@ -38,7 +38,7 @@ /** * Returns a simple HTML page with a list of plugins, split into enabled and disabled. */ -public class PluginListHandler implements HttpHandler { +public class PluginListHandler implements WebServiceHandler { private final ConfigStore configStore; public PluginListHandler(ConfigStore configStore) { @@ -46,7 +46,7 @@ public PluginListHandler(ConfigStore configStore) { } @Override - public Eventual handle(LiveHttpRequest request, HttpInterceptor.Context context) { + public Eventual handle(HttpRequest request, HttpInterceptor.Context context) { List plugins = configStore.valuesStartingWith("plugins", NamedPlugin.class); Stream enabled = plugins.stream().filter(NamedPlugin::enabled); @@ -58,8 +58,7 @@ public Eventual handle(LiveHttpRequest request, HttpIntercepto return Eventual.of(response(OK) .body(output, UTF_8) .addHeader(CONTENT_TYPE, HTML_UTF_8.toString()) - .build() - .stream()); + .build()); } private static String section(String toggleState, Stream plugins) { diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/PluginToggleHandler.java b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/PluginToggleHandler.java index da62795806..aa087e2427 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/PluginToggleHandler.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/PluginToggleHandler.java @@ -19,12 +19,11 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.hotels.styx.api.Eventual; -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.HttpResponseStatus; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; +import com.hotels.styx.api.WebServiceHandler; import com.hotels.styx.configstore.ConfigStore; import com.hotels.styx.proxy.plugin.NamedPlugin; import org.slf4j.Logger; @@ -38,12 +37,12 @@ import static com.hotels.styx.api.HttpHeaderNames.CONTENT_TYPE; import static com.hotels.styx.api.HttpMethod.GET; import static com.hotels.styx.api.HttpMethod.PUT; +import static com.hotels.styx.api.HttpResponse.response; import static com.hotels.styx.api.HttpResponseStatus.BAD_REQUEST; import static com.hotels.styx.api.HttpResponseStatus.INTERNAL_SERVER_ERROR; import static com.hotels.styx.api.HttpResponseStatus.METHOD_NOT_ALLOWED; import static com.hotels.styx.api.HttpResponseStatus.NOT_FOUND; import static com.hotels.styx.api.HttpResponseStatus.OK; -import static com.hotels.styx.api.LiveHttpResponse.response; import static java.lang.String.format; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Comparator.naturalOrder; @@ -53,7 +52,7 @@ /** * Handler that will enable and disable plugins. */ -public class PluginToggleHandler implements HttpHandler { +public class PluginToggleHandler implements WebServiceHandler { private static final Logger LOGGER = getLogger(PluginToggleHandler.class); private static final Pattern URL_PATTERN = Pattern.compile(".*/([^/]+)/enabled/?"); @@ -71,54 +70,54 @@ public PluginToggleHandler(ConfigStore configStore) { this.configStore = requireNonNull(configStore); } - @Override - public Eventual handle(LiveHttpRequest request, HttpInterceptor.Context context) { - return getCurrentOrPutNewState(request, context) - .onError(cause -> handleErrors(cause, context)); + private static HttpResponse applyUpdate(RequestedUpdate requestedUpdate) { + boolean changed = requestedUpdate.apply(); + + String message = responseMessage(requestedUpdate, changed); + + return responseWith(OK, message); } - private Eventual getCurrentOrPutNewState(LiveHttpRequest request, HttpInterceptor.Context context) { - if (GET.equals(request.method())) { - return getCurrentState(request, context); - } else if (PUT.equals(request.method())) { - return putNewState(request, context); - } else { - return Eventual.of(response(METHOD_NOT_ALLOWED).build()); + private static Matcher urlMatcher(HttpRequest request) { + Matcher matcher = URL_PATTERN.matcher(request.path()); + + if (!matcher.matches()) { + throw new BadPluginToggleRequestException("Invalid URL"); } + return matcher; } - private Eventual getCurrentState(LiveHttpRequest request, HttpInterceptor.Context context) { + private static Eventual requestedNewState(HttpRequest request) { + // TODO: Mikko: Crappy coding, fix this: return Eventual.of(request) - .map(this::plugin) - .map(PluginToggleHandler::currentState) - .map(state -> responseWith(OK, state.toString())); + .map(fullRequest -> fullRequest.bodyAs(UTF_8)) + .map(PluginToggleHandler::parseToBoolean) + .map(PluginEnabledState::fromBoolean); } private static PluginEnabledState currentState(NamedPlugin plugin) { return plugin.enabled() ? PluginEnabledState.ENABLED : PluginEnabledState.DISABLED; } - private Eventual putNewState(LiveHttpRequest request, HttpInterceptor.Context context) { - return Eventual.of(request) - .flatMap(this::requestedUpdate) - .map(PluginToggleHandler::applyUpdate); - } - - private Eventual requestedUpdate(LiveHttpRequest request) { - return requestedNewState(request) - .map(state -> { - NamedPlugin plugin = plugin(request); - - return new RequestedUpdate(plugin, state); - }); + private static HttpResponse responseWith(HttpResponseStatus status, String message) { + return HttpResponse.response(status) + .body(message + "\n", UTF_8) + .addHeader(CONTENT_TYPE, PLAIN_TEXT_UTF_8.toString()) + .disableCaching() + .build(); } - private static LiveHttpResponse applyUpdate(RequestedUpdate requestedUpdate) { - boolean changed = requestedUpdate.apply(); + private static Eventual handleErrors(Throwable e, HttpInterceptor.Context context) { + if (e instanceof PluginNotFoundException) { + return Eventual.of(responseWith(NOT_FOUND, e.getMessage())); + } - String message = responseMessage(requestedUpdate, changed); + if (e instanceof BadPluginToggleRequestException) { + return Eventual.of(responseWith(BAD_REQUEST, e.getMessage())); + } - return responseWith(OK, message); + LOGGER.error("Plugin toggle error", e); + return Eventual.of(responseWith(INTERNAL_SERVER_ERROR, "")); } private static String responseMessage(RequestedUpdate requestedUpdate, boolean changed) { @@ -139,10 +138,20 @@ private static String wasChangedMessage(RequestedUpdate requestedUpdate) { return format("State of '%s' changed to '%s'", requestedUpdate.plugin().name(), requestedUpdate.newState()); } - private NamedPlugin plugin(LiveHttpRequest request) { - Matcher matcher = urlMatcher(request); - String pluginName = matcher.group(1); - return plugin(pluginName); + @Override + public Eventual handle(HttpRequest request, HttpInterceptor.Context context) { + return getCurrentOrPutNewState(request, context) + .onError(cause -> handleErrors(cause, context)); + } + + private Eventual getCurrentOrPutNewState(HttpRequest request, HttpInterceptor.Context context) { + if (GET.equals(request.method())) { + return getCurrentState(request, context); + } else if (PUT.equals(request.method())) { + return putNewState(request, context); + } else { + return Eventual.of(response(METHOD_NOT_ALLOWED).build()); + } } private NamedPlugin plugin(String pluginName) { @@ -150,29 +159,26 @@ private NamedPlugin plugin(String pluginName) { .orElseThrow(() -> new PluginNotFoundException("No such plugin: pluginName=" + pluginName)); } - private static Matcher urlMatcher(LiveHttpRequest request) { - Matcher matcher = URL_PATTERN.matcher(request.path()); - - if (!matcher.matches()) { - throw new BadPluginToggleRequestException("Invalid URL"); - } - return matcher; + private Eventual getCurrentState(HttpRequest request, HttpInterceptor.Context context) { + return Eventual.of(request) + .map(this::plugin) + .map(PluginToggleHandler::currentState) + .map(state -> responseWith(OK, state.toString())); } - private static Eventual requestedNewState(LiveHttpRequest request) { - return request.aggregate(MAX_CONTENT_SIZE) - .map(fullRequest -> fullRequest.bodyAs(UTF_8)) - .map(PluginToggleHandler::parseToBoolean) - .map(PluginEnabledState::fromBoolean); + private Eventual putNewState(HttpRequest request, HttpInterceptor.Context context) { + return Eventual.of(request) + .flatMap(this::requestedUpdate) + .map(PluginToggleHandler::applyUpdate); } - private static LiveHttpResponse responseWith(HttpResponseStatus status, String message) { - return HttpResponse.response(status) - .body(message + "\n", UTF_8) - .addHeader(CONTENT_TYPE, PLAIN_TEXT_UTF_8.toString()) - .disableCaching() - .build() - .stream(); + private Eventual requestedUpdate(HttpRequest request) { + return requestedNewState(request) + .map(state -> { + NamedPlugin plugin = plugin(request); + + return new RequestedUpdate(plugin, state); + }); } private static boolean parseToBoolean(String string) { @@ -186,17 +192,10 @@ private static boolean parseToBoolean(String string) { } } - private static Eventual handleErrors(Throwable e, HttpInterceptor.Context context) { - if (e instanceof PluginNotFoundException) { - return Eventual.of(responseWith(NOT_FOUND, e.getMessage())); - } - - if (e instanceof BadPluginToggleRequestException) { - return Eventual.of(responseWith(BAD_REQUEST, e.getMessage())); - } - - LOGGER.error("Plugin toggle error", e); - return Eventual.of(responseWith(INTERNAL_SERVER_ERROR, "")); + private NamedPlugin plugin(HttpRequest request) { + Matcher matcher = urlMatcher(request); + String pluginName = matcher.group(1); + return plugin(pluginName); } private enum PluginEnabledState { diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/RoutingObjectHandler.java b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/RoutingObjectHandler.java index 50a1ae309e..09c77cdad3 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/RoutingObjectHandler.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/RoutingObjectHandler.java @@ -22,13 +22,11 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.hotels.styx.api.Eventual; -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.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; import com.hotels.styx.routing.RoutingObject; +import com.hotels.styx.api.WebServiceHandler; import com.hotels.styx.routing.RoutingObjectRecord; import com.hotels.styx.routing.config.RoutingObjectDefinition; import com.hotels.styx.routing.config.RoutingObjectFactory; @@ -55,7 +53,7 @@ /** * Provides admin interface access to Styx routing configuration. */ -public class RoutingObjectHandler implements HttpHandler { +public class RoutingObjectHandler implements WebServiceHandler { private static final Logger LOGGER = getLogger(RoutingObjectHandler.class); private static final ObjectMapper YAML_MAPPER = addStyxMixins(new ObjectMapper(new YAMLFactory())) @@ -67,7 +65,7 @@ public class RoutingObjectHandler implements HttpHandler { public RoutingObjectHandler(StyxObjectStore routeDatabase, RoutingObjectFactory objectFactory) { urlRouter = new UrlPatternRouter.Builder() - .get("/admin/routing/objects", httpHandler((request, context) -> { + .get("/admin/routing/objects", (request, context) -> { String output = routeDatabase.entrySet() .stream() .map(entry -> serialise(entry.getFirst(), entry.getSecond())) @@ -76,8 +74,8 @@ public RoutingObjectHandler(StyxObjectStore routeDatabase, return Eventual.of(response(OK) .body(output, UTF_8) .build()); - })) - .get("/admin/routing/objects/:objectName", httpHandler((request, context) -> { + }) + .get("/admin/routing/objects/:objectName", (request, context) -> { String name = placeholders(context).get("objectName"); try { @@ -89,8 +87,8 @@ public RoutingObjectHandler(StyxObjectStore routeDatabase, } catch (ResourceNotFoundException e) { return Eventual.of(response(NOT_FOUND).build()); } - })) - .put("/admin/routing/objects/:objectName", httpHandler((request, context) -> { + }) + .put("/admin/routing/objects/:objectName", (request, context) -> { String body = request.bodyAs(UTF_8); String name = placeholders(context).get("objectName"); @@ -105,15 +103,15 @@ public RoutingObjectHandler(StyxObjectStore routeDatabase, } catch (IOException | RuntimeException cause) { return Eventual.of(response(BAD_REQUEST).body(cause.toString(), UTF_8).build()); } - })) - .delete("/admin/routing/objects/:objectName", httpHandler((request, context) -> { + }) + .delete("/admin/routing/objects/:objectName", (request, context) -> { String name = placeholders(context).get("objectName"); return routeDatabase.remove(name) .map(previous -> previous.getRoutingObject().stop()) .map(previous -> Eventual.of(response(OK).build())) .orElse(Eventual.of(response(NOT_FOUND).build())); - })) + }) .build(); } @@ -131,14 +129,8 @@ private static String serialise(String name, RoutingObjectRecord app) { } } - private static HttpHandler httpHandler(FullHttpHandler delegate) { - return (request, context) -> request.aggregate(1000000) - .flatMap(fullRequest -> delegate.handle(fullRequest, context)) - .map(HttpResponse::stream); - } - @Override - public Eventual handle(LiveHttpRequest request, HttpInterceptor.Context context) { + public Eventual handle(HttpRequest request, HttpInterceptor.Context context) { return urlRouter.handle(request, context); } diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/StyxConfigurationHandler.java b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/StyxConfigurationHandler.java index 40dc9675ae..6aa7d5ca38 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/StyxConfigurationHandler.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/StyxConfigurationHandler.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,10 +17,10 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.hotels.styx.api.Eventual; -import com.hotels.styx.api.HttpHandler; import com.hotels.styx.api.HttpInterceptor; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; +import com.hotels.styx.api.HttpRequest; +import com.hotels.styx.api.HttpResponse; +import com.hotels.styx.api.WebServiceHandler; import com.hotels.styx.api.configuration.Configuration; import com.hotels.styx.common.http.handler.StaticBodyHttpHandler; @@ -33,7 +33,7 @@ /** * Returns a response consisting of the configuration variables. */ -public class StyxConfigurationHandler implements HttpHandler { +public class StyxConfigurationHandler implements WebServiceHandler { private final ObjectMapper objectMapper = new ObjectMapper(); private final StaticBodyHttpHandler styxConfigHandler; @@ -49,21 +49,21 @@ public StyxConfigurationHandler(Configuration configuration) { prettyStyxConfigHandler = new StaticBodyHttpHandler(PLAIN_TEXT_UTF_8, prettify(configuration)); } - @Override - public Eventual handle(LiveHttpRequest request, HttpInterceptor.Context context) { - return configHandler(request.queryParam("pretty").isPresent()) - .handle(request, context) - .map(StyxConfigurationHandler::disableCaching); + private static HttpResponse disableCaching(HttpResponse response) { + return response.newBuilder() + .disableCaching() + .build(); } private StaticBodyHttpHandler configHandler(boolean pretty) { return pretty ? prettyStyxConfigHandler : styxConfigHandler; } - private static LiveHttpResponse disableCaching(LiveHttpResponse response) { - return response.newBuilder() - .disableCaching() - .build(); + @Override + public Eventual handle(HttpRequest request, HttpInterceptor.Context context) { + return configHandler(request.queryParam("pretty").isPresent()) + .handle(request, context) + .map(StyxConfigurationHandler::disableCaching); } private static String body(Configuration styxConfig) { diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/ThreadsHandler.java b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/ThreadsHandler.java index 1ea00fad9a..60b8fefdc4 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/ThreadsHandler.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/ThreadsHandler.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,9 +16,9 @@ package com.hotels.styx.admin.handlers; import com.codahale.metrics.jvm.ThreadDump; +import com.hotels.styx.api.HttpInterceptor; +import com.hotels.styx.api.HttpRequest; import com.hotels.styx.api.HttpResponse; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; import com.hotels.styx.common.http.handler.BaseHttpHandler; import java.io.ByteArrayOutputStream; @@ -42,13 +42,12 @@ public ThreadsHandler() { } @Override - public LiveHttpResponse doHandle(LiveHttpRequest request) { + public HttpResponse doHandle(HttpRequest request, HttpInterceptor.Context context) { return HttpResponse.response(OK) .disableCaching() .header(CONTENT_TYPE, PLAIN_TEXT_UTF_8) .body(threadDumpContent(), true) - .build() - .stream(); + .build(); } private byte[] threadDumpContent() { diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/UrlPatternRouter.java b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/UrlPatternRouter.java index a4895868eb..1c906df57a 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/UrlPatternRouter.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/UrlPatternRouter.java @@ -17,11 +17,11 @@ import com.google.common.collect.ImmutableList; import com.hotels.styx.api.Eventual; -import com.hotels.styx.api.HttpHandler; import com.hotels.styx.api.HttpInterceptor; import com.hotels.styx.api.HttpMethod; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; +import com.hotels.styx.api.HttpRequest; +import com.hotels.styx.api.HttpResponse; +import com.hotels.styx.api.WebServiceHandler; import org.slf4j.Logger; import java.util.ArrayList; @@ -35,16 +35,16 @@ import static com.hotels.styx.api.HttpMethod.GET; import static com.hotels.styx.api.HttpMethod.POST; import static com.hotels.styx.api.HttpMethod.PUT; +import static com.hotels.styx.api.HttpResponse.response; import static com.hotels.styx.api.HttpResponseStatus.INTERNAL_SERVER_ERROR; import static com.hotels.styx.api.HttpResponseStatus.NOT_FOUND; -import static com.hotels.styx.api.LiveHttpResponse.response; import static java.util.stream.Collectors.toMap; import static org.slf4j.LoggerFactory.getLogger; /** * A configurable router. */ -public class UrlPatternRouter implements HttpHandler { +public class UrlPatternRouter implements WebServiceHandler { private static final Logger LOGGER = getLogger(UrlPatternRouter.class); private static final String PLACEHOLDERS_KEY = "UrlRouter.placeholders"; private final List alternatives; @@ -58,7 +58,7 @@ public static Map placeholders(HttpInterceptor.Context context) } @Override - public Eventual handle(LiveHttpRequest request, HttpInterceptor.Context context) { + public Eventual handle(HttpRequest request, HttpInterceptor.Context context) { for (RouteDescriptor route : alternatives) { if (request.method().equals(route.method())) { Matcher match = route.uriPattern().matcher(request.path()); @@ -91,22 +91,22 @@ public Eventual handle(LiveHttpRequest request, HttpIntercepto public static class Builder { private final List alternatives = new LinkedList<>(); - public Builder get(String uriPattern, HttpHandler handler) { + public Builder get(String uriPattern, WebServiceHandler handler) { alternatives.add(new RouteDescriptor(GET, uriPattern, handler)); return this; } - public Builder post(String uriPattern, HttpHandler handler) { + public Builder post(String uriPattern, WebServiceHandler handler) { alternatives.add(new RouteDescriptor(POST, uriPattern, handler)); return this; } - public Builder put(String uriPattern, HttpHandler handler) { + public Builder put(String uriPattern, WebServiceHandler handler) { alternatives.add(new RouteDescriptor(PUT, uriPattern, handler)); return this; } - public Builder delete(String uriPattern, HttpHandler handler) { + public Builder delete(String uriPattern, WebServiceHandler handler) { alternatives.add(new RouteDescriptor(DELETE, uriPattern, handler)); return this; } @@ -121,10 +121,10 @@ private static class RouteDescriptor { private final HttpMethod method; private final Pattern uriPattern; - private final HttpHandler handler; + private final WebServiceHandler handler; private final List placeholderNames; - public RouteDescriptor(HttpMethod method, String uriPattern, HttpHandler handler) { + public RouteDescriptor(HttpMethod method, String uriPattern, WebServiceHandler handler) { this.method = method; this.handler = handler; this.placeholderNames = placeholders(uriPattern); @@ -139,7 +139,7 @@ public Pattern uriPattern() { return uriPattern; } - public HttpHandler handler() { + public WebServiceHandler handler() { return handler; } diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/tasks/OriginsCommandHandler.java b/components/proxy/src/main/java/com/hotels/styx/admin/tasks/OriginsCommandHandler.java index 5890a667c5..6548fd11d4 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/tasks/OriginsCommandHandler.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/tasks/OriginsCommandHandler.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,27 +19,28 @@ import com.google.common.collect.ImmutableList; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; -import com.hotels.styx.api.LiveHttpResponse; +import com.hotels.styx.api.HttpInterceptor; +import com.hotels.styx.api.HttpRequest; +import com.hotels.styx.api.HttpResponse; import com.hotels.styx.api.Id; -import com.hotels.styx.api.LiveHttpRequest; import com.hotels.styx.api.extension.OriginsChangeListener; import com.hotels.styx.api.extension.OriginsSnapshot; -import com.hotels.styx.common.http.handler.BaseHttpHandler; import com.hotels.styx.client.origincommands.DisableOrigin; import com.hotels.styx.client.origincommands.EnableOrigin; import com.hotels.styx.client.origincommands.GetOriginsInventorySnapshot; +import com.hotels.styx.common.http.handler.BaseHttpHandler; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import static com.google.common.base.Strings.isNullOrEmpty; -import static com.hotels.styx.api.HttpResponse.response; import static com.hotels.styx.api.HttpHeaderNames.CONTENT_LENGTH; import static com.hotels.styx.api.HttpHeaderNames.LOCATION; -import static com.hotels.styx.api.Id.id; +import static com.hotels.styx.api.HttpResponse.response; import static com.hotels.styx.api.HttpResponseStatus.BAD_REQUEST; import static com.hotels.styx.api.HttpResponseStatus.TEMPORARY_REDIRECT; +import static com.hotels.styx.api.Id.id; import static java.lang.String.format; import static java.nio.charset.StandardCharsets.UTF_8; @@ -68,29 +69,26 @@ public OriginsCommandHandler(EventBus eventBus) { } @Override - public LiveHttpResponse doHandle(LiveHttpRequest request) { + public HttpResponse doHandle(HttpRequest request, HttpInterceptor.Context context) { String cmd = request.queryParam("cmd").orElse(""); String appId = request.queryParam("appId").orElse(""); String originId = request.queryParam("originId").orElse(""); if (!isValidCommand(cmd) || isNullOrEmpty(appId) || isNullOrEmpty(originId)) { return response(BAD_REQUEST) .body(MISSING_ERROR_MESSAGE, UTF_8) - .build() - .stream(); + .build(); } if (!originsInventorySnapshotMap.containsKey(id(appId))) { return response(BAD_REQUEST) .body(format(INVALID_APP_ID_FORMAT, appId), UTF_8) - .build() - .stream(); + .build(); } if (!validOriginId(id(appId), id(originId))) { return response(BAD_REQUEST) .body(format(INVALID_ORIGIN_ID_FORMAT, originId, appId), UTF_8) - .build() - .stream(); + .build(); } Object originCommand = newOriginCommand(cmd, id(appId), id(originId)); @@ -100,8 +98,7 @@ public LiveHttpResponse doHandle(LiveHttpRequest request) { return response(TEMPORARY_REDIRECT) .header(LOCATION, "/admin/origins/status") .header(CONTENT_LENGTH, 0) - .build() - .stream(); + .build(); } private boolean validOriginId(Id appId, Id originId) { diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/tasks/OriginsReloadCommandHandler.java b/components/proxy/src/main/java/com/hotels/styx/admin/tasks/OriginsReloadCommandHandler.java index f2991ed5fe..02d1254311 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/tasks/OriginsReloadCommandHandler.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/tasks/OriginsReloadCommandHandler.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,12 +17,11 @@ import com.fasterxml.jackson.databind.JsonMappingException; import com.hotels.styx.api.Eventual; -import com.hotels.styx.api.HttpResponse; -import com.hotels.styx.api.HttpHandler; import com.hotels.styx.api.HttpInterceptor; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; +import com.hotels.styx.api.HttpRequest; +import com.hotels.styx.api.HttpResponse; import com.hotels.styx.api.HttpResponseStatus; +import com.hotels.styx.api.WebServiceHandler; import com.hotels.styx.api.extension.service.BackendService; import com.hotels.styx.api.extension.service.spi.Registry; import org.slf4j.Logger; @@ -50,7 +49,7 @@ /** * Handler for the origins reloading command. */ -public class OriginsReloadCommandHandler implements HttpHandler { +public class OriginsReloadCommandHandler implements WebServiceHandler { private static final Logger LOG = getLogger(OriginsReloadCommandHandler.class); private final ExecutorService executor = newSingleThreadExecutor(); @@ -62,12 +61,12 @@ public OriginsReloadCommandHandler(Registry backendServicesRegis } @Override - public Eventual handle(LiveHttpRequest request, HttpInterceptor.Context context) { - return new Eventual<>(toPublisher(Observable.create(this::reload) + public Eventual handle(HttpRequest request, HttpInterceptor.Context context) { + return new Eventual<>(toPublisher(Observable.create(this::reload) .subscribeOn(Schedulers.from(executor)))); } - private void reload(Subscriber subscriber) { + private void reload(Subscriber subscriber) { backendServicesRegistry.reload() .handle((result, exception) -> { if (exception == null) { @@ -96,15 +95,14 @@ private Throwable mapError(Registry.ReloadResult result) { } } - private LiveHttpResponse okResponse(String content) { + private HttpResponse okResponse(String content) { return HttpResponse.response(OK) .header(CONTENT_TYPE, PLAIN_TEXT_UTF_8) .body(content, UTF_8) - .build() - .stream(); + .build(); } - private LiveHttpResponse errorResponse(Throwable cause) { + private HttpResponse errorResponse(Throwable cause) { String errorId = randomUUID().toString(); LOG.error("id={}", errorId, cause); @@ -129,12 +127,11 @@ private boolean deSerialisationError(Throwable cause) { return false; } - private LiveHttpResponse errorResponse(HttpResponseStatus code, String content) { + private HttpResponse errorResponse(HttpResponseStatus code, String content) { return HttpResponse.response(code) .header(CONTENT_TYPE, PLAIN_TEXT_UTF_8) .body(content, UTF_8) - .build() - .stream(); + .build(); } } diff --git a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/CurrentRequestsHandlerTest.java b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/CurrentRequestsHandlerTest.java index 7a4212ee1d..4f075d8474 100644 --- a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/CurrentRequestsHandlerTest.java +++ b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/CurrentRequestsHandlerTest.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,14 +15,16 @@ */ package com.hotels.styx.admin.handlers; +import com.hotels.styx.api.HttpInterceptor; +import com.hotels.styx.api.HttpRequest; import com.hotels.styx.api.HttpResponse; import com.hotels.styx.api.LiveHttpRequest; +import com.hotels.styx.server.HttpInterceptorContext; import com.hotels.styx.server.track.CurrentRequestTracker; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import reactor.core.publisher.Mono; -import static com.hotels.styx.api.LiveHttpRequest.get; import static java.lang.String.format; import static java.nio.charset.StandardCharsets.UTF_8; import static org.hamcrest.MatcherAssert.assertThat; @@ -32,11 +34,12 @@ public class CurrentRequestsHandlerTest { - private static final int MAX_CONTENT_SIZE = 10_000; - private LiveHttpRequest req1 = get("/requestId1").build(); + private final HttpInterceptor.Context ctx = HttpInterceptorContext.create(); + private LiveHttpRequest req1 = LiveHttpRequest.get("/requestId1").build(); private CurrentRequestTracker tracker = new CurrentRequestTracker(); private CurrentRequestsHandler handler; + private HttpRequest adminRequest = HttpRequest.get("/admin/x").build(); @BeforeMethod public void setUp() { @@ -48,7 +51,7 @@ public void setUp() { public void testStackTrace() { Thread.currentThread().setName("Test-Thread"); tracker.trackRequest(req1); - HttpResponse response = Mono.from(handler.doHandle(req1).aggregate(MAX_CONTENT_SIZE)).block(); + HttpResponse response = Mono.from(handler.handle(adminRequest, ctx)).block(); assertThat(response.bodyAs(UTF_8).contains("Test-Thread"), is(true)); } @@ -57,7 +60,7 @@ public void testStackTraceForSentRequest() { Thread.currentThread().setName("Test-Thread-1"); tracker.trackRequest(req1); tracker.markRequestAsSent(req1); - HttpResponse response = Mono.from(handler.doHandle(req1).aggregate(MAX_CONTENT_SIZE)).block(); + HttpResponse response = Mono.from(handler.handle(adminRequest, ctx)).block(); assertThat(response.bodyAs(UTF_8).contains("Request state: Waiting response from origin."), is(true)); } @@ -65,7 +68,7 @@ public void testStackTraceForSentRequest() { public void testWithStackTrace() { Thread.currentThread().setName("Test-Thread"); tracker.trackRequest(req1); - HttpResponse response = Mono.from(handler.doHandle(get("/req?withStackTrace=true").build()).aggregate(MAX_CONTENT_SIZE)).block(); + HttpResponse response = Mono.from(handler.handle(HttpRequest.get("/req?withStackTrace=true").build(), ctx)).block(); assertTrue(response.bodyAs(UTF_8).contains("Thread Info:")); assertTrue(response.bodyAs(UTF_8).contains("id=" + Thread.currentThread().getId() + " state")); @@ -74,7 +77,7 @@ public void testWithStackTrace() { @Test public void testWithoutStackTrace() { tracker.trackRequest(req1); - HttpResponse response = Mono.from(handler.doHandle(req1).aggregate(100000)).block(); + HttpResponse response = Mono.from(handler.handle(adminRequest, ctx)).block(); String body = response.bodyAs(UTF_8); diff --git a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/IndexHandlerTest.java b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/IndexHandlerTest.java index 26e7d391ac..d39d846ddf 100644 --- a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/IndexHandlerTest.java +++ b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/IndexHandlerTest.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,16 +15,15 @@ */ package com.hotels.styx.admin.handlers; -import com.hotels.styx.api.LiveHttpResponse; +import com.hotels.styx.api.HttpResponse; import com.hotels.styx.server.HttpInterceptorContext; import org.testng.annotations.Test; import reactor.core.publisher.Mono; import static com.hotels.styx.admin.handlers.IndexHandler.Link.link; +import static com.hotels.styx.api.HttpRequest.get; import static com.hotels.styx.api.HttpResponseStatus.OK; -import static com.hotels.styx.api.LiveHttpRequest.get; -import static com.hotels.styx.support.api.matchers.HttpResponseBodyMatcher.hasBody; -import static com.hotels.styx.support.api.matchers.HttpStatusMatcher.hasStatus; +import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; @@ -34,10 +33,10 @@ public class IndexHandlerTest { @Test public void printsTheRegisteredPaths() { - LiveHttpResponse response = Mono.from(handler.handle(get("/admin").build(), HttpInterceptorContext.create())).block(); - assertThat(response, hasStatus(OK)); + HttpResponse response = Mono.from(handler.handle(get("/admin").build(), HttpInterceptorContext.create())).block(); + assertThat(response.status(), is(OK)); assertThat(response.contentType().get(), is("text/html; charset=utf-8")); - assertThat(response, hasBody( + assertThat(response.bodyAs(UTF_8), is( "

    " + "
  1. Abc
  2. " + "
  3. Xyz
  4. " + diff --git a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/JVMMetricsHandlerTest.java b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/JVMMetricsHandlerTest.java index e9284c57a3..1b181db692 100644 --- a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/JVMMetricsHandlerTest.java +++ b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/JVMMetricsHandlerTest.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,8 +16,8 @@ package com.hotels.styx.admin.handlers; import com.codahale.metrics.Gauge; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; +import com.hotels.styx.api.HttpRequest; +import com.hotels.styx.api.HttpResponse; import com.hotels.styx.api.metrics.codahale.CodaHaleMetricRegistry; import com.hotels.styx.server.HttpInterceptorContext; import org.hamcrest.Description; @@ -33,10 +33,9 @@ import static com.google.common.collect.Iterables.all; import static com.hotels.styx.admin.handlers.JVMMetricsHandlerTest.StringsContains.containsStrings; import static com.hotels.styx.api.HttpHeaderValues.APPLICATION_JSON; +import static com.hotels.styx.api.HttpRequest.get; import static com.hotels.styx.api.HttpResponseStatus.OK; -import static com.hotels.styx.api.LiveHttpRequest.get; -import static com.hotels.styx.support.api.matchers.HttpResponseBodyMatcher.hasBody; -import static com.hotels.styx.support.api.matchers.HttpResponseStatusMatcher.hasStatus; +import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -66,30 +65,30 @@ public void setUp() { @Test public void respondsToRequestWithJsonResponse() { - LiveHttpResponse response = call(get("/jvm").build()); - assertThat(response, hasStatus(OK)); + HttpResponse response = call(get("/jvm").build()); + assertThat(response.status(), is(OK)); assertThat(response.contentType().get(), is(APPLICATION_JSON.toString())); } @Test public void doesNotExposeIrrelevantMetrics() { - LiveHttpResponse response = call(get("/jvm").build()); - assertThat(response, hasBody(not(containsString("irrelevant")))); + HttpResponse response = call(get("/jvm").build()); + assertThat(response.bodyAs(UTF_8), is(not(containsString("irrelevant")))); } @Test public void exposesAllMetricsStartingWithJvm() { - LiveHttpResponse response = call(get("/jvm").build()); - assertThat(response, hasBody(containsStrings( + HttpResponse response = call(get("/jvm").build()); + assertThat(response.bodyAs(UTF_8), containsStrings( "jvm.foo.gauge", "jvm.bar.counter", "jvm.baz.meter", "jvm.hello.timer", "jvm.world.histogram" - ))); + )); } - private LiveHttpResponse call(LiveHttpRequest request) { + private HttpResponse call(HttpRequest request) { return Mono.from(handler.handle(request, HttpInterceptorContext.create())).block(); } diff --git a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/JsonHandlerTest.java b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/JsonHandlerTest.java index 8e82017a9e..4a85bf0f61 100644 --- a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/JsonHandlerTest.java +++ b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/JsonHandlerTest.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.hotels.styx.api.Clock; -import com.hotels.styx.api.LiveHttpRequest; +import com.hotels.styx.api.HttpRequest; import com.hotels.styx.server.HttpInterceptorContext; import org.testng.annotations.Test; import reactor.core.publisher.Mono; @@ -26,7 +26,7 @@ import java.util.Optional; import java.util.function.Supplier; -import static com.hotels.styx.api.LiveHttpRequest.get; +import static com.hotels.styx.api.HttpRequest.get; import static java.lang.System.currentTimeMillis; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Arrays.asList; @@ -86,7 +86,7 @@ public void cachesSupplierValue() { @Test public void prettyPrintsOutputWhenPrettyIsSetToTrue() { - LiveHttpRequest request = get("/?pretty=true").build(); + HttpRequest request = get("/?pretty=true").build(); Supplier supplier = sequentialSupplier(new Convertible("foo", 456)); JsonHandler handler = new JsonHandler<>(supplier, Optional.empty()); @@ -105,10 +105,9 @@ private String response(JsonHandler handler) { return responseFor(handler, get("/").build()); } - private String responseFor(JsonHandler handler, LiveHttpRequest request) { + private String responseFor(JsonHandler handler, HttpRequest request) { return Mono.from( handler.handle(request, HttpInterceptorContext.create()) - .flatMap(response -> response.aggregate(1000000)) .map(response -> response.bodyAs(UTF_8)) ).block(); } diff --git a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/LoggingConfigurationHandlerTest.java b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/LoggingConfigurationHandlerTest.java index 47bd1f9a59..92afd5b896 100644 --- a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/LoggingConfigurationHandlerTest.java +++ b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/LoggingConfigurationHandlerTest.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,14 +20,14 @@ import com.hotels.styx.common.io.ClasspathResource; import com.hotels.styx.server.HttpInterceptorContext; import org.testng.annotations.Test; +import reactor.core.publisher.Mono; import java.io.IOException; import static com.hotels.styx.StartupConfig.newStartupConfigBuilder; -import static com.hotels.styx.api.LiveHttpRequest.get; +import static com.hotels.styx.api.HttpRequest.get; import static com.hotels.styx.api.HttpResponseStatus.OK; import static com.hotels.styx.support.ResourcePaths.fixturesHome; -import static com.hotels.styx.support.api.BlockingObservables.waitForResponse; import static com.hotels.styx.support.matchers.RegExMatcher.matchesRegex; import static java.nio.charset.StandardCharsets.UTF_8; import static org.hamcrest.MatcherAssert.assertThat; @@ -41,7 +41,7 @@ public void showsErrorMessageInContentIfLogConfigFileDoesNotExist() { .build(); LoggingConfigurationHandler handler = new LoggingConfigurationHandler(startupConfig.logConfigLocation()); - HttpResponse response = waitForResponse(handler.handle(get("/").build(), HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(get("/").build(), HttpInterceptorContext.create())).block(); assertThat(response.status(), is(OK)); assertThat(response.bodyAs(UTF_8), matchesRegex("Could not load resource=.*foo[\\\\/]bar'")); @@ -54,7 +54,7 @@ public void showsLogConfigContent() throws IOException { .build(); LoggingConfigurationHandler handler = new LoggingConfigurationHandler(startupConfig.logConfigLocation()); - HttpResponse response = waitForResponse(handler.handle(get("/").build(), HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(get("/").build(), HttpInterceptorContext.create())).block(); String expected = Resources.load(new ClasspathResource("conf/environment/styx-config-test.yml", LoggingConfigurationHandlerTest.class)); diff --git a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/MetricsHandlerTest.java b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/MetricsHandlerTest.java index ddaa608b8a..671717d7f2 100644 --- a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/MetricsHandlerTest.java +++ b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/MetricsHandlerTest.java @@ -20,14 +20,14 @@ import com.hotels.styx.server.HttpInterceptorContext; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import reactor.core.publisher.Mono; import java.util.Optional; import static com.google.common.net.MediaType.JSON_UTF_8; -import static com.hotels.styx.api.LiveHttpRequest.get; +import static com.hotels.styx.api.HttpRequest.get; import static com.hotels.styx.api.HttpResponseStatus.NOT_FOUND; import static com.hotels.styx.api.HttpResponseStatus.OK; -import static com.hotels.styx.support.api.BlockingObservables.waitForResponse; import static com.hotels.styx.support.matchers.RegExMatcher.matchesRegex; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.regex.Pattern.quote; @@ -46,7 +46,7 @@ public void setUp() { @Test public void respondsToRequestWithJsonResponse() { - HttpResponse response = waitForResponse(handler.handle(get("/admin/metrics").build(), HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(get("/admin/metrics").build(), HttpInterceptorContext.create())).block(); assertThat(response.status(), is(OK)); assertThat(response.contentType().get(), is(JSON_UTF_8.toString())); } @@ -54,7 +54,7 @@ public void respondsToRequestWithJsonResponse() { @Test public void exposesRegisteredMetrics() { metricRegistry.counter("foo").inc(); - HttpResponse response = waitForResponse(handler.handle(get("/admin/metrics").build(), HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(get("/admin/metrics").build(), HttpInterceptorContext.create())).block(); assertThat(response.bodyAs(UTF_8), matchesRegex(quote("{\"version\":\"") + "\\d+\\.\\d+\\.\\d+" + quote("\",\"gauges\":{},\"counters\":{\"foo\":{\"count\":1}},\"histograms\":{},\"meters\":{},\"timers\":{}}"))); @@ -66,13 +66,13 @@ public void canRequestMetricsBeginningWithPrefix() { metricRegistry.counter("foo.bar.baz").inc(1); metricRegistry.counter("foo.barx").inc(1); // should not be included - HttpResponse response = waitForResponse(handler.handle(get("/admin/metrics/foo.bar").build(), HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(get("/admin/metrics/foo.bar").build(), HttpInterceptorContext.create())).block(); assertThat(response.bodyAs(UTF_8), is("{\"foo.bar\":{\"count\":1},\"foo.bar.baz\":{\"count\":1}}")); } @Test public void ifNoMetricsMatchNameThen404NotFoundIsReturned() { - HttpResponse response = waitForResponse(handler.handle(get("/admin/metrics/foo.bar").build(), HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(get("/admin/metrics/foo.bar").build(), HttpInterceptorContext.create())).block(); assertThat(response.status(), is(NOT_FOUND)); } @@ -83,7 +83,7 @@ public void canSearchForTermWithinMetricName() { metricRegistry.counter("baz.bar.foo").inc(1); metricRegistry.counter("foo.baz.a").inc(1); - HttpResponse response = waitForResponse(handler.handle(get("/admin/metrics?filter=bar").build(), HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(get("/admin/metrics/?filter=bar").build(), HttpInterceptorContext.create())).block(); assertThat(response.status(), is(OK)); assertThat(response.bodyAs(UTF_8), is("{" + "\"baz.bar.foo\":{\"count\":1}," + @@ -100,7 +100,7 @@ public void canRequestMetricsBeginningWithPrefixAndSearchForTermTogether() { metricRegistry.counter("foo.baz.a").inc(1); metricRegistry.counter("foo.baz.a.bar").inc(1); - HttpResponse response = waitForResponse(handler.handle(get("/admin/metrics/foo?filter=bar").build(), HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(get("/admin/metrics/foo?filter=bar").build(), HttpInterceptorContext.create())).block(); assertThat(response.status(), is(OK)); assertThat(response.bodyAs(UTF_8), is("{" + "\"foo.bar.a\":{\"count\":1}," + @@ -114,7 +114,7 @@ public void searchReturnsEmptyJsonObjectWhenThereAreNoResults() { metricRegistry.counter("foo.bar.a").inc(1); metricRegistry.counter("foo.bar.b").inc(1); - HttpResponse response = waitForResponse(handler.handle(get("/admin/metrics?filter=notpresent").build(), HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(get("/admin/metrics/?filter=notpresent").build(), HttpInterceptorContext.create())).block(); assertThat(response.status(), is(OK)); assertThat(response.bodyAs(UTF_8), is("{}")); } diff --git a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/OriginsHandlerTest.java b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/OriginsHandlerTest.java index cf6f393b4e..3e39363f37 100644 --- a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/OriginsHandlerTest.java +++ b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/OriginsHandlerTest.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -24,19 +24,19 @@ import com.hotels.styx.proxy.backends.file.FileBackedBackendServicesRegistry; import com.hotels.styx.server.HttpInterceptorContext; import org.testng.annotations.Test; +import reactor.core.publisher.Mono; import java.io.IOException; import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES; import static com.google.common.net.MediaType.JSON_UTF_8; -import static com.hotels.styx.api.LiveHttpRequest.get; +import static com.hotels.styx.api.HttpRequest.get; import static com.hotels.styx.api.HttpResponseStatus.OK; import static com.hotels.styx.applications.BackendServices.newBackendServices; import static com.hotels.styx.applications.yaml.YamlApplicationsProvider.loadFromPath; import static com.hotels.styx.common.StyxFutures.await; import static com.hotels.styx.infrastructure.configuration.json.ObjectMappers.addStyxMixins; import static com.hotels.styx.support.ResourcePaths.fixturesHome; -import static com.hotels.styx.support.api.BlockingObservables.waitForResponse; import static com.hotels.styx.support.matchers.IsOptional.isValue; import static java.nio.charset.StandardCharsets.UTF_8; import static org.hamcrest.MatcherAssert.assertThat; @@ -54,7 +54,7 @@ public void respondsToRequestWithJsonResponse() throws IOException { Iterable expected = loadFromPath(originsFile).get(); withOriginsHandler(originsFile, handler -> { - HttpResponse response = waitForResponse(handler.handle(get("/admin/configuration/origins").build(), HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(get("/admin/configuration/origins").build(), HttpInterceptorContext.create())).block(); assertThat(response.status(), is(OK)); assertThat(response.contentType(), isValue(JSON_UTF_8.toString())); @@ -70,7 +70,7 @@ public void respondsWithEmptyArrayWhenNoOrigins() { Registry backendServicesRegistry = new MemoryBackedRegistry<>(); OriginsHandler handler = new OriginsHandler(backendServicesRegistry); - HttpResponse response = waitForResponse(handler.handle(get("/admin/configuration/origins").build(), HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(get("/admin/configuration/origins").build(), HttpInterceptorContext.create())).block(); assertThat(response.status(), is(OK)); assertThat(response.contentType(), isValue(JSON_UTF_8.toString())); @@ -85,7 +85,7 @@ public void healthCheckIsAbsentWhenNotConfigured() throws IOException { Iterable expected = loadFromPath(originsFile).get(); withOriginsHandler(originsFile, handler -> { - HttpResponse response = waitForResponse(handler.handle(get("/admin/configuration/origins").build(), HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(get("/admin/configuration/origins").build(), HttpInterceptorContext.create())).block(); assertThat(response.status(), is(OK)); assertThat(response.contentType(), isValue(JSON_UTF_8.toString())); diff --git a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/OriginsInventoryHandlerTest.java b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/OriginsInventoryHandlerTest.java index 12ff262ad8..8d413ccead 100644 --- a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/OriginsInventoryHandlerTest.java +++ b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/OriginsInventoryHandlerTest.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,8 +20,8 @@ import com.google.common.base.Charsets; import com.google.common.eventbus.EventBus; import com.hotels.styx.admin.tasks.StubConnectionPool; -import com.hotels.styx.api.HttpResponse; import com.hotels.styx.api.HttpHandler; +import com.hotels.styx.api.HttpResponse; import com.hotels.styx.api.Id; import com.hotels.styx.api.extension.Origin; import com.hotels.styx.api.extension.OriginsSnapshot; @@ -29,6 +29,7 @@ import com.hotels.styx.api.extension.loadbalancing.spi.LoadBalancingMetricSupplier; import com.hotels.styx.server.HttpInterceptorContext; import org.testng.annotations.Test; +import reactor.core.publisher.Mono; import java.io.IOException; import java.util.HashMap; @@ -36,12 +37,11 @@ import java.util.Map; import java.util.Set; -import static com.hotels.styx.api.LiveHttpRequest.get; +import static com.hotels.styx.api.HttpRequest.get; import static com.hotels.styx.api.Id.id; import static com.hotels.styx.api.extension.Origin.newOriginBuilder; import static com.hotels.styx.api.extension.RemoteHost.remoteHost; import static com.hotels.styx.infrastructure.configuration.json.ObjectMappers.addStyxMixins; -import static com.hotels.styx.support.api.BlockingObservables.waitForResponse; import static com.hotels.styx.support.matchers.RegExMatcher.matchesRegex; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Collections.emptySet; @@ -67,7 +67,7 @@ public void respondsWithCorrectSnapshot() throws IOException { eventBus.post(new OriginsSnapshot(APP_ID, pool(activeOrigins), pool(inactiveOrigins), pool(disabledOrigins))); - HttpResponse response = waitForResponse(handler.handle(get("/").build(), HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(get("/").build(), HttpInterceptorContext.create())).block(); assertThat(response.bodyAs(UTF_8).split("\n").length, is(1)); Map output = deserialiseJson(response.bodyAs(UTF_8)); @@ -91,7 +91,7 @@ public void prettyPrintsOriginsSnapshot() { eventBus.post(new OriginsSnapshot(APP_ID, pool(emptySet()), pool(emptySet()), pool(disabledOrigins))); - HttpResponse response = waitForResponse(handler.handle(get("/?pretty=1").build(), HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(get("/?pretty=1").build(), HttpInterceptorContext.create())).block(); assertThat(body(response).replace("\r\n", "\n"), matchesRegex("\\{\n" + " \"" + APP_ID + "\" : \\{\n" + @@ -113,7 +113,7 @@ public void prettyPrintsOriginsSnapshot() { public void returnsEmptyObjectWhenNoOrigins() { OriginsInventoryHandler handler = new OriginsInventoryHandler(new EventBus()); - HttpResponse response = waitForResponse(handler.handle(get("/").build(), HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(get("/").build(), HttpInterceptorContext.create())).block(); assertThat(response.bodyAs(UTF_8), is("{}")); } diff --git a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/PingHandlerTest.java b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/PingHandlerTest.java index 92699bc2b9..b7e62f942e 100644 --- a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/PingHandlerTest.java +++ b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/PingHandlerTest.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,10 +18,10 @@ import com.hotels.styx.api.HttpResponse; import com.hotels.styx.server.HttpInterceptorContext; import org.testng.annotations.Test; +import reactor.core.publisher.Mono; -import static com.hotels.styx.api.LiveHttpRequest.get; +import static com.hotels.styx.api.HttpRequest.get; import static com.hotels.styx.api.HttpResponseStatus.OK; -import static com.hotels.styx.support.api.BlockingObservables.waitForResponse; import static com.hotels.styx.support.api.matchers.HttpHeadersMatcher.isNotCacheable; import static java.nio.charset.StandardCharsets.UTF_8; import static org.hamcrest.MatcherAssert.assertThat; @@ -32,7 +32,7 @@ public class PingHandlerTest { @Test public void respondsPongToPingRequest() { - HttpResponse response = waitForResponse(handler.handle(get("/ping").build(), HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(get("/ping").build(), HttpInterceptorContext.create())).block(); assertThat(response.status(), is(OK)); assertThat(response.headers(), isNotCacheable()); assertThat(response.contentType().get(), is("text/plain; charset=utf-8")); diff --git a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/PluginListHandlerTest.java b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/PluginListHandlerTest.java index 035bf184b7..e84557b65c 100644 --- a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/PluginListHandlerTest.java +++ b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/PluginListHandlerTest.java @@ -20,12 +20,12 @@ import com.hotels.styx.proxy.plugin.NamedPlugin; import com.hotels.styx.server.HttpInterceptorContext; import org.testng.annotations.Test; +import reactor.core.publisher.Mono; +import static com.hotels.styx.api.HttpRequest.get; import static com.hotels.styx.api.HttpResponseStatus.OK; -import static com.hotels.styx.api.LiveHttpRequest.get; import static com.hotels.styx.api.plugins.spi.Plugin.PASS_THROUGH; import static com.hotels.styx.proxy.plugin.NamedPlugin.namedPlugin; -import static com.hotels.styx.support.api.BlockingObservables.waitForResponse; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; @@ -50,7 +50,7 @@ public void showsEnabledAndDisabledPlugins() { PluginListHandler handler = new PluginListHandler(configStore); - HttpResponse response = waitForResponse(handler.handle(get("/").build(), HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(get("/").build(), HttpInterceptorContext.create())).block(); assertThat(response.status(), is(OK)); assertThat(response.bodyAs(UTF_8), is("" + diff --git a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/PluginToggleHandlerTest.java b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/PluginToggleHandlerTest.java index c896cb84c3..3021960cf8 100644 --- a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/PluginToggleHandlerTest.java +++ b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/PluginToggleHandlerTest.java @@ -15,14 +15,15 @@ */ package com.hotels.styx.admin.handlers; +import com.hotels.styx.api.HttpRequest; import com.hotels.styx.api.HttpResponse; -import com.hotels.styx.api.LiveHttpRequest; import com.hotels.styx.api.plugins.spi.Plugin; import com.hotels.styx.configstore.ConfigStore; import com.hotels.styx.proxy.plugin.NamedPlugin; import com.hotels.styx.server.HttpInterceptorContext; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import reactor.core.publisher.Mono; import java.util.List; @@ -31,7 +32,6 @@ import static com.hotels.styx.api.HttpResponseStatus.NOT_FOUND; import static com.hotels.styx.api.HttpResponseStatus.OK; import static com.hotels.styx.proxy.plugin.NamedPlugin.namedPlugin; -import static com.hotels.styx.support.api.BlockingObservables.waitForResponse; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; @@ -61,9 +61,9 @@ public void setUp() { @Test public void enablesDisabledPlugin() { - LiveHttpRequest request = put("/foo/off/enabled").body("true", UTF_8).build().stream(); + HttpRequest request = put("/foo/off/enabled").body("true", UTF_8).build(); - HttpResponse response = waitForResponse(handler.handle(request, HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(request, HttpInterceptorContext.create())).block(); assertThat(response.status(), is(OK)); assertThat(body(response), is("{\"message\":\"State of 'off' changed to 'enabled'\",\"plugin\":{\"name\":\"off\",\"state\":\"enabled\"}}")); @@ -73,9 +73,9 @@ public void enablesDisabledPlugin() { @Test public void disablesEnabledPlugin() { - LiveHttpRequest request = put("/foo/on/enabled").body("false", UTF_8).build().stream(); + HttpRequest request = put("/foo/on/enabled").body("false", UTF_8).build(); - HttpResponse response = waitForResponse(handler.handle(request, HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(request, HttpInterceptorContext.create())).block(); assertThat(response.status(), is(OK)); assertThat(body(response), is("{\"message\":\"State of 'on' changed to 'disabled'\",\"plugin\":{\"name\":\"on\",\"state\":\"disabled\"}}")); @@ -85,9 +85,9 @@ public void disablesEnabledPlugin() { @Test public void notifiesWhenPluginAlreadyDisabled() { - LiveHttpRequest request = put("/foo/off/enabled").body("false", UTF_8).build().stream(); + HttpRequest request = put("/foo/off/enabled").body("false", UTF_8).build(); - HttpResponse response = waitForResponse(handler.handle(request, HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(request, HttpInterceptorContext.create())).block(); assertThat(response.status(), is(OK)); assertThat(body(response), is("{\"message\":\"State of 'off' was already 'disabled'\",\"plugin\":{\"name\":\"off\",\"state\":\"disabled\"}}")); @@ -97,9 +97,9 @@ public void notifiesWhenPluginAlreadyDisabled() { @Test public void notifiesWhenPluginAlreadyEnabled() { - LiveHttpRequest request = put("/foo/on/enabled").body("true", UTF_8).build().stream(); + HttpRequest request = put("/foo/on/enabled").body("true", UTF_8).build(); - HttpResponse response = waitForResponse(handler.handle(request, HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(request, HttpInterceptorContext.create())).block(); assertThat(response.status(), is(OK)); assertThat(body(response), is("{\"message\":\"State of 'on' was already 'enabled'\",\"plugin\":{\"name\":\"on\",\"state\":\"enabled\"}}")); @@ -109,9 +109,9 @@ public void notifiesWhenPluginAlreadyEnabled() { @Test public void saysBadRequestWhenUrlIsInvalid() { - LiveHttpRequest request = put("/foo//enabled").body("true", UTF_8).build().stream(); + HttpRequest request = put("/foo//enabled").body("true", UTF_8).build(); - HttpResponse response = waitForResponse(handler.handle(request, HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(request, HttpInterceptorContext.create())).block(); assertThat(response.status(), is(BAD_REQUEST)); assertThat(body(response), is("Invalid URL")); @@ -121,9 +121,9 @@ public void saysBadRequestWhenUrlIsInvalid() { @Test public void saysBadRequestWhenNoStateSpecified() { - LiveHttpRequest request = put("/foo/on/enabled").build().stream(); + HttpRequest request = put("/foo/on/enabled").build(); - HttpResponse response = waitForResponse(handler.handle(request, HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(request, HttpInterceptorContext.create())).block(); assertThat(response.status(), is(BAD_REQUEST)); assertThat(body(response), is("No such state: only 'true' and 'false' are valid.")); @@ -133,9 +133,9 @@ public void saysBadRequestWhenNoStateSpecified() { @Test public void saysBadRequestWhenPluginDoesNotExist() { - LiveHttpRequest request = put("/foo/nonexistent/enabled").body("true", UTF_8).build().stream(); + HttpRequest request = put("/foo/nonexistent/enabled").body("true", UTF_8).build(); - HttpResponse response = waitForResponse(handler.handle(request, HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(request, HttpInterceptorContext.create())).block(); assertThat(response.status(), is(NOT_FOUND)); assertThat(body(response), is("No such plugin: pluginName=nonexistent")); @@ -145,9 +145,9 @@ public void saysBadRequestWhenPluginDoesNotExist() { @Test public void saysBadRequestWhenValueIsInvalid() { - LiveHttpRequest request = put("/foo/off/enabled").body("invalid", UTF_8).build().stream(); + HttpRequest request = put("/foo/off/enabled").body("invalid", UTF_8).build(); - HttpResponse response = waitForResponse(handler.handle(request, HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(request, HttpInterceptorContext.create())).block(); assertThat(response.status(), is(BAD_REQUEST)); assertThat(body(response), is("No such state: only 'true' and 'false' are valid.")); diff --git a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/StartupConfigHandlerTest.java b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/StartupConfigHandlerTest.java index 1d257e3320..0cc279ff27 100644 --- a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/StartupConfigHandlerTest.java +++ b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/StartupConfigHandlerTest.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,11 +19,11 @@ import com.hotels.styx.api.HttpResponse; import com.hotels.styx.server.HttpInterceptorContext; import org.testng.annotations.Test; +import reactor.core.publisher.Mono; import static com.hotels.styx.StartupConfig.newStartupConfigBuilder; -import static com.hotels.styx.api.LiveHttpRequest.get; +import static com.hotels.styx.api.HttpRequest.get; import static com.hotels.styx.api.HttpResponseStatus.OK; -import static com.hotels.styx.support.api.BlockingObservables.waitForResponse; import static com.hotels.styx.support.matchers.RegExMatcher.matchesRegex; import static java.nio.charset.StandardCharsets.UTF_8; import static org.hamcrest.MatcherAssert.assertThat; @@ -40,7 +40,7 @@ public void outputsExpectedData(){ StartupConfigHandler handler = new StartupConfigHandler(startupConfig); - HttpResponse response = waitForResponse(handler.handle(get("/").build(), HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(get("/").build(), HttpInterceptorContext.create())).block(); assertThat(response.status(), is(OK)); assertThat(response.bodyAs(UTF_8), matchesRegex("" + diff --git a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/StyxConfigurationHandlerTest.java b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/StyxConfigurationHandlerTest.java index d47ee6102b..40b9a62bac 100644 --- a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/StyxConfigurationHandlerTest.java +++ b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/StyxConfigurationHandlerTest.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,17 +17,16 @@ import com.hotels.styx.StyxConfig; import com.hotels.styx.api.Eventual; +import com.hotels.styx.api.HttpRequest; import com.hotels.styx.api.HttpResponse; -import com.hotels.styx.api.LiveHttpResponse; -import com.hotels.styx.api.LiveHttpRequest; import com.hotels.styx.server.HttpInterceptorContext; import org.testng.annotations.Test; +import reactor.core.publisher.Mono; import java.io.File; -import static com.hotels.styx.api.LiveHttpRequest.get; +import static com.hotels.styx.api.HttpRequest.get; import static com.hotels.styx.support.ResourcePaths.fixturesHome; -import static com.hotels.styx.support.api.BlockingObservables.waitForResponse; import static java.nio.charset.StandardCharsets.UTF_8; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; @@ -35,6 +34,25 @@ public class StyxConfigurationHandlerTest { private static final String ORIGINS_FILE = fixturesHome() + "conf/origins/origins-development.yml"; + private static Eventual browseForCurrentConfiguration(String yaml, boolean pretty) { + return configurationBrowserHandler(yaml).handle(adminRequest(pretty), HttpInterceptorContext.create()); + } + + private static HttpRequest adminRequest(boolean pretty) { + if (pretty) { + return get("/?pretty=").build(); + } else { + return get("/").build(); + } + } + + private static String formatPathLikeYamlConfig(String path) { + if (File.separator.equals("\\")) { + return path.replace("\\", "\\\\"); + } + return path; + } + @Test public void outputsConfiguration() { String yaml = "" + @@ -46,7 +64,7 @@ public void outputsConfiguration() { " strategy: ROUND_ROBIN\n" + "originsFile: " + ORIGINS_FILE + "\n"; - HttpResponse adminPageResponse = waitForResponse(browseForCurrentConfiguration(yaml, false)); + HttpResponse adminPageResponse = Mono.from(browseForCurrentConfiguration(yaml, false)).block(); assertThat(adminPageResponse.bodyAs(UTF_8), is("{\"proxy\":{\"connectors\":{\"http\":{\"port\":8080}}}," + "\"loadBalancing\":{\"strategy\":\"ROUND_ROBIN\"},\"originsFile\":\"" + @@ -64,7 +82,7 @@ public void outputsPrettifiedConfiguration() throws Exception { " strategy: ROUND_ROBIN\n" + "originsFile: " + ORIGINS_FILE + "\n"; - HttpResponse adminPageResponse = waitForResponse(browseForCurrentConfiguration(yaml, true)); + HttpResponse adminPageResponse = Mono.from(browseForCurrentConfiguration(yaml, true)).block(); assertThat(adminPageResponse.bodyAs(UTF_8), is("{\n" + " \"proxy\" : {\n" + @@ -81,25 +99,6 @@ public void outputsPrettifiedConfiguration() throws Exception { "}")); } - private static String formatPathLikeYamlConfig(String path) { - if (File.separator.equals("\\")) { - return path.replace("\\", "\\\\"); - } - return path; - } - - private static Eventual browseForCurrentConfiguration(String yaml, boolean pretty) { - return configurationBrowserHandler(yaml).handle(adminRequest(pretty), HttpInterceptorContext.create()); - } - - private static LiveHttpRequest adminRequest(boolean pretty) { - if (pretty) { - return get("/?pretty=").build(); - } else { - return get("/").build(); - } - } - private static StyxConfigurationHandler configurationBrowserHandler(String yaml) { return new StyxConfigurationHandler(new StyxConfig(yaml)); } diff --git a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/ThreadsHandlerTest.java b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/ThreadsHandlerTest.java index 93205d6501..026c6d0bdb 100644 --- a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/ThreadsHandlerTest.java +++ b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/ThreadsHandlerTest.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,10 +19,10 @@ import com.hotels.styx.api.HttpResponse; import com.hotels.styx.server.HttpInterceptorContext; import org.testng.annotations.Test; +import reactor.core.publisher.Mono; +import static com.hotels.styx.api.HttpRequest.get; import static com.hotels.styx.api.HttpResponseStatus.OK; -import static com.hotels.styx.api.LiveHttpRequest.get; -import static com.hotels.styx.support.api.BlockingObservables.waitForResponse; import static com.hotels.styx.support.api.matchers.HttpHeadersMatcher.isNotCacheable; import static java.nio.charset.StandardCharsets.UTF_8; import static org.hamcrest.MatcherAssert.assertThat; @@ -34,7 +34,7 @@ public class ThreadsHandlerTest { @Test public void dumpsCurrentThreadsState() { - HttpResponse response = waitForResponse(handler.handle(get("/threads").build(), HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(get("/threads").build(), HttpInterceptorContext.create())).block(); assertThat(response.status(), is(OK)); assertThat(response.headers(), isNotCacheable()); assertThat(response.contentType().get(), is("text/plain; charset=utf-8")); diff --git a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/VersionTextHandlerTest.java b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/VersionTextHandlerTest.java index ccf0c30028..04025da146 100644 --- a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/VersionTextHandlerTest.java +++ b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/VersionTextHandlerTest.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,9 +20,9 @@ import com.hotels.styx.common.io.ResourceFactory; import com.hotels.styx.server.HttpInterceptorContext; import org.testng.annotations.Test; +import reactor.core.publisher.Mono; -import static com.hotels.styx.api.LiveHttpRequest.get; -import static com.hotels.styx.support.api.BlockingObservables.waitForResponse; +import static com.hotels.styx.api.HttpRequest.get; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Arrays.stream; import static java.util.stream.Collectors.toList; @@ -34,7 +34,7 @@ public class VersionTextHandlerTest { public void canProvideASingleVersionTextFile() { VersionTextHandler handler = new VersionTextHandler(resources("classpath:/versions/version1.txt")); - HttpResponse response = waitForResponse(handler.handle( get("/version.txt").build(), HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(get("/version.txt").build(), HttpInterceptorContext.create())).block(); assertThat(response.bodyAs(UTF_8), is("foo\n")); } @@ -43,7 +43,7 @@ public void canProvideASingleVersionTextFile() { public void canCombineVersionTextFiles() { VersionTextHandler handler = new VersionTextHandler(resources("classpath:/versions/version1.txt", "classpath:/versions/version2.txt")); - HttpResponse response = waitForResponse(handler.handle( get("/version.txt").build(), HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle( get("/version.txt").build(), HttpInterceptorContext.create())).block(); assertThat(response.bodyAs(UTF_8), is("foo\nbar\n")); } @@ -52,7 +52,7 @@ public void canCombineVersionTextFiles() { public void nonExistentFilesAreIgnored() { VersionTextHandler handler = new VersionTextHandler(resources("classpath:/versions/version1.txt", "version-nonexistent.txt")); - HttpResponse response = waitForResponse(handler.handle( get("/version.txt").build(), HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle( get("/version.txt").build(), HttpInterceptorContext.create())).block(); assertThat(response.bodyAs(UTF_8), is("foo\n")); } @@ -61,7 +61,7 @@ public void nonExistentFilesAreIgnored() { public void returnsUnknownVersionIfNoFilesAreFound() { VersionTextHandler handler = new VersionTextHandler(resources("version-nonexistent1.txt", "version-nonexistent2.txt")); - HttpResponse response = waitForResponse(handler.handle(get("/version.txt").build(), HttpInterceptorContext.create())); + HttpResponse response = Mono.from(handler.handle(get("/version.txt").build(), HttpInterceptorContext.create())).block(); assertThat(response.bodyAs(UTF_8), is("Unknown version\n")); } diff --git a/components/proxy/src/test/java/com/hotels/styx/admin/tasks/OriginsCommandHandlerTest.java b/components/proxy/src/test/java/com/hotels/styx/admin/tasks/OriginsCommandHandlerTest.java index fc4dffd4d8..e02108ab81 100644 --- a/components/proxy/src/test/java/com/hotels/styx/admin/tasks/OriginsCommandHandlerTest.java +++ b/components/proxy/src/test/java/com/hotels/styx/admin/tasks/OriginsCommandHandlerTest.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,8 +18,8 @@ import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import com.hotels.styx.api.HttpHandler; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; +import com.hotels.styx.api.HttpRequest; +import com.hotels.styx.api.HttpResponse; import com.hotels.styx.api.extension.Origin; import com.hotels.styx.api.extension.OriginsSnapshot; import com.hotels.styx.api.extension.RemoteHost; @@ -40,9 +40,8 @@ import static com.hotels.styx.api.Id.id; import static com.hotels.styx.api.extension.Origin.newOriginBuilder; import static com.hotels.styx.api.extension.RemoteHost.remoteHost; -import static com.hotels.styx.support.api.matchers.HttpResponseBodyMatcher.hasBody; -import static com.hotels.styx.support.api.matchers.HttpResponseStatusMatcher.hasStatus; import static java.lang.String.format; +import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Collections.singleton; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; @@ -84,37 +83,37 @@ public void propagatesTheOriginsEnableCommand() { @Test public void returnsProperErrorMessageForBadCommand() { - LiveHttpResponse response = post("/admin/tasks/origins?cmd=foo&appId=foo&originId=bar"); - assertThat(response, hasStatus(BAD_REQUEST)); - assertThat(response, hasBody("cmd, appId and originId are all required parameters. cmd can be enable_origin|disable_origin")); + HttpResponse response = post("/admin/tasks/origins?cmd=foo&appId=foo&originId=bar"); + assertThat(response.status(), is(BAD_REQUEST)); + assertThat(response.bodyAs(UTF_8), is("cmd, appId and originId are all required parameters. cmd can be enable_origin|disable_origin")); } @Test public void returnsProperErrorMessageForMissingAppId() { - LiveHttpResponse response = post("/admin/tasks/origins?cmd=enable_origin&originId=bar"); - assertThat(response, hasStatus(BAD_REQUEST)); - assertThat(response, hasBody("cmd, appId and originId are all required parameters. cmd can be enable_origin|disable_origin")); + HttpResponse response = post("/admin/tasks/origins?cmd=enable_origin&originId=bar"); + assertThat(response.status(), is(BAD_REQUEST)); + assertThat(response.bodyAs(UTF_8), is("cmd, appId and originId are all required parameters. cmd can be enable_origin|disable_origin")); } @Test public void returnsProperErrorMessageForMissingOriginId() { - LiveHttpResponse response = post("/admin/tasks/origins?cmd=disable_origin&appId=foo"); - assertThat(response, hasStatus(BAD_REQUEST)); - assertThat(response, hasBody("cmd, appId and originId are all required parameters. cmd can be enable_origin|disable_origin")); + HttpResponse response = post("/admin/tasks/origins?cmd=disable_origin&appId=foo"); + assertThat(response.status(), is(BAD_REQUEST)); + assertThat(response.bodyAs(UTF_8), is("cmd, appId and originId are all required parameters. cmd can be enable_origin|disable_origin")); } @Test(dataProvider = "nonexistentAppOrOriginId") public void failsToIssueTheCommandForNonexistentAppId(String appId) { - LiveHttpResponse response = post(format("/admin/tasks/origins?cmd=disable_origin&appId=%s&originId=bar", appId)); - assertThat(response, hasStatus(BAD_REQUEST)); - assertThat(response, hasBody(format("application with id=%s is not found", appId))); + HttpResponse response = post(format("/admin/tasks/origins?cmd=disable_origin&appId=%s&originId=bar", appId)); + assertThat(response.status(), is(BAD_REQUEST)); + assertThat(response.bodyAs(UTF_8), is(format("application with id=%s is not found", appId))); } @Test(dataProvider = "nonexistentAppOrOriginId") public void failsToIssueTheCommandForNonexistentOriginId(String originId) { - LiveHttpResponse response = post(format("/admin/tasks/origins?cmd=disable_origin&appId=activeAppId&originId=%s", originId)); - assertThat(response, hasStatus(BAD_REQUEST)); - assertThat(response, hasBody(format("origin with id=%s is not found for application=activeAppId", originId))); + HttpResponse response = post(format("/admin/tasks/origins?cmd=disable_origin&appId=activeAppId&originId=%s", originId)); + assertThat(response.status(), is(BAD_REQUEST)); + assertThat(response.bodyAs(UTF_8), is(format("origin with id=%s is not found for application=activeAppId", originId))); } @DataProvider(name = "nonexistentAppOrOriginId") @@ -122,8 +121,8 @@ protected Object[][] nonexistentAppOrOriginId() { return new Object[][]{{"foo"}, {"bar"}}; } - private LiveHttpResponse post(String path) { - LiveHttpRequest request = LiveHttpRequest.post(path).build(); + private HttpResponse post(String path) { + HttpRequest request = HttpRequest.post(path).build(); return Mono.from(originsCommand.handle(request, HttpInterceptorContext.create())).block(); } diff --git a/components/proxy/src/test/java/com/hotels/styx/admin/tasks/OriginsReloadCommandHandlerTest.java b/components/proxy/src/test/java/com/hotels/styx/admin/tasks/OriginsReloadCommandHandlerTest.java index b4b0ba2945..f031853884 100644 --- a/components/proxy/src/test/java/com/hotels/styx/admin/tasks/OriginsReloadCommandHandlerTest.java +++ b/components/proxy/src/test/java/com/hotels/styx/admin/tasks/OriginsReloadCommandHandlerTest.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,23 +16,23 @@ package com.hotels.styx.admin.tasks; import com.fasterxml.jackson.databind.JsonMappingException; -import com.hotels.styx.api.HttpResponse; import com.hotels.styx.api.HttpInterceptor; +import com.hotels.styx.api.HttpResponse; import com.hotels.styx.api.extension.service.BackendService; import com.hotels.styx.api.extension.service.spi.Registry; import com.hotels.styx.api.extension.service.spi.Registry.ReloadResult; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import reactor.core.publisher.Mono; import java.util.concurrent.CompletableFuture; -import static com.hotels.styx.api.LiveHttpRequest.get; +import static com.hotels.styx.api.HttpRequest.get; import static com.hotels.styx.api.HttpResponseStatus.BAD_REQUEST; import static com.hotels.styx.api.HttpResponseStatus.INTERNAL_SERVER_ERROR; import static com.hotels.styx.api.HttpResponseStatus.OK; import static com.hotels.styx.api.extension.service.spi.Registry.ReloadResult.reloaded; import static com.hotels.styx.api.extension.service.spi.Registry.ReloadResult.unchanged; -import static com.hotels.styx.support.api.BlockingObservables.waitForResponse; import static com.hotels.styx.support.matchers.RegExMatcher.matchesRegex; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.concurrent.CompletableFuture.completedFuture; @@ -55,7 +55,7 @@ public void setUp() { public void returnsWithConfirmationWhenChangesArePerformed() { mockRegistryReload(completedFuture(reloaded("ok"))); - HttpResponse response = waitForResponse(handler.handle(get("/").build(), mock(HttpInterceptor.Context.class))); + HttpResponse response = Mono.from(handler.handle(get("/").build(), mock(HttpInterceptor.Context.class))).block(); assertThat(response.status(), is(OK)); assertThat(response.bodyAs(UTF_8), is("Origins reloaded successfully.\n")); @@ -65,7 +65,7 @@ public void returnsWithConfirmationWhenChangesArePerformed() { public void returnsWithInformationWhenChangesAreUnnecessary() { mockRegistryReload(completedFuture(unchanged("this test returns 'no meaningful changes'"))); - HttpResponse response = waitForResponse(handler.handle(get("/").build(), mock(HttpInterceptor.Context.class))); + HttpResponse response = Mono.from(handler.handle(get("/").build(), mock(HttpInterceptor.Context.class))).block(); assertThat(response.status(), is(OK)); assertThat(response.bodyAs(UTF_8), is("Origins were not reloaded because this test returns 'no meaningful changes'.\n")); @@ -75,7 +75,7 @@ public void returnsWithInformationWhenChangesAreUnnecessary() { public void returnsWithInformationWhenJsonErrorOccursDuringReload() { mockRegistryReload(failedFuture(new RuntimeException(new JsonMappingException("simulated error")))); - HttpResponse response = waitForResponse(handler.handle(get("/").build(), mock(HttpInterceptor.Context.class))); + HttpResponse response = Mono.from(handler.handle(get("/").build(), mock(HttpInterceptor.Context.class))).block(); assertThat(response.status(), is(BAD_REQUEST)); assertThat(response.bodyAs(UTF_8), is(matchesRegex("There was an error processing your request. It has been logged \\(ID [0-9a-f-]+\\)\\.\n"))); @@ -85,7 +85,7 @@ public void returnsWithInformationWhenJsonErrorOccursDuringReload() { public void returnsWithInformationWhenErrorDuringReload() { mockRegistryReload(failedFuture(new RuntimeException(new RuntimeException("simulated error")))); - HttpResponse response = waitForResponse(handler.handle(get("/").build(), mock(HttpInterceptor.Context.class))); + HttpResponse response = Mono.from(handler.handle(get("/").build(), mock(HttpInterceptor.Context.class))).block(); assertThat(response.status(), is(INTERNAL_SERVER_ERROR)); assertThat(response.bodyAs(UTF_8), is(matchesRegex("There was an error processing your request. It has been logged \\(ID [0-9a-f-]+\\)\\.\n"))); diff --git a/components/proxy/src/test/java/com/hotels/styx/proxy/StyxProxyTest.java b/components/proxy/src/test/java/com/hotels/styx/proxy/StyxProxyTest.java index 6377ecb22a..80b3ea9b5c 100644 --- a/components/proxy/src/test/java/com/hotels/styx/proxy/StyxProxyTest.java +++ b/components/proxy/src/test/java/com/hotels/styx/proxy/StyxProxyTest.java @@ -17,12 +17,13 @@ import com.google.common.collect.ImmutableList; import com.hotels.styx.api.Eventual; +import com.hotels.styx.api.HttpInterceptor; import com.hotels.styx.api.HttpRequest; import com.hotels.styx.api.HttpResponse; -import com.hotels.styx.client.HttpClient; -import com.hotels.styx.api.HttpInterceptor; import com.hotels.styx.api.LiveHttpResponse; +import com.hotels.styx.client.HttpClient; import com.hotels.styx.client.StyxHttpClient; +import com.hotels.styx.common.http.handler.HttpAggregator; import com.hotels.styx.infrastructure.configuration.yaml.YamlConfig; import com.hotels.styx.routing.handlers.HttpInterceptorPipeline; import com.hotels.styx.server.HttpConnectorConfig; @@ -79,7 +80,10 @@ public void startsServerWithHttpConnector() { HttpServer server = NettyServerBuilder.newBuilder() .setHttpConnector(connector(0)) - .handlerFactory(() -> new HttpInterceptorPipeline(ImmutableList.of(echoInterceptor), handler::handle, false)) + .handlerFactory(() -> new HttpInterceptorPipeline( + ImmutableList.of(echoInterceptor), + (request, context) -> new HttpAggregator(new StandardHttpRouter()).handle(request, context), + false)) .build(); server.startAsync().awaitRunning(); assertThat("Server should be running", server.isRunning()); diff --git a/components/proxy/src/test/kotlin/com/hotels/styx/admin/handlers/UrlPatternRouterTest.kt b/components/proxy/src/test/kotlin/com/hotels/styx/admin/handlers/UrlPatternRouterTest.kt index 184e81beed..3c500c419d 100644 --- a/components/proxy/src/test/kotlin/com/hotels/styx/admin/handlers/UrlPatternRouterTest.kt +++ b/components/proxy/src/test/kotlin/com/hotels/styx/admin/handlers/UrlPatternRouterTest.kt @@ -18,20 +18,20 @@ package com.hotels.styx.admin.handlers import ch.qos.logback.classic.Level import com.hotels.styx.api.Eventual import com.hotels.styx.api.HttpInterceptor +import com.hotels.styx.api.HttpRequest +import com.hotels.styx.api.HttpRequest.post +import com.hotels.styx.api.HttpResponse +import com.hotels.styx.api.HttpResponse.response import com.hotels.styx.api.HttpResponseStatus.ACCEPTED import com.hotels.styx.api.HttpResponseStatus.CREATED import com.hotels.styx.api.HttpResponseStatus.INTERNAL_SERVER_ERROR import com.hotels.styx.api.HttpResponseStatus.NO_CONTENT import com.hotels.styx.api.HttpResponseStatus.OK -import com.hotels.styx.api.LiveHttpRequest -import com.hotels.styx.api.LiveHttpRequest.post -import com.hotels.styx.api.LiveHttpResponse -import com.hotels.styx.api.LiveHttpResponse.response import com.hotels.styx.server.HttpInterceptorContext import com.hotels.styx.support.matchers.LoggingTestSupport import io.kotlintest.shouldBe import io.kotlintest.specs.FeatureSpec -import reactor.core.publisher.Mono +import reactor.core.publisher.toMono import java.util.Optional import java.util.concurrent.atomic.AtomicReference @@ -39,67 +39,79 @@ class UrlPatternRouterTest : FeatureSpec({ val LOGGER = LoggingTestSupport(UrlPatternRouter::class.java); val router = UrlPatternRouter.Builder() - .get("/admin/apps/:appId") { request, context -> Eventual.of( - response(OK) - .header("appId", UrlPatternRouter.placeholders(context)["appId"]) - .build()) + .get("/admin/apps/:appId") { request, context -> + Eventual.of( + response(OK) + .header("appId", UrlPatternRouter.placeholders(context)["appId"]) + .build()) } - .get("/admin/apps/:appId/origin/:originId") { request, context -> Eventual.of( - response(OK) - .header("appId", UrlPatternRouter.placeholders(context)["appId"]) - .header("originId", UrlPatternRouter.placeholders(context)["originId"]) - .build()) + .get("/admin/apps/:appId/origin/:originId") { request, context -> + Eventual.of( + response(OK) + .header("appId", UrlPatternRouter.placeholders(context)["appId"]) + .header("originId", UrlPatternRouter.placeholders(context)["originId"]) + .build()) + } + .post("/admin/apps/:appId") { request, context -> + Eventual.of( + response(CREATED) + .header("appId", UrlPatternRouter.placeholders(context)["appId"]) + .build() + ) + } + .post("/admin/apps/:appId/origin/:originId") { request, context -> + Eventual.of( + response(CREATED) + .header("appId", UrlPatternRouter.placeholders(context)["appId"]) + .header("originId", UrlPatternRouter.placeholders(context)["originId"]) + .build() + ) + } + .put("/admin/apps/:appId") { request, context -> + Eventual.of( + response(NO_CONTENT) + .header("appId", UrlPatternRouter.placeholders(context)["appId"]) + .build() + ) + } + .put("/admin/apps/:appId/origin/:originId") { request, context -> + Eventual.of( + response(NO_CONTENT) + .header("appId", UrlPatternRouter.placeholders(context)["appId"]) + .header("originId", UrlPatternRouter.placeholders(context)["originId"]) + .build() + ) + } + .delete("/admin/apps/:appId") { request, context -> + Eventual.of( + response(ACCEPTED) + .header("appId", UrlPatternRouter.placeholders(context)["appId"]) + .build() + ) + } + .delete("/admin/apps/:appId/origin/:originId") { request, context -> + Eventual.of( + response(ACCEPTED) + .header("appId", UrlPatternRouter.placeholders(context)["appId"]) + .header("originId", UrlPatternRouter.placeholders(context)["originId"]) + .build() + ) } - .post("/admin/apps/:appId") { request, context -> Eventual.of( - response(CREATED) - .header("appId", UrlPatternRouter.placeholders(context)["appId"]) - .build() - )} - .post("/admin/apps/:appId/origin/:originId") { request, context -> Eventual.of( - response(CREATED) - .header("appId", UrlPatternRouter.placeholders(context)["appId"]) - .header("originId", UrlPatternRouter.placeholders(context)["originId"]) - .build() - )} - .put("/admin/apps/:appId") { request, context -> Eventual.of( - response(NO_CONTENT) - .header("appId", UrlPatternRouter.placeholders(context)["appId"]) - .build() - )} - .put("/admin/apps/:appId/origin/:originId") { request, context -> Eventual.of( - response(NO_CONTENT) - .header("appId", UrlPatternRouter.placeholders(context)["appId"]) - .header("originId", UrlPatternRouter.placeholders(context)["originId"]) - .build() - )} - .delete("/admin/apps/:appId") { request, context -> Eventual.of( - response(ACCEPTED) - .header("appId", UrlPatternRouter.placeholders(context)["appId"]) - .build() - )} - .delete("/admin/apps/:appId/origin/:originId") { request, context -> Eventual.of( - response(ACCEPTED) - .header("appId", UrlPatternRouter.placeholders(context)["appId"]) - .header("originId", UrlPatternRouter.placeholders(context)["originId"]) - .build() - )} .build() feature("Request routing") { scenario("GET requests") { - val response1 = Mono.from( - router.handle(LiveHttpRequest.get("/admin/apps/234").build(), HttpInterceptorContext.create()) - .flatMap { it.aggregate(10000) }) + val response1 = router.handle(HttpRequest.get("/admin/apps/234").build(), HttpInterceptorContext.create()) + .toMono() .block() response1!!.status() shouldBe OK response1.header("appId") shouldBe Optional.of("234") response1.header("originId") shouldBe Optional.empty() - val response2 = Mono.from( - router.handle(LiveHttpRequest.get("/admin/apps/234/origin/123").build(), HttpInterceptorContext.create()) - .flatMap { it.aggregate(10000) }) + val response2 = router.handle(HttpRequest.get("/admin/apps/234/origin/123").build(), HttpInterceptorContext.create()) + .toMono() .block() response2!!.status() shouldBe OK @@ -108,18 +120,16 @@ class UrlPatternRouterTest : FeatureSpec({ } scenario("POST requests") { - val response1 = Mono.from( - router.handle(LiveHttpRequest.post("/admin/apps/234").build(), HttpInterceptorContext.create()) - .flatMap { it.aggregate(10000) }) + val response1 = router.handle(HttpRequest.post("/admin/apps/234").build(), HttpInterceptorContext.create()) + .toMono() .block() response1!!.status() shouldBe CREATED response1.header("appId") shouldBe Optional.of("234") response1.header("originId") shouldBe Optional.empty() - val response2 = Mono.from( - router.handle(LiveHttpRequest.post("/admin/apps/234/origin/123").build(), HttpInterceptorContext.create()) - .flatMap { it.aggregate(10000) }) + val response2 = router.handle(HttpRequest.post("/admin/apps/234/origin/123").build(), HttpInterceptorContext.create()) + .toMono() .block() response2!!.status() shouldBe CREATED @@ -128,18 +138,16 @@ class UrlPatternRouterTest : FeatureSpec({ } scenario("PUT requests") { - val response1 = Mono.from( - router.handle(LiveHttpRequest.put("/admin/apps/234").build(), HttpInterceptorContext.create()) - .flatMap { it.aggregate(10000) }) + val response1 = router.handle(HttpRequest.put("/admin/apps/234").build(), HttpInterceptorContext.create()) + .toMono() .block() response1!!.status() shouldBe NO_CONTENT response1.header("appId") shouldBe Optional.of("234") response1.header("originId") shouldBe Optional.empty() - val response2 = Mono.from( - router.handle(LiveHttpRequest.put("/admin/apps/234/origin/123").build(), HttpInterceptorContext.create()) - .flatMap { it.aggregate(10000) }) + val response2 = router.handle(HttpRequest.put("/admin/apps/234/origin/123").build(), HttpInterceptorContext.create()) + .toMono() .block() response2!!.status() shouldBe NO_CONTENT @@ -148,18 +156,16 @@ class UrlPatternRouterTest : FeatureSpec({ } scenario("DELETE requests") { - val response1 = Mono.from( - router.handle(LiveHttpRequest.delete("/admin/apps/234").build(), HttpInterceptorContext.create()) - .flatMap { it.aggregate(10000) }) + val response1 = router.handle(HttpRequest.delete("/admin/apps/234").build(), HttpInterceptorContext.create()) + .toMono() .block() response1!!.status() shouldBe ACCEPTED response1.header("appId") shouldBe Optional.of("234") response1.header("originId") shouldBe Optional.empty() - val response2 = Mono.from( - router.handle(LiveHttpRequest.delete("/admin/apps/234/origin/123").build(), HttpInterceptorContext.create()) - .flatMap { it.aggregate(10000) }) + val response2 = router.handle(HttpRequest.delete("/admin/apps/234/origin/123").build(), HttpInterceptorContext.create()) + .toMono() .block() response2!!.status() shouldBe ACCEPTED @@ -178,9 +184,8 @@ class UrlPatternRouterTest : FeatureSpec({ .post("/admin/apps/:appId/:originId") { request, context -> throw RuntimeException("Something went wrong") } .build() - val response = Mono.from( - router.handle(post("/admin/apps/appx/appx-01").build(), HttpInterceptorContext.create()) - .flatMap { it.aggregate(10000) }) + val response = router.handle(post("/admin/apps/appx/appx-01").build(), HttpInterceptorContext.create()) + .toMono() .block() response!!.status() shouldBe INTERNAL_SERVER_ERROR @@ -197,16 +202,14 @@ class UrlPatternRouterTest : FeatureSpec({ val router = UrlPatternRouter.Builder() .post("/admin/apps/:appId/:originId") { request, context -> contextCapture.set(context) - Eventual.of(response(OK).build()) + Eventual.of(response(OK).build()) } .build() - val response = Mono.from( - router.handle(post("/admin/apps/appx/appx-01").build(), HttpInterceptorContext.create()) - .flatMap { it.aggregate(10000) }) + val response = router.handle(post("/admin/apps/appx/appx-01").build(), HttpInterceptorContext.create()) + .toMono() .block() - response!!.status() shouldBe OK val placeholders = UrlPatternRouter.placeholders(contextCapture.get()) diff --git a/components/server/src/main/java/com/hotels/styx/server/HttpServers.java b/components/server/src/main/java/com/hotels/styx/server/HttpServers.java index 77caa131d1..6f373802bf 100644 --- a/components/server/src/main/java/com/hotels/styx/server/HttpServers.java +++ b/components/server/src/main/java/com/hotels/styx/server/HttpServers.java @@ -33,7 +33,7 @@ public static HttpServer createHttpServer(int port, HttpHandler handler) { return NettyServerBuilder.newBuilder() .name("NettyServer") .setHttpConnector(new WebServerConnectorFactory().create(new HttpConnectorConfig(port))) - .handlerFactory(() -> new StandardHttpRouter().add("/", handler)) + .handlerFactory(() -> handler) .build(); } diff --git a/components/server/src/main/java/com/hotels/styx/server/StandardHttpRouter.java b/components/server/src/main/java/com/hotels/styx/server/StandardHttpRouter.java index 68e6127d0c..cd26036b0f 100644 --- a/components/server/src/main/java/com/hotels/styx/server/StandardHttpRouter.java +++ b/components/server/src/main/java/com/hotels/styx/server/StandardHttpRouter.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,29 +16,30 @@ package com.hotels.styx.server; import com.hotels.styx.api.Eventual; -import com.hotels.styx.api.HttpHandler; import com.hotels.styx.api.HttpInterceptor; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; +import com.hotels.styx.api.HttpRequest; +import com.hotels.styx.api.HttpResponse; +import com.hotels.styx.api.WebServiceHandler; +import static com.hotels.styx.api.HttpResponse.response; import static com.hotels.styx.api.HttpResponseStatus.NOT_FOUND; /** * Simple Http Router. */ -public class StandardHttpRouter implements HttpHandler { - private static final HttpHandler NOT_FOUND_HANDLER = (request, context) -> Eventual.of(LiveHttpResponse.response(NOT_FOUND).build()); +public class StandardHttpRouter implements WebServiceHandler { + private static final WebServiceHandler NOT_FOUND_HANDLER = (request, context) -> Eventual.of(response(NOT_FOUND).build()); - private final PathTrie routes = new PathTrie<>(); + private final PathTrie routes = new PathTrie<>(); @Override - public Eventual handle(LiveHttpRequest request, HttpInterceptor.Context context) { + public Eventual handle(HttpRequest request, HttpInterceptor.Context context) { return routes.get(request.path()) .orElse(NOT_FOUND_HANDLER) .handle(request, context); } - public StandardHttpRouter add(String path, HttpHandler httpHandler) { + public StandardHttpRouter add(String path, WebServiceHandler httpHandler) { routes.put(path, httpHandler); return this; } diff --git a/components/server/src/main/java/com/hotels/styx/server/handlers/ClassPathResourceHandler.java b/components/server/src/main/java/com/hotels/styx/server/handlers/ClassPathResourceHandler.java index 6ba9e5b5df..ca2b819c30 100644 --- a/components/server/src/main/java/com/hotels/styx/server/handlers/ClassPathResourceHandler.java +++ b/components/server/src/main/java/com/hotels/styx/server/handlers/ClassPathResourceHandler.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,14 +16,11 @@ package com.hotels.styx.server.handlers; import com.google.common.io.ByteStreams; -import com.hotels.styx.api.Buffer; -import com.hotels.styx.api.ByteStream; +import com.hotels.styx.api.HttpInterceptor; +import com.hotels.styx.api.HttpRequest; import com.hotels.styx.api.HttpResponse; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; import com.hotels.styx.api.HttpResponseStatus; import com.hotels.styx.common.http.handler.BaseHttpHandler; -import reactor.core.publisher.Flux; import java.io.FileNotFoundException; import java.io.IOException; @@ -51,8 +48,20 @@ private static String ensureRootEndsInSlash(String root) { return root.endsWith("/") ? root : root + "/"; } + private static HttpResponse error(HttpResponseStatus status) { + return new HttpResponse.Builder(status) + .body(status.description(), UTF_8) + .build(); + } + + private static byte[] resourceBody(String path) throws IOException { + try (InputStream stream = classPathResourceAsStream(path)) { + return readStream(stream); + } + } + @Override - protected LiveHttpResponse doHandle(LiveHttpRequest request) { + protected HttpResponse doHandle(HttpRequest request, HttpInterceptor.Context context) { try { String path = request.path(); @@ -63,8 +72,7 @@ protected LiveHttpResponse doHandle(LiveHttpRequest request) { return new HttpResponse.Builder(OK) .body(resourceBody(path), true) .header(CONTENT_TYPE, mediaTypeOf(path)) - .build() - .stream(); + .build(); } catch (FileNotFoundException e) { return error(NOT_FOUND); } catch (IOException e) { @@ -72,18 +80,6 @@ protected LiveHttpResponse doHandle(LiveHttpRequest request) { } } - private static byte[] resourceBody(String path) throws IOException { - try (InputStream stream = classPathResourceAsStream(path)) { - return readStream(stream); - } - } - - private static LiveHttpResponse error(HttpResponseStatus status) { - return new LiveHttpResponse.Builder(status) - .body(new ByteStream(Flux.just(new Buffer(status.description(), UTF_8)))) - .build(); - } - private static InputStream classPathResourceAsStream(String resource) throws FileNotFoundException { InputStream stream = ClassPathResourceHandler.class.getResourceAsStream(resource); diff --git a/components/server/src/main/java/com/hotels/styx/server/handlers/ReturnResponseHandler.java b/components/server/src/main/java/com/hotels/styx/server/handlers/ReturnResponseHandler.java deleted file mode 100644 index 805edf5583..0000000000 --- a/components/server/src/main/java/com/hotels/styx/server/handlers/ReturnResponseHandler.java +++ /dev/null @@ -1,50 +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.server.handlers; - -import com.hotels.styx.api.HttpHandler; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; -import com.hotels.styx.common.http.handler.BaseHttpHandler; - -import java.util.function.Supplier; - -import static com.hotels.styx.api.HttpResponse.response; -import static java.nio.charset.StandardCharsets.UTF_8; - -/** - * A handler that return whatever response returned from the passed in handler. - * - */ -public final class ReturnResponseHandler extends BaseHttpHandler { - private final Supplier response; - - private ReturnResponseHandler(Supplier response) { - this.response = response; - } - - protected LiveHttpResponse doHandle(LiveHttpRequest request) { - return response.get(); - } - - public static HttpHandler returnsResponse(String response) { - return returnsResponse(() -> response().body(response, UTF_8).build().stream()); - } - - public static HttpHandler returnsResponse(Supplier responseSupplier) { - return new ReturnResponseHandler(responseSupplier); - } -} diff --git a/components/server/src/main/java/com/hotels/styx/server/handlers/StaticFileHandler.java b/components/server/src/main/java/com/hotels/styx/server/handlers/StaticFileHandler.java index ec38bfe954..aba62a0d3d 100644 --- a/components/server/src/main/java/com/hotels/styx/server/handlers/StaticFileHandler.java +++ b/components/server/src/main/java/com/hotels/styx/server/handlers/StaticFileHandler.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import com.hotels.styx.api.HttpInterceptor; import com.hotels.styx.api.LiveHttpRequest; import com.hotels.styx.api.LiveHttpResponse; +import com.hotels.styx.common.http.handler.HttpAggregator; import org.slf4j.Logger; import java.io.File; @@ -64,7 +65,7 @@ public Eventual handle(LiveHttpRequest request, HttpIntercepto .build() .stream()) .map(Eventual::of) - .orElseGet(() -> NOT_FOUND_HANDLER.handle(request, context)); + .orElseGet(() -> new HttpAggregator(NOT_FOUND_HANDLER).handle(request, context)); } catch (IOException e) { return Eventual.of(HttpResponse.response(INTERNAL_SERVER_ERROR).build().stream()); } diff --git a/components/server/src/test/java/com/hotels/styx/server/handlers/ClassPathResourceHandlerTest.java b/components/server/src/test/java/com/hotels/styx/server/handlers/ClassPathResourceHandlerTest.java index 990bc5ce05..d6849f8047 100644 --- a/components/server/src/test/java/com/hotels/styx/server/handlers/ClassPathResourceHandlerTest.java +++ b/components/server/src/test/java/com/hotels/styx/server/handlers/ClassPathResourceHandlerTest.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,16 +16,16 @@ package com.hotels.styx.server.handlers; import com.hotels.styx.api.HttpResponse; -import com.hotels.styx.api.LiveHttpRequest; +import com.hotels.styx.api.HttpRequest; import com.hotels.styx.server.HttpInterceptorContext; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import reactor.core.publisher.Mono; -import static com.hotels.styx.api.LiveHttpRequest.get; +import static com.hotels.styx.api.HttpRequest.get; import static com.hotels.styx.api.HttpResponseStatus.FORBIDDEN; import static com.hotels.styx.api.HttpResponseStatus.NOT_FOUND; import static com.hotels.styx.api.HttpResponseStatus.OK; -import static com.hotels.styx.support.api.BlockingObservables.waitForResponse; import static java.lang.System.lineSeparator; import static java.nio.charset.StandardCharsets.UTF_8; import static org.hamcrest.CoreMatchers.is; @@ -36,8 +36,8 @@ public class ClassPathResourceHandlerTest { @Test public void readsClassPathResources() { - LiveHttpRequest request = get("/admin/dashboard/expected.txt").build(); - HttpResponse response = waitForResponse(handler.handle(request, HttpInterceptorContext.create())); + HttpRequest request = get("/admin/dashboard/expected.txt").build(); + HttpResponse response = Mono.from(handler.handle(request, HttpInterceptorContext.create())).block(); assertThat(response.status(), is(OK)); assertThat(body(response), is("Foo\nBar\n")); @@ -49,8 +49,8 @@ private static String body(HttpResponse response) { @Test public void returns404IfResourceDoesNotExist() { - LiveHttpRequest request = get("/admin/dashboard/unexpected.txt").build(); - HttpResponse response = waitForResponse(handler.handle(request, HttpInterceptorContext.create())); + HttpRequest request = get("/admin/dashboard/unexpected.txt").build(); + HttpResponse response = Mono.from(handler.handle(request, HttpInterceptorContext.create())).block(); assertThat(response.status(), is(NOT_FOUND)); } @@ -67,8 +67,8 @@ private static Object[][] illegalPrefixes() { @Test(dataProvider = "forbiddenPaths") public void returns403IfTryingToAccessResourcesOutsidePermittedRoot(String path) { - LiveHttpRequest request = get(path).build(); - HttpResponse response = waitForResponse(handler.handle(request, HttpInterceptorContext.create())); + HttpRequest request = get(path).build(); + HttpResponse response = Mono.from(handler.handle(request, HttpInterceptorContext.create())).block(); assertThat(response.status(), is(FORBIDDEN)); } diff --git a/support/api-testsupport/src/main/java/com/hotels/styx/support/api/BlockingObservables.java b/support/api-testsupport/src/main/java/com/hotels/styx/support/api/BlockingObservables.java deleted file mode 100644 index fd8864a519..0000000000 --- a/support/api-testsupport/src/main/java/com/hotels/styx/support/api/BlockingObservables.java +++ /dev/null @@ -1,48 +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.support.api; - -import com.hotels.styx.api.Eventual; -import com.hotels.styx.api.HttpResponse; -import com.hotels.styx.api.LiveHttpResponse; -import reactor.core.publisher.Mono; -import rx.Observable; - -import static rx.RxReactiveStreams.toObservable; - - -// TODO: This class needs to be removed once we have migrated over to Reactor/Flux. - -public final class BlockingObservables { - - public static T getFirst(Observable observable) { - return observable.toBlocking().single(); - } - - public static HttpResponse waitForResponse(Eventual responseObs) { - return Mono.from(responseObs.flatMap(response -> response.aggregate(120*1024))).block(); - } - - public static HttpResponse waitForResponse(Observable responseObs) { - return responseObs - .flatMap(response -> toObservable(response.aggregate(120*1024))) - .toBlocking() - .single(); - } - - private BlockingObservables() { - } -} diff --git a/support/origins-starter-app/src/main/java/com/hotels/styx/support/origins/AppHandler.java b/support/origins-starter-app/src/main/java/com/hotels/styx/support/origins/AppHandler.java index 06a20f0c2c..81bcec9d11 100644 --- a/support/origins-starter-app/src/main/java/com/hotels/styx/support/origins/AppHandler.java +++ b/support/origins-starter-app/src/main/java/com/hotels/styx/support/origins/AppHandler.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,12 +16,11 @@ package com.hotels.styx.support.origins; import com.hotels.styx.api.Eventual; -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.HttpResponseStatus; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; +import com.hotels.styx.api.WebServiceHandler; import com.hotels.styx.api.extension.Origin; import java.util.Optional; @@ -36,7 +35,7 @@ import static java.util.Arrays.fill; import static java.util.UUID.randomUUID; -public class AppHandler implements HttpHandler { +public class AppHandler implements WebServiceHandler { private final Origin origin; private final HttpResponse standardResponse; @@ -49,7 +48,7 @@ public AppHandler(Origin origin) { } @Override - public Eventual handle(LiveHttpRequest request, HttpInterceptor.Context context) { + public Eventual handle(HttpRequest request, HttpInterceptor.Context context) { HttpResponse.Builder responseBuilder = standardResponse.newBuilder() .headers(request.headers()) .header(STUB_ORIGIN_INFO, origin.applicationInfo()); @@ -63,8 +62,7 @@ public Eventual handle(LiveHttpRequest request, HttpIntercepto .map(length -> it.body(generateContent(parseInt(length)), UTF_8)) .orElse(it)) .orElse(responseBuilder) - .build() - .stream()); + .build()); } private static String makeAResponse(Origin origin) { diff --git a/support/origins-starter-app/src/main/java/com/hotels/styx/support/origins/StyxOriginsStarterApp.java b/support/origins-starter-app/src/main/java/com/hotels/styx/support/origins/StyxOriginsStarterApp.java index 73b388c829..45d60dc04e 100644 --- a/support/origins-starter-app/src/main/java/com/hotels/styx/support/origins/StyxOriginsStarterApp.java +++ b/support/origins-starter-app/src/main/java/com/hotels/styx/support/origins/StyxOriginsStarterApp.java @@ -19,15 +19,15 @@ import com.hotels.styx.StartupConfig; import com.hotels.styx.StyxConfig; import com.hotels.styx.api.Resource; -import com.hotels.styx.api.extension.Origin; import com.hotels.styx.api.configuration.Configuration; +import com.hotels.styx.api.extension.Origin; import com.hotels.styx.applications.BackendServices; +import com.hotels.styx.common.http.handler.HttpAggregator; import com.hotels.styx.infrastructure.configuration.ConfigurationParser; import com.hotels.styx.infrastructure.configuration.yaml.YamlConfiguration; import com.hotels.styx.server.HttpConnectorConfig; import com.hotels.styx.server.HttpServer; import com.hotels.styx.server.ServerEventLoopFactory; -import com.hotels.styx.server.StandardHttpRouter; import com.hotels.styx.server.netty.NettyServerBuilder; import com.hotels.styx.server.netty.WebServerConnectorFactory; import com.hotels.styx.server.netty.eventloop.PlatformAwareServerEventLoopFactory; @@ -70,7 +70,7 @@ private static HttpServer createHttpServer(Origin origin) { .name(origin.hostAndPortString()) .setServerEventLoopFactory(serverEventLoopFactory) .setHttpConnector(new WebServerConnectorFactory().create(new HttpConnectorConfig(origin.port()))) - .handlerFactory(() -> new StandardHttpRouter().add("/*", new AppHandler(origin))) + .handlerFactory(() -> new HttpAggregator(new AppHandler(origin))) .build(); } diff --git a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/MockServer.scala b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/MockServer.scala index ab38071787..0b93e730c8 100644 --- a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/MockServer.scala +++ b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/MockServer.scala @@ -22,9 +22,7 @@ import java.util.function.Supplier import com.google.common.util.concurrent.AbstractIdleService import com.hotels.styx.api._ import com.hotels.styx.api.extension.Origin.newOriginBuilder -import com.hotels.styx.common.FreePorts._ -import com.hotels.styx.common.http.handler.NotFoundHandler -import com.hotels.styx.server.handlers.ReturnResponseHandler.returnsResponse +import com.hotels.styx.common.http.handler.{HttpAggregator, NotFoundHandler} import com.hotels.styx.server.netty.{NettyServerBuilder, ServerConnector, WebServerConnectorFactory} import com.hotels.styx.server.{HttpConnectorConfig, HttpServer} @@ -48,7 +46,7 @@ class MockServer(id: String, val port: Int) extends AbstractIdleService with Htt val routes = new ConcurrentHashMap[String, HttpHandler]() override def handle(request: LiveHttpRequest, context: HttpInterceptor.Context): Eventual[LiveHttpResponse] = { - val handler: HttpHandler = routes.getOrDefault(request.path(), new NotFoundHandler) + val handler: HttpHandler = routes.getOrDefault(request.path(), new HttpAggregator(new NotFoundHandler)) handler.handle(request, context) } @@ -71,7 +69,7 @@ class MockServer(id: String, val port: Int) extends AbstractIdleService with Htt } def stub(path: String, responseSupplier: Supplier[LiveHttpResponse]): MockServer = { - router.addRoute(path, requestRecordingHandler(requestQueue, returnsResponse(responseSupplier))) + router.addRoute(path, requestRecordingHandler(requestQueue, (request, context) => Eventual.of(responseSupplier.get()))) this } diff --git a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/plugins/PluginAdminInterfaceSpec.scala b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/plugins/PluginAdminInterfaceSpec.scala index f735a82f66..dd2fd0604f 100644 --- a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/plugins/PluginAdminInterfaceSpec.scala +++ b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/plugins/PluginAdminInterfaceSpec.scala @@ -22,7 +22,7 @@ import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort._ import com.google.common.net.MediaType.PLAIN_TEXT_UTF_8 import com.hotels.styx.api._ -import com.hotels.styx.common.http.handler.StaticBodyHttpHandler +import com.hotels.styx.common.http.handler.{HttpAggregator, StaticBodyHttpHandler} import com.hotels.styx.support.backends.FakeHttpServer import com.hotels.styx.support.configuration.{HttpBackend, Origins, StyxConfig} import com.hotels.styx.{PluginAdapter, StyxClientSupplier, StyxProxySpec} @@ -115,8 +115,8 @@ class PluginAdminInterfaceSpec extends FunSpec with StyxProxySpec with StyxClien override def intercept(request: LiveHttpRequest, chain: HttpInterceptor.Chain): Eventual[LiveHttpResponse] = chain.proceed(request) override def adminInterfaceHandlers(): util.Map[String, HttpHandler] = Map[String, HttpHandler]( - "/path/one" -> new StaticBodyHttpHandler(PLAIN_TEXT_UTF_8, "X: Response from first admin interface"), - "/path/two" -> new StaticBodyHttpHandler(PLAIN_TEXT_UTF_8, "X: Response from second admin interface") + "/path/one" -> new HttpAggregator(new StaticBodyHttpHandler(PLAIN_TEXT_UTF_8, "X: Response from first admin interface")), + "/path/two" -> new HttpAggregator(new StaticBodyHttpHandler(PLAIN_TEXT_UTF_8, "X: Response from second admin interface")) ).asJava } @@ -124,8 +124,8 @@ class PluginAdminInterfaceSpec extends FunSpec with StyxProxySpec with StyxClien override def intercept(request: LiveHttpRequest, chain: HttpInterceptor.Chain): Eventual[LiveHttpResponse] = chain.proceed(request) override def adminInterfaceHandlers(): util.Map[String, HttpHandler] = Map[String, HttpHandler]( - "/path/one" -> new StaticBodyHttpHandler(PLAIN_TEXT_UTF_8, "Y: Response from first admin interface"), - "/path/two" -> new StaticBodyHttpHandler(PLAIN_TEXT_UTF_8, "Y: Response from second admin interface") + "/path/one" -> new HttpAggregator(new StaticBodyHttpHandler(PLAIN_TEXT_UTF_8, "Y: Response from first admin interface")), + "/path/two" -> new HttpAggregator(new StaticBodyHttpHandler(PLAIN_TEXT_UTF_8, "Y: Response from second admin interface")) ).asJava } @@ -133,8 +133,8 @@ class PluginAdminInterfaceSpec extends FunSpec with StyxProxySpec with StyxClien override def intercept(request: LiveHttpRequest, chain: HttpInterceptor.Chain): Eventual[LiveHttpResponse] = chain.proceed(request) override def adminInterfaceHandlers(): util.Map[String, HttpHandler] = Map[String, HttpHandler]( - "path/one" -> new StaticBodyHttpHandler(PLAIN_TEXT_UTF_8, "Z: Response from first admin interface"), - "path/two" -> new StaticBodyHttpHandler(PLAIN_TEXT_UTF_8, "Z: Response from second admin interface") + "path/one" -> new HttpAggregator(new StaticBodyHttpHandler(PLAIN_TEXT_UTF_8, "Z: Response from first admin interface")), + "path/two" -> new HttpAggregator(new StaticBodyHttpHandler(PLAIN_TEXT_UTF_8, "Z: Response from second admin interface")) ).asJava } diff --git a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/proxy/ChunkedUploadSpec.scala b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/proxy/ChunkedUploadSpec.scala index e69c16dbfa..70435a4fcf 100644 --- a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/proxy/ChunkedUploadSpec.scala +++ b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/proxy/ChunkedUploadSpec.scala @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -24,11 +24,11 @@ import com.google.common.base.Charsets._ import com.google.common.net.HostAndPort import com.hotels.styx.api.extension.Origin.newOriginBuilder import com.hotels.styx.common.FreePorts._ +import com.hotels.styx.common.http.handler.HttpAggregator import com.hotels.styx.server.HttpServers.createHttpServer import com.hotels.styx.support.TestClientSupport import com.hotels.styx.support.configuration.{HttpBackend, Origins, ProxyConfig, StyxConfig} import com.hotels.styx.support.backends.FakeHttpServer -import com.hotels.styx.utils.HttpTestClient import com.hotels.styx.utils.handlers.ContentDigestHandler import com.hotels.styx.{StyxClientSupplier, StyxProxySpec} import io.netty.buffer.Unpooled @@ -325,7 +325,7 @@ class ChunkedUploadSpec extends FunSpec def originAndWebServer(appId: String, originId: String) = { val serverPort = freePort() val origin = newOriginBuilder("localhost", serverPort).applicationId("app").id("app1").build() - val server = createHttpServer(serverPort, new ContentDigestHandler(origin)) + val server = createHttpServer(serverPort, new HttpAggregator(new ContentDigestHandler(origin))) server.startAsync().awaitRunning() origin -> server diff --git a/system-tests/e2e-testsupport/src/main/java/com/hotels/styx/utils/handlers/ContentDigestHandler.java b/system-tests/e2e-testsupport/src/main/java/com/hotels/styx/utils/handlers/ContentDigestHandler.java index 6f0f357c15..eae64552e7 100644 --- a/system-tests/e2e-testsupport/src/main/java/com/hotels/styx/utils/handlers/ContentDigestHandler.java +++ b/system-tests/e2e-testsupport/src/main/java/com/hotels/styx/utils/handlers/ContentDigestHandler.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Expedia Inc. + Copyright (C) 2013-2019 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,13 +15,11 @@ */ package com.hotels.styx.utils.handlers; +import com.hotels.styx.api.HttpInterceptor; import com.hotels.styx.api.HttpRequest; import com.hotels.styx.api.HttpResponse; -import com.hotels.styx.api.LiveHttpRequest; -import com.hotels.styx.api.LiveHttpResponse; import com.hotels.styx.api.extension.Origin; import com.hotels.styx.common.http.handler.BaseHttpHandler; -import reactor.core.publisher.Mono; import static com.google.common.base.Charsets.UTF_8; import static com.google.common.net.HttpHeaders.CONTENT_LENGTH; @@ -39,19 +37,17 @@ public ContentDigestHandler(Origin origin) { } @Override - protected LiveHttpResponse doHandle(LiveHttpRequest request) { - HttpRequest fullRequest = Mono.from(request.aggregate(0x100000)).block(); + protected HttpResponse doHandle(HttpRequest request, HttpInterceptor.Context context) { String responseBody = format("Response From %s - %s, received content digest: %s", origin.hostAndPortString(), randomUUID(), - fullRequest.bodyAs(UTF_8).hashCode()); + request.bodyAs(UTF_8).hashCode()); return HttpResponse.response(OK) .header(CONTENT_TYPE, HTML_UTF_8.toString()) .header(CONTENT_LENGTH, responseBody.getBytes(UTF_8).length) .body(responseBody, UTF_8) - .build() - .stream(); + .build(); } } From 30063d73d418879d01e0a70d841e863e8b9ff92b Mon Sep 17 00:00:00 2001 From: Mikko Karjalainen Date: Fri, 24 May 2019 11:53:04 +0100 Subject: [PATCH 2/5] Rearrange methods that were accidentally moved around. --- .../hotels/styx/admin/AdminServerBuilder.java | 114 +++++++-------- .../admin/handlers/PluginToggleHandler.java | 134 +++++++++--------- .../handlers/StyxConfigurationHandler.java | 14 +- .../StyxConfigurationHandlerTest.java | 38 ++--- .../handlers/ClassPathResourceHandler.java | 24 ++-- 5 files changed, 162 insertions(+), 162 deletions(-) diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/AdminServerBuilder.java b/components/proxy/src/main/java/com/hotels/styx/admin/AdminServerBuilder.java index 50e730d9a4..d5358f2680 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/AdminServerBuilder.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/AdminServerBuilder.java @@ -104,63 +104,6 @@ public AdminServerBuilder backendServicesRegistry(Registry backe return this; } - private static List routesForPlugin(NamedPlugin namedPlugin) { - List routes = pluginAdminEndpointRoutes(namedPlugin); - - List endpointLinks = routes.stream() - .map(PluginAdminEndpointRoute::link) - .collect(toList()); - - WebServiceHandler handler = endpointLinks.isEmpty() - ? new StaticBodyHttpHandler(HTML_UTF_8, format("This plugin (%s) does not expose any admin interfaces", namedPlugin.name())) - : new IndexHandler(endpointLinks); - - Route indexRoute = new Route(pluginPath(namedPlugin), handler); - - return concatenate(indexRoute, routes); - } - - private static List pluginAdminEndpointRoutes(NamedPlugin namedPlugin) { - Map adminInterfaceHandlers = namedPlugin.adminInterfaceHandlers(); - - return mapToList(adminInterfaceHandlers, (relativePath, handler) -> - new PluginAdminEndpointRoute(namedPlugin, relativePath, new HttpStreamer(MEGABYTE, handler))); - } - - private static Iterable indexLinkPaths() { - return ImmutableSortedSet.of( - link("version.txt", "/version.txt"), - link("Ping", "/admin/ping"), - link("Threads", "/admin/threads"), - link("Current Requests", "/admin/current_requests?withStackTrace=true"), - link("Metrics", "/admin/metrics?pretty"), - link("Configuration", "/admin/configuration?pretty"), - link("Log Configuration", "/admin/configuration/logging"), - link("Origins Configuration", "/admin/configuration/origins?pretty"), - link("Startup Configuration", "/admin/configuration/startup"), - link("JVM", "/admin/jvm?pretty"), - link("Origins Status", "/admin/origins/status?pretty"), - link("Dashboard", "/admin/dashboard/index.html"), - link("Plugins", "/admin/plugins")); - } - - private static List concatenate(T item, List items) { - List list = new ArrayList<>(items.size() + 1); - list.add(item); - list.addAll(items); - return list; - } - - private static String pluginPath(NamedPlugin namedPlugin) { - return "/admin/plugins/" + namedPlugin.name(); - } - - private JsonHandler dashboardDataHandler(StyxConfig styxConfig) { - return new JsonHandler<>(new DashboardDataSupplier(backendServicesRegistry, environment, styxConfig), - Optional.of(Duration.ofSeconds(10)), - new MetricsModule(SECONDS, MILLISECONDS, false)); - } - public HttpServer build() { LOG.info("event bus that will be used is {}", environment.eventBus()); StyxConfig styxConfig = environment.configuration(); @@ -219,6 +162,63 @@ private WebServiceHandler adminEndpoints(StyxConfig styxConfig) { return httpRouter; } + private static Iterable indexLinkPaths() { + return ImmutableSortedSet.of( + link("version.txt", "/version.txt"), + link("Ping", "/admin/ping"), + link("Threads", "/admin/threads"), + link("Current Requests", "/admin/current_requests?withStackTrace=true"), + link("Metrics", "/admin/metrics?pretty"), + link("Configuration", "/admin/configuration?pretty"), + link("Log Configuration", "/admin/configuration/logging"), + link("Origins Configuration", "/admin/configuration/origins?pretty"), + link("Startup Configuration", "/admin/configuration/startup"), + link("JVM", "/admin/jvm?pretty"), + link("Origins Status", "/admin/origins/status?pretty"), + link("Dashboard", "/admin/dashboard/index.html"), + link("Plugins", "/admin/plugins")); + } + + private static List routesForPlugin(NamedPlugin namedPlugin) { + List routes = pluginAdminEndpointRoutes(namedPlugin); + + List endpointLinks = routes.stream() + .map(PluginAdminEndpointRoute::link) + .collect(toList()); + + WebServiceHandler handler = endpointLinks.isEmpty() + ? new StaticBodyHttpHandler(HTML_UTF_8, format("This plugin (%s) does not expose any admin interfaces", namedPlugin.name())) + : new IndexHandler(endpointLinks); + + Route indexRoute = new Route(pluginPath(namedPlugin), handler); + + return concatenate(indexRoute, routes); + } + + private static List concatenate(T item, List items) { + List list = new ArrayList<>(items.size() + 1); + list.add(item); + list.addAll(items); + return list; + } + + private static String pluginPath(NamedPlugin namedPlugin) { + return "/admin/plugins/" + namedPlugin.name(); + } + + private static List pluginAdminEndpointRoutes(NamedPlugin namedPlugin) { + Map adminInterfaceHandlers = namedPlugin.adminInterfaceHandlers(); + + return mapToList(adminInterfaceHandlers, (relativePath, handler) -> + new PluginAdminEndpointRoute(namedPlugin, relativePath, new HttpStreamer(MEGABYTE, handler))); + } + + private JsonHandler dashboardDataHandler(StyxConfig styxConfig) { + return new JsonHandler<>(new DashboardDataSupplier(backendServicesRegistry, environment, styxConfig), + Optional.of(Duration.ofSeconds(10)), + new MetricsModule(SECONDS, MILLISECONDS, false)); + } + // allows key and value to be labelled in lambda instead of having to use Entry.getKey, Entry.getValue private static List mapToList(Map map, BiFunction function) { return map.entrySet().stream() diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/PluginToggleHandler.java b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/PluginToggleHandler.java index aa087e2427..b37db260f0 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/PluginToggleHandler.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/PluginToggleHandler.java @@ -70,54 +70,54 @@ public PluginToggleHandler(ConfigStore configStore) { this.configStore = requireNonNull(configStore); } - private static HttpResponse applyUpdate(RequestedUpdate requestedUpdate) { - boolean changed = requestedUpdate.apply(); - - String message = responseMessage(requestedUpdate, changed); - - return responseWith(OK, message); + @Override + public Eventual handle(HttpRequest request, HttpInterceptor.Context context) { + return getCurrentOrPutNewState(request, context) + .onError(cause -> handleErrors(cause, context)); } - private static Matcher urlMatcher(HttpRequest request) { - Matcher matcher = URL_PATTERN.matcher(request.path()); - - if (!matcher.matches()) { - throw new BadPluginToggleRequestException("Invalid URL"); + private Eventual getCurrentOrPutNewState(HttpRequest request, HttpInterceptor.Context context) { + if (GET.equals(request.method())) { + return getCurrentState(request, context); + } else if (PUT.equals(request.method())) { + return putNewState(request, context); + } else { + return Eventual.of(response(METHOD_NOT_ALLOWED).build()); } - return matcher; } - private static Eventual requestedNewState(HttpRequest request) { - // TODO: Mikko: Crappy coding, fix this: + private Eventual getCurrentState(HttpRequest request, HttpInterceptor.Context context) { return Eventual.of(request) - .map(fullRequest -> fullRequest.bodyAs(UTF_8)) - .map(PluginToggleHandler::parseToBoolean) - .map(PluginEnabledState::fromBoolean); + .map(this::plugin) + .map(PluginToggleHandler::currentState) + .map(state -> responseWith(OK, state.toString())); } private static PluginEnabledState currentState(NamedPlugin plugin) { return plugin.enabled() ? PluginEnabledState.ENABLED : PluginEnabledState.DISABLED; } - private static HttpResponse responseWith(HttpResponseStatus status, String message) { - return HttpResponse.response(status) - .body(message + "\n", UTF_8) - .addHeader(CONTENT_TYPE, PLAIN_TEXT_UTF_8.toString()) - .disableCaching() - .build(); + private Eventual putNewState(HttpRequest request, HttpInterceptor.Context context) { + return Eventual.of(request) + .flatMap(this::requestedUpdate) + .map(PluginToggleHandler::applyUpdate); } - private static Eventual handleErrors(Throwable e, HttpInterceptor.Context context) { - if (e instanceof PluginNotFoundException) { - return Eventual.of(responseWith(NOT_FOUND, e.getMessage())); - } + private Eventual requestedUpdate(HttpRequest request) { + return requestedNewState(request) + .map(state -> { + NamedPlugin plugin = plugin(request); - if (e instanceof BadPluginToggleRequestException) { - return Eventual.of(responseWith(BAD_REQUEST, e.getMessage())); - } + return new RequestedUpdate(plugin, state); + }); + } - LOGGER.error("Plugin toggle error", e); - return Eventual.of(responseWith(INTERNAL_SERVER_ERROR, "")); + private static HttpResponse applyUpdate(RequestedUpdate requestedUpdate) { + boolean changed = requestedUpdate.apply(); + + String message = responseMessage(requestedUpdate, changed); + + return responseWith(OK, message); } private static String responseMessage(RequestedUpdate requestedUpdate, boolean changed) { @@ -138,47 +138,48 @@ private static String wasChangedMessage(RequestedUpdate requestedUpdate) { return format("State of '%s' changed to '%s'", requestedUpdate.plugin().name(), requestedUpdate.newState()); } - @Override - public Eventual handle(HttpRequest request, HttpInterceptor.Context context) { - return getCurrentOrPutNewState(request, context) - .onError(cause -> handleErrors(cause, context)); - } + private static Matcher urlMatcher(HttpRequest request) { + Matcher matcher = URL_PATTERN.matcher(request.path()); - private Eventual getCurrentOrPutNewState(HttpRequest request, HttpInterceptor.Context context) { - if (GET.equals(request.method())) { - return getCurrentState(request, context); - } else if (PUT.equals(request.method())) { - return putNewState(request, context); - } else { - return Eventual.of(response(METHOD_NOT_ALLOWED).build()); + if (!matcher.matches()) { + throw new BadPluginToggleRequestException("Invalid URL"); } + return matcher; } - private NamedPlugin plugin(String pluginName) { - return configStore.get("plugins." + pluginName, NamedPlugin.class) - .orElseThrow(() -> new PluginNotFoundException("No such plugin: pluginName=" + pluginName)); - } - - private Eventual getCurrentState(HttpRequest request, HttpInterceptor.Context context) { + private static Eventual requestedNewState(HttpRequest request) { + // TODO: Mikko: Crappy coding, fix this: return Eventual.of(request) - .map(this::plugin) - .map(PluginToggleHandler::currentState) - .map(state -> responseWith(OK, state.toString())); + .map(fullRequest -> fullRequest.bodyAs(UTF_8)) + .map(PluginToggleHandler::parseToBoolean) + .map(PluginEnabledState::fromBoolean); } - private Eventual putNewState(HttpRequest request, HttpInterceptor.Context context) { - return Eventual.of(request) - .flatMap(this::requestedUpdate) - .map(PluginToggleHandler::applyUpdate); + private static HttpResponse responseWith(HttpResponseStatus status, String message) { + return HttpResponse.response(status) + .body(message + "\n", UTF_8) + .addHeader(CONTENT_TYPE, PLAIN_TEXT_UTF_8.toString()) + .disableCaching() + .build(); } - private Eventual requestedUpdate(HttpRequest request) { - return requestedNewState(request) - .map(state -> { - NamedPlugin plugin = plugin(request); + private static Eventual handleErrors(Throwable e, HttpInterceptor.Context context) { + if (e instanceof PluginNotFoundException) { + return Eventual.of(responseWith(NOT_FOUND, e.getMessage())); + } - return new RequestedUpdate(plugin, state); - }); + if (e instanceof BadPluginToggleRequestException) { + return Eventual.of(responseWith(BAD_REQUEST, e.getMessage())); + } + + LOGGER.error("Plugin toggle error", e); + return Eventual.of(responseWith(INTERNAL_SERVER_ERROR, "")); + } + + private NamedPlugin plugin(HttpRequest request) { + Matcher matcher = urlMatcher(request); + String pluginName = matcher.group(1); + return plugin(pluginName); } private static boolean parseToBoolean(String string) { @@ -192,10 +193,9 @@ private static boolean parseToBoolean(String string) { } } - private NamedPlugin plugin(HttpRequest request) { - Matcher matcher = urlMatcher(request); - String pluginName = matcher.group(1); - return plugin(pluginName); + private NamedPlugin plugin(String pluginName) { + return configStore.get("plugins." + pluginName, NamedPlugin.class) + .orElseThrow(() -> new PluginNotFoundException("No such plugin: pluginName=" + pluginName)); } private enum PluginEnabledState { diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/StyxConfigurationHandler.java b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/StyxConfigurationHandler.java index 6aa7d5ca38..28d56bfb25 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/StyxConfigurationHandler.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/StyxConfigurationHandler.java @@ -49,6 +49,13 @@ public StyxConfigurationHandler(Configuration configuration) { prettyStyxConfigHandler = new StaticBodyHttpHandler(PLAIN_TEXT_UTF_8, prettify(configuration)); } + @Override + public Eventual handle(HttpRequest request, HttpInterceptor.Context context) { + return configHandler(request.queryParam("pretty").isPresent()) + .handle(request, context) + .map(StyxConfigurationHandler::disableCaching); + } + private static HttpResponse disableCaching(HttpResponse response) { return response.newBuilder() .disableCaching() @@ -59,13 +66,6 @@ private StaticBodyHttpHandler configHandler(boolean pretty) { return pretty ? prettyStyxConfigHandler : styxConfigHandler; } - @Override - public Eventual handle(HttpRequest request, HttpInterceptor.Context context) { - return configHandler(request.queryParam("pretty").isPresent()) - .handle(request, context) - .map(StyxConfigurationHandler::disableCaching); - } - private static String body(Configuration styxConfig) { return styxConfig + "\n"; } diff --git a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/StyxConfigurationHandlerTest.java b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/StyxConfigurationHandlerTest.java index 40b9a62bac..43be420ac2 100644 --- a/components/proxy/src/test/java/com/hotels/styx/admin/handlers/StyxConfigurationHandlerTest.java +++ b/components/proxy/src/test/java/com/hotels/styx/admin/handlers/StyxConfigurationHandlerTest.java @@ -34,25 +34,6 @@ public class StyxConfigurationHandlerTest { private static final String ORIGINS_FILE = fixturesHome() + "conf/origins/origins-development.yml"; - private static Eventual browseForCurrentConfiguration(String yaml, boolean pretty) { - return configurationBrowserHandler(yaml).handle(adminRequest(pretty), HttpInterceptorContext.create()); - } - - private static HttpRequest adminRequest(boolean pretty) { - if (pretty) { - return get("/?pretty=").build(); - } else { - return get("/").build(); - } - } - - private static String formatPathLikeYamlConfig(String path) { - if (File.separator.equals("\\")) { - return path.replace("\\", "\\\\"); - } - return path; - } - @Test public void outputsConfiguration() { String yaml = "" + @@ -99,6 +80,25 @@ public void outputsPrettifiedConfiguration() throws Exception { "}")); } + private static String formatPathLikeYamlConfig(String path) { + if (File.separator.equals("\\")) { + return path.replace("\\", "\\\\"); + } + return path; + } + + private static Eventual browseForCurrentConfiguration(String yaml, boolean pretty) { + return configurationBrowserHandler(yaml).handle(adminRequest(pretty), HttpInterceptorContext.create()); + } + + private static HttpRequest adminRequest(boolean pretty) { + if (pretty) { + return get("/?pretty=").build(); + } else { + return get("/").build(); + } + } + private static StyxConfigurationHandler configurationBrowserHandler(String yaml) { return new StyxConfigurationHandler(new StyxConfig(yaml)); } diff --git a/components/server/src/main/java/com/hotels/styx/server/handlers/ClassPathResourceHandler.java b/components/server/src/main/java/com/hotels/styx/server/handlers/ClassPathResourceHandler.java index ca2b819c30..67c7cf536e 100644 --- a/components/server/src/main/java/com/hotels/styx/server/handlers/ClassPathResourceHandler.java +++ b/components/server/src/main/java/com/hotels/styx/server/handlers/ClassPathResourceHandler.java @@ -48,18 +48,6 @@ private static String ensureRootEndsInSlash(String root) { return root.endsWith("/") ? root : root + "/"; } - private static HttpResponse error(HttpResponseStatus status) { - return new HttpResponse.Builder(status) - .body(status.description(), UTF_8) - .build(); - } - - private static byte[] resourceBody(String path) throws IOException { - try (InputStream stream = classPathResourceAsStream(path)) { - return readStream(stream); - } - } - @Override protected HttpResponse doHandle(HttpRequest request, HttpInterceptor.Context context) { try { @@ -80,6 +68,18 @@ protected HttpResponse doHandle(HttpRequest request, HttpInterceptor.Context con } } + private static byte[] resourceBody(String path) throws IOException { + try (InputStream stream = classPathResourceAsStream(path)) { + return readStream(stream); + } + } + + private static HttpResponse error(HttpResponseStatus status) { + return new HttpResponse.Builder(status) + .body(status.description(), UTF_8) + .build(); + } + private static InputStream classPathResourceAsStream(String resource) throws FileNotFoundException { InputStream stream = ClassPathResourceHandler.class.getResourceAsStream(resource); From 69aa44d0f19b4898c70653e18bd9a4d4ca4825a1 Mon Sep 17 00:00:00 2001 From: Mikko Karjalainen Date: Fri, 24 May 2019 12:02:08 +0100 Subject: [PATCH 3/5] Add Kotlin `WebServiceHandler.handle` extension method for unit tests. --- .../hotels/styx/admin/handlers/MetricsHandler.java | 12 ++++++------ .../test/kotlin/com/hotels/styx/routing/Support.kt | 3 +++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/MetricsHandler.java b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/MetricsHandler.java index 795597693b..9456d9af0e 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/MetricsHandler.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/MetricsHandler.java @@ -80,6 +80,11 @@ public MetricsHandler(MetricRegistry metricRegistry, Optional cacheExp this.metricRegistry = metricRegistry; } + @Override + public Eventual handle(HttpRequest request, HttpInterceptor.Context context) { + return this.urlMatcher.handle(request, context); + } + private static boolean matchesRoot(String metricName, String root) { return root == null || metricName.equals(root) || metricName.startsWith(root + "."); } @@ -88,11 +93,6 @@ private static boolean containsSearchTerm(String name, String searchTerm) { return searchTerm == null || name.contains(searchTerm); } - @Override - public Eventual handle(HttpRequest request, HttpInterceptor.Context context) { - return this.urlMatcher.handle(request, context); - } - private HttpResponse filteredMetricResponse(HttpRequest request) { String root = Optional.of(SPECIFIC_METRICS_PATH_PATTERN.matcher(request.path())) .filter(Matcher::matches) @@ -129,7 +129,7 @@ private String serialise(Object object, boolean pretty) { } } - static class RootMetricsHandler extends JsonHandler { + private static class RootMetricsHandler extends JsonHandler { public RootMetricsHandler(MetricRegistry data, Optional cacheExpiration, Module... modules) { super(data, cacheExpiration, modules); } diff --git a/components/proxy/src/test/kotlin/com/hotels/styx/routing/Support.kt b/components/proxy/src/test/kotlin/com/hotels/styx/routing/Support.kt index 47e8e8a554..3e44845cc6 100644 --- a/components/proxy/src/test/kotlin/com/hotels/styx/routing/Support.kt +++ b/components/proxy/src/test/kotlin/com/hotels/styx/routing/Support.kt @@ -21,6 +21,7 @@ import com.hotels.styx.api.HttpHandler import com.hotels.styx.api.HttpRequest import com.hotels.styx.api.HttpResponse import com.hotels.styx.api.HttpResponseStatus.OK +import com.hotels.styx.api.WebServiceHandler import com.hotels.styx.infrastructure.configuration.yaml.YamlConfig import com.hotels.styx.proxy.plugin.NamedPlugin import com.hotels.styx.routing.config.BuiltinInterceptorsFactory @@ -50,6 +51,8 @@ data class RoutingContext( } +fun WebServiceHandler.handle(request: HttpRequest) = this.handle(request, HttpInterceptorContext.create()) + fun HttpHandler.handle(request: HttpRequest, count: Int = 10000) = this.handle(request.stream(), HttpInterceptorContext.create()) .flatMap { it.aggregate(count) } From d729612ac91b29e4ae04cd4c45c5dd2263f9d4e5 Mon Sep 17 00:00:00 2001 From: Mikko Karjalainen Date: Fri, 24 May 2019 17:30:16 +0100 Subject: [PATCH 4/5] Rearrange method order. --- .../handlers/OriginsInventoryHandler.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/OriginsInventoryHandler.java b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/OriginsInventoryHandler.java index c6a368f0e3..ebd345f736 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/OriginsInventoryHandler.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/OriginsInventoryHandler.java @@ -64,8 +64,13 @@ public OriginsInventoryHandler(EventBus eventBus) { eventBus.post(new GetOriginsInventorySnapshot()); } - private static boolean isPrettyPrint(HttpRequest request) { - return request.queryParam("pretty").isPresent(); + @Override + protected HttpResponse doHandle(HttpRequest request, HttpInterceptor.Context context) { + return response(OK) + .addHeader(CONTENT_TYPE, JSON_UTF_8.toString()) + .disableCaching() + .body(content(isPrettyPrint(request)), UTF_8) + .build(); } private String content(boolean pretty) { @@ -86,13 +91,8 @@ private ObjectWriter writer(boolean prettyPrint) { : this.mapper.writer(); } - @Override - protected HttpResponse doHandle(HttpRequest request, HttpInterceptor.Context context) { - return response(OK) - .addHeader(CONTENT_TYPE, JSON_UTF_8.toString()) - .disableCaching() - .body(content(isPrettyPrint(request)), UTF_8) - .build(); + private static boolean isPrettyPrint(HttpRequest request) { + return request.queryParam("pretty").isPresent(); } @Subscribe From bf35eadbe7088c9bc1db8396b32e5a1463791b5b Mon Sep 17 00:00:00 2001 From: Mikko Karjalainen Date: Tue, 28 May 2019 15:41:38 +0100 Subject: [PATCH 5/5] Address code review comments. --- .../styx/common/http/handler/HttpStreamer.java | 10 +++++----- .../com/hotels/styx/admin/AdminServerBuilder.java | 12 ++++++------ .../styx/admin/handlers/PluginToggleHandler.java | 6 +----- .../admin/handlers/StyxConfigurationHandler.java | 8 ++++---- 4 files changed, 16 insertions(+), 20 deletions(-) diff --git a/components/common/src/main/java/com/hotels/styx/common/http/handler/HttpStreamer.java b/components/common/src/main/java/com/hotels/styx/common/http/handler/HttpStreamer.java index 03dd7ce236..1146b79856 100644 --- a/components/common/src/main/java/com/hotels/styx/common/http/handler/HttpStreamer.java +++ b/components/common/src/main/java/com/hotels/styx/common/http/handler/HttpStreamer.java @@ -30,17 +30,17 @@ public class HttpStreamer implements WebServiceHandler { private static final int KILOBYTE = 1024; - private int bytes; + private int maxContentBytes; private HttpHandler delegate; /** * HttpStreamer constructor. * - * @param bytes max number of content bytes to aggregate + * @param maxContentBytes max number of content maxContentBytes to aggregate * @param delegate adapted HttpHandler instance */ - public HttpStreamer(int bytes, HttpHandler delegate) { - this.bytes = bytes; + public HttpStreamer(int maxContentBytes, HttpHandler delegate) { + this.maxContentBytes = maxContentBytes; this.delegate = requireNonNull(delegate); } @@ -56,6 +56,6 @@ public HttpStreamer(HttpHandler delegate) { @Override public Eventual handle(HttpRequest request, HttpInterceptor.Context context) { return delegate.handle(request.stream(), context) - .flatMap(live -> live.aggregate(bytes)); + .flatMap(live -> live.aggregate(maxContentBytes)); } } diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/AdminServerBuilder.java b/components/proxy/src/main/java/com/hotels/styx/admin/AdminServerBuilder.java index d5358f2680..34cb722151 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/AdminServerBuilder.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/AdminServerBuilder.java @@ -162,6 +162,12 @@ private WebServiceHandler adminEndpoints(StyxConfig styxConfig) { return httpRouter; } + private JsonHandler dashboardDataHandler(StyxConfig styxConfig) { + return new JsonHandler<>(new DashboardDataSupplier(backendServicesRegistry, environment, styxConfig), + Optional.of(Duration.ofSeconds(10)), + new MetricsModule(SECONDS, MILLISECONDS, false)); + } + private static Iterable indexLinkPaths() { return ImmutableSortedSet.of( link("version.txt", "/version.txt"), @@ -213,12 +219,6 @@ private static List pluginAdminEndpointRoutes(NamedPlu new PluginAdminEndpointRoute(namedPlugin, relativePath, new HttpStreamer(MEGABYTE, handler))); } - private JsonHandler dashboardDataHandler(StyxConfig styxConfig) { - return new JsonHandler<>(new DashboardDataSupplier(backendServicesRegistry, environment, styxConfig), - Optional.of(Duration.ofSeconds(10)), - new MetricsModule(SECONDS, MILLISECONDS, false)); - } - // allows key and value to be labelled in lambda instead of having to use Entry.getKey, Entry.getValue private static List mapToList(Map map, BiFunction function) { return map.entrySet().stream() diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/PluginToggleHandler.java b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/PluginToggleHandler.java index b37db260f0..3e546a24c4 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/PluginToggleHandler.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/PluginToggleHandler.java @@ -148,11 +148,7 @@ private static Matcher urlMatcher(HttpRequest request) { } private static Eventual requestedNewState(HttpRequest request) { - // TODO: Mikko: Crappy coding, fix this: - return Eventual.of(request) - .map(fullRequest -> fullRequest.bodyAs(UTF_8)) - .map(PluginToggleHandler::parseToBoolean) - .map(PluginEnabledState::fromBoolean); + return Eventual.of(PluginEnabledState.fromBoolean(parseToBoolean(request.bodyAs(UTF_8)))); } private static HttpResponse responseWith(HttpResponseStatus status, String message) { diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/StyxConfigurationHandler.java b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/StyxConfigurationHandler.java index 28d56bfb25..0b14fec673 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/StyxConfigurationHandler.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/StyxConfigurationHandler.java @@ -56,16 +56,16 @@ public Eventual handle(HttpRequest request, HttpInterceptor.Contex .map(StyxConfigurationHandler::disableCaching); } + private StaticBodyHttpHandler configHandler(boolean pretty) { + return pretty ? prettyStyxConfigHandler : styxConfigHandler; + } + private static HttpResponse disableCaching(HttpResponse response) { return response.newBuilder() .disableCaching() .build(); } - private StaticBodyHttpHandler configHandler(boolean pretty) { - return pretty ? prettyStyxConfigHandler : styxConfigHandler; - } - private static String body(Configuration styxConfig) { return styxConfig + "\n"; }