diff --git a/components/api/src/main/java/com/hotels/styx/api/messages/HttpResponseStatus.java b/components/api/src/main/java/com/hotels/styx/api/messages/HttpResponseStatus.java index db3c25e525..eef6ff1d84 100644 --- a/components/api/src/main/java/com/hotels/styx/api/messages/HttpResponseStatus.java +++ b/components/api/src/main/java/com/hotels/styx/api/messages/HttpResponseStatus.java @@ -16,6 +16,7 @@ package com.hotels.styx.api.messages; import java.lang.reflect.Field; +import java.util.Objects; import java.util.stream.Stream; import static java.lang.reflect.Modifier.isStatic; @@ -134,4 +135,18 @@ public String description() { public String toString() { return code + " " + description; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + HttpResponseStatus that = (HttpResponseStatus) o; + return code == that.code; + } + + @Override + public int hashCode() { + + return Objects.hash(code); + } } diff --git a/components/api/src/main/java/com/hotels/styx/api/support/PathTrie.java b/components/api/src/main/java/com/hotels/styx/api/support/PathTrie.java index ac999127f4..f87c391e60 100644 --- a/components/api/src/main/java/com/hotels/styx/api/support/PathTrie.java +++ b/components/api/src/main/java/com/hotels/styx/api/support/PathTrie.java @@ -252,7 +252,7 @@ private List pathToComponents(Path path) { private String removeAsterisk(String path) { String newPath = path; - if (path.endsWith("*")) { + if (path.endsWith("/*")) { newPath = path.substring(0, path.length() - 1); } return newPath; diff --git a/components/api/src/test/java/com/hotels/styx/api/io/ResourceContentMatcher.java b/components/api/src/test/java/com/hotels/styx/api/io/ResourceContentMatcher.java index ee03979c26..7f965cbf85 100644 --- a/components/api/src/test/java/com/hotels/styx/api/io/ResourceContentMatcher.java +++ b/components/api/src/test/java/com/hotels/styx/api/io/ResourceContentMatcher.java @@ -36,8 +36,16 @@ private ResourceContentMatcher(String expected) { this.expected = checkNotNull(expected); } + /** + * Returns a Matcher that will compare the textual content of this {@link Resource} (using UTF-8 encoding) + * to the provided String. Line separator differences will be ignored as long the CRLF or LF sequences + * are used for line breaks. + * + * @param expected text string to which this resource will be compared. + * @return + */ public static ResourceContentMatcher contains(String expected) { - return new ResourceContentMatcher(expected); + return new ResourceContentMatcher(expected.replace("\r\n", "\n")); } @Override diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/HealthCheckHandler.java b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/HealthCheckHandler.java index e392e5e52c..eda6ca6abe 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/HealthCheckHandler.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/HealthCheckHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (C) 2013-2017 Expedia Inc. + * Copyright (C) 2013-2018 Expedia Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,6 +34,7 @@ import static com.google.common.base.Preconditions.checkNotNull; 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.Builder.response; import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR; import static io.netty.handler.codec.http.HttpResponseStatus.NOT_IMPLEMENTED; 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 40b320939d..63d0f2b1ec 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-2017 Expedia Inc. + * Copyright (C) 2013-2018 Expedia Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ package com.hotels.styx.admin.handlers; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.util.DefaultIndenter; +import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.google.common.eventbus.EventBus; @@ -34,6 +36,7 @@ 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.Builder.response; import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static org.slf4j.LoggerFactory.getLogger; @@ -44,7 +47,8 @@ public class OriginsInventoryHandler extends BaseHttpHandler implements OriginsInventoryStateChangeListener { private static final Logger LOG = getLogger(OriginsInventoryHandler.class); - private final ObjectMapper mapper = new ObjectMapper().disable(FAIL_ON_EMPTY_BEANS); + private final ObjectMapper mapper = new ObjectMapper().disable(FAIL_ON_EMPTY_BEANS) + .setDefaultPrettyPrinter(PRETTY_PRINTER); private final Map originsInventorySnapshotMap = new ConcurrentHashMap<>(); diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/Resources.java b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/Resources.java index 8437d68515..f64acb2da8 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/Resources.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/Resources.java @@ -15,34 +15,22 @@ */ package com.hotels.styx.admin.handlers; +import com.google.common.io.CharStreams; import com.hotels.styx.api.Resource; +import java.io.BufferedReader; import java.io.IOException; -import java.net.URISyntaxException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.stream.Stream; - -import static com.google.common.base.Throwables.propagate; -import static java.lang.System.lineSeparator; -import static java.util.stream.Collectors.joining; +import java.io.InputStreamReader; +import java.io.Reader; final class Resources { private Resources() { } public static String load(Resource resource) throws IOException { - try { - return fileContents(Paths.get(resource.url().toURI())); - } catch (URISyntaxException e) { - throw propagate(e); + try (Reader reader = new BufferedReader(new InputStreamReader(resource.inputStream()))) { + return CharStreams.toString(reader); } } - private static String fileContents(Path path) throws IOException { - try (Stream lines = Files.lines(path)) { - return lines.collect(joining(lineSeparator())); - } - } } diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/support/Json.java b/components/proxy/src/main/java/com/hotels/styx/admin/support/Json.java index 59fc86896c..b192d3b70c 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/support/Json.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/support/Json.java @@ -25,9 +25,7 @@ public final class Json { // Always uses unix-style line separators regardless of platform public static final PrettyPrinter PRETTY_PRINTER = new DefaultPrettyPrinter() - .withObjectIndenter(new DefaultIndenter().withLinefeed("\n")) - .withArrayIndenter(new DefaultIndenter().withLinefeed("\n")); - + .withObjectIndenter(new DefaultIndenter().withLinefeed("\n")); private Json() { } 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 0729f18c41..48627946cc 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 @@ -17,6 +17,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.type.MapType; +import com.google.common.base.Charsets; import com.google.common.eventbus.EventBus; import com.hotels.styx.admin.tasks.StubConnectionPool; import com.hotels.styx.api.Id; @@ -87,20 +88,21 @@ public void prettyPrintsOriginsSnapshot() { eventBus.post(new OriginsInventorySnapshot(APP_ID, pool(emptySet()), pool(emptySet()), pool(disabledOrigins))); FullHttpResponse response = waitForResponse(handler.handle(get("/?pretty=1").build())); - assertThat(response.bodyAs(UTF_8), matchesRegex("\\{\n" + - " \"" + APP_ID + "\" : \\{\n" + - " \"appId\" : \"" + APP_ID + "\",\n" + - " \"activeOrigins\" : \\[ ],\n" + - " \"inactiveOrigins\" : \\[ ],\n" + - " \"disabledOrigins\" : \\[ \\{\n" + - " \"id\" : \"origin.\",\n" + - " \"host\" : \"localhost:....\"\n" + - " }, \\{\n" + - " \"id\" : \"origin.\",\n" + - " \"host\" : \"localhost:....\"\n" + - " } ]\n" + - " }\n" + - "}")); + assertThat(body(response).replace("\r\n", "\n"), + matchesRegex("\\{\n" + + " \"" + APP_ID + "\" : \\{\n" + + " \"appId\" : \"" + APP_ID + "\",\n" + + " \"activeOrigins\" : \\[ ],\n" + + " \"inactiveOrigins\" : \\[ ],\n" + + " \"disabledOrigins\" : \\[ \\{\n" + + " \"id\" : \"origin.\",\n" + + " \"host\" : \"localhost:....\"\n" + + " }, \\{\n" + + " \"id\" : \"origin.\",\n" + + " \"host\" : \"localhost:....\"\n" + + " } ]\n" + + " }\n" + + "}")); } @Test @@ -133,4 +135,8 @@ private static List pool(Set origins) { .map(pool -> remoteHost(pool.getOrigin(), pool, mock(StyxHostHttpClient.class))) .collect(toList()); } + + private String body(FullHttpResponse response){ + return response.bodyAs(Charsets.UTF_8).replace("\r\n", "\n"); + } } \ No newline at end of file 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 d9a4071731..3b0c4f0de6 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 @@ -40,7 +40,6 @@ public void readsClassPathResources() { FullHttpResponse response = waitForResponse(handler.handle(request)); assertThat(response.status(), is(OK)); - assertThat(response.contentLength(), isValue("Foo\nBar\n".length())); assertThat(body(response), is("Foo\nBar\n")); } diff --git a/pom.xml b/pom.xml index e171c16b98..3ddf6c0588 100755 --- a/pom.xml +++ b/pom.xml @@ -140,7 +140,7 @@ 2.6 3.3 2.2.1 - 2.16 + 2.19 1.0 diff --git a/support/testsupport/src/main/java/com/hotels/styx/support/matchers/LoggingEventMatcher.java b/support/testsupport/src/main/java/com/hotels/styx/support/matchers/LoggingEventMatcher.java index fe22b105d3..c92ee62b80 100644 --- a/support/testsupport/src/main/java/com/hotels/styx/support/matchers/LoggingEventMatcher.java +++ b/support/testsupport/src/main/java/com/hotels/styx/support/matchers/LoggingEventMatcher.java @@ -1,5 +1,5 @@ /** - * Copyright (C) 2013-2017 Expedia Inc. + * Copyright (C) 2013-2018 Expedia Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -106,7 +106,7 @@ public void describeTo(Description description) { @Override protected boolean matchesSafely(ILoggingEvent event) { return this.level.equals(event.getLevel()) - && this.message.matches(event.getFormattedMessage().replace("\n", "")) + && this.message.matches(event.getFormattedMessage().replace(System.lineSeparator(), "")) && exceptionMatches(event.getThrowableProxy()); } @@ -138,7 +138,7 @@ public void describeTo(Description description) { @Override protected boolean matchesSafely(IThrowableProxy item) { return exceptionClass.equals(item.getClassName()) - && exceptionMessage.matches(item.getMessage().replace("\n", "")) + && exceptionMessage.matches(item.getMessage().replace(System.lineSeparator(), "")) && item.getStackTraceElementProxyArray().length > 0; } } diff --git a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/admin/FileBasedOriginsFileChangeMonitorSpec.scala b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/admin/FileBasedOriginsFileChangeMonitorSpec.scala index a7885d421b..3e1123c217 100644 --- a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/admin/FileBasedOriginsFileChangeMonitorSpec.scala +++ b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/admin/FileBasedOriginsFileChangeMonitorSpec.scala @@ -1,3 +1,18 @@ +/** + * 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. + */ /** * Copyright (C) 2013-2018 Expedia Inc. * @@ -15,11 +30,11 @@ */ package com.hotels.styx.admin -import java.io.ByteArrayInputStream +import java.io.{ByteArrayInputStream, File} import java.nio.charset.StandardCharsets.UTF_8 import java.nio.file.Files.{copy, delete} +import java.nio.file.Path import java.nio.file.StandardCopyOption.REPLACE_EXISTING -import java.nio.file.{Files, Path, Paths} import com.google.common.io.Files.createTempDir import com.hotels.styx.api.messages.FullHttpRequest.get @@ -39,7 +54,7 @@ class FileBasedOriginsFileChangeMonitorSpec extends FunSpec with Eventually { val tempDir = createTempDir() - val styxOriginsFile = Paths.get(tempDir.toString, "origins.yml") + val styxOriginsFile = new File(tempDir, "origins.yml").toPath val origin = FakeHttpServer.HttpStartupConfig(appId = "app", originId="app").start() @@ -78,7 +93,7 @@ class FileBasedOriginsFileChangeMonitorSpec extends FunSpec | backendServiceRegistry: | class: "com.hotels.styx.proxy.backends.file.FileBackedBackendServicesRegistry$$Factory" | config: - | originsFile: "${styxOriginsFile.toString}" + | originsFile: "${styxOriginsFile.toString.replace("\\","/")}" | monitor: | enabled: true """.stripMargin diff --git a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/plugins/PluginToggleSpec.scala b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/plugins/PluginToggleSpec.scala index c3bd8b030d..1b0377ffaa 100644 --- a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/plugins/PluginToggleSpec.scala +++ b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/plugins/PluginToggleSpec.scala @@ -49,7 +49,7 @@ class PluginToggleSpec extends FunSpec with StyxProxySpec with StyxClientSupplie resp2.status() should be (OK) resp2.bodyAs(UTF_8) should include("response-from-plugin") - checkPluginEnabled() should be("enabled\n") + checkPluginEnabled().trim should be("enabled") } override protected def afterAll(): Unit = { @@ -79,17 +79,17 @@ class PluginToggleSpec extends FunSpec with StyxProxySpec with StyxClientSupplie disablePlugin() val outcome = setPluginEnabled("true") - outcome should be("{\"message\":\"State of 'pluginUnderTest' changed to 'enabled'\",\"plugin\":{\"name\":\"pluginUnderTest\",\"state\":\"enabled\"}}"+System.lineSeparator) - checkPluginEnabled() should be("enabled\n") + outcome.trim should be("{\"message\":\"State of 'pluginUnderTest' changed to 'enabled'\",\"plugin\":{\"name\":\"pluginUnderTest\",\"state\":\"enabled\"}}") + checkPluginEnabled().trim should be("enabled") } } private def disablePlugin() = { val outcome = setPluginEnabled("false") - outcome should be("{\"message\":\"State of 'pluginUnderTest' changed to 'disabled'\",\"plugin\":{\"name\":\"pluginUnderTest\",\"state\":\"disabled\"}}"+System.lineSeparator) + outcome.trim should be("{\"message\":\"State of 'pluginUnderTest' changed to 'disabled'\",\"plugin\":{\"name\":\"pluginUnderTest\",\"state\":\"disabled\"}}") - checkPluginEnabled() should be("disabled\n") + checkPluginEnabled().trim should be("disabled") } private def setPluginEnabled(enabled : String): String = { diff --git a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/proxy/BadRequestsSpec.scala b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/proxy/BadRequestsSpec.scala index b7cde76677..1dd8c49d2d 100644 --- a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/proxy/BadRequestsSpec.scala +++ b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/proxy/BadRequestsSpec.scala @@ -39,11 +39,10 @@ import io.netty.handler.codec.http.HttpVersion.HTTP_1_1 import io.netty.handler.codec.http._ import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.{hasItem, is} +import org.scalatest.FunSpec import org.scalatest.concurrent.Eventually -import org.scalatest.{FunSpec, Ignore} import scala.concurrent.duration._ -@Ignore class BadRequestsSpec extends FunSpec with StyxProxySpec with TestClientSupport @@ -133,7 +132,7 @@ class BadRequestsSpec extends FunSpec assert(response.status == BAD_REQUEST, s"\nExpecting 400 Bad Request in message: \n$response \n\n$content\n\n") assertThat(response.headers().get(STYX_INFO_DEFAULT), matchesRegex("noJvmRouteSet;")) - assertThat(loggingSupport.log(), hasItem(loggingEvent(ERROR, "Failure status=\"400 Bad Request\"", "io.netty.handler.codec.DecoderException", "com.hotels.styx.server.BadRequestException: Bad Host header.*"))) + assertThat(loggingSupport.log(), hasItem(loggingEvent(ERROR, "Failure status=\"400 Bad Request\"", "io.netty.handler.codec.DecoderException", "com.hotels.styx.server.BadRequestException: Bad Host header. .*"))) } }