diff --git a/jaxrs/pom.xml b/jaxrs/pom.xml
index a268264e..c2c0a1ef 100644
--- a/jaxrs/pom.xml
+++ b/jaxrs/pom.xml
@@ -65,6 +65,10 @@
javax.inject
javax.inject
+
+ javax.servlet
+ javax.servlet-api
+
javax.ws.rs
javax.ws.rs-api
diff --git a/jaxrs/src/main/java/com/cerner/beadledom/jaxrs/JaxRsModule.java b/jaxrs/src/main/java/com/cerner/beadledom/jaxrs/JaxRsModule.java
index a5c19930..5dcb5b4d 100644
--- a/jaxrs/src/main/java/com/cerner/beadledom/jaxrs/JaxRsModule.java
+++ b/jaxrs/src/main/java/com/cerner/beadledom/jaxrs/JaxRsModule.java
@@ -1,7 +1,9 @@
package com.cerner.beadledom.jaxrs;
import com.cerner.beadledom.jaxrs.provider.CorrelationIdFilter;
+import com.cerner.beadledom.jaxrs.provider.MalformedRequestFilter;
import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
import javax.inject.Singleton;
/**
@@ -20,4 +22,9 @@ protected void configure() {
bind(CorrelationIdFilter.class).toProvider(CorrelationIdFilterProvider.class)
.in(Singleton.class);
}
+
+ @Provides
+ MalformedRequestFilter provideMalformedRequestFilter() {
+ return new MalformedRequestFilter();
+ }
}
diff --git a/jaxrs/src/main/java/com/cerner/beadledom/jaxrs/provider/FilteringJacksonJsonProvider.java b/jaxrs/src/main/java/com/cerner/beadledom/jaxrs/provider/FilteringJacksonJsonProvider.java
index 95279e77..f8274a2f 100644
--- a/jaxrs/src/main/java/com/cerner/beadledom/jaxrs/provider/FilteringJacksonJsonProvider.java
+++ b/jaxrs/src/main/java/com/cerner/beadledom/jaxrs/provider/FilteringJacksonJsonProvider.java
@@ -11,6 +11,7 @@
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import javax.inject.Inject;
+import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
@@ -32,6 +33,9 @@ public class FilteringJacksonJsonProvider extends JacksonJsonProvider {
@Context
UriInfo uriInfo;
+ @Context
+ HttpServletResponse httpServletResponse;
+
/**
* Creates a new instance of {@link FilteringJacksonJsonProvider}.
*/
@@ -52,6 +56,12 @@ public long getSize(
public void writeTo(
Object o, Class> type, Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap httpHeaders, OutputStream os) throws IOException {
+
+ if (httpServletResponse.getStatus() >= 400 ) {
+ super.writeTo(o, type, genericType, annotations, mediaType, httpHeaders, os);
+ return;
+ }
+
String fields = uriInfo.getQueryParameters() == null ? null
: uriInfo.getQueryParameters().getFirst("fields");
diff --git a/jaxrs/src/main/java/com/cerner/beadledom/jaxrs/provider/MalformedRequestFilter.java b/jaxrs/src/main/java/com/cerner/beadledom/jaxrs/provider/MalformedRequestFilter.java
new file mode 100644
index 00000000..cc05625c
--- /dev/null
+++ b/jaxrs/src/main/java/com/cerner/beadledom/jaxrs/provider/MalformedRequestFilter.java
@@ -0,0 +1,37 @@
+package com.cerner.beadledom.jaxrs.provider;
+
+import com.cerner.beadledom.json.common.model.JsonError;
+import java.net.URI;
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * The MalformedRequestFilter reads the request context and determines if the request has a valid uri structure if it
+ * does not then it aborts the request and responds with a 400
+ *
+ * @author Kyle Roush
+ */
+@Provider
+@Priority(Priorities.AUTHENTICATION)
+@PreMatching
+public class MalformedRequestFilter implements ContainerRequestFilter {
+
+ @Override
+ public void filter(ContainerRequestContext requestContext) {
+ try {
+ URI.create(requestContext.getUriInfo().getAbsolutePath().toString());
+ } catch (IllegalArgumentException e) {
+ requestContext.abortWith(
+ Response.status(400)
+ .type(MediaType.APPLICATION_JSON)
+ .entity(JsonError.builder().code(400).message("Malformed URI").build())
+ .build());
+ }
+ }
+}
diff --git a/jaxrs/src/test/scala/com/cerner/beadledom/jaxrs/provider/FilteringJacksonJsonProviderSpec.scala b/jaxrs/src/test/scala/com/cerner/beadledom/jaxrs/provider/FilteringJacksonJsonProviderSpec.scala
index 78738025..98665b62 100644
--- a/jaxrs/src/test/scala/com/cerner/beadledom/jaxrs/provider/FilteringJacksonJsonProviderSpec.scala
+++ b/jaxrs/src/test/scala/com/cerner/beadledom/jaxrs/provider/FilteringJacksonJsonProviderSpec.scala
@@ -6,6 +6,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
import com.google.common.collect.Lists
import java.io.{ByteArrayOutputStream, OutputStream}
import java.nio.charset.Charset
+import javax.servlet.http.HttpServletResponse
import javax.ws.rs.core._
import org.jboss.resteasy.specimpl.MultivaluedMapImpl
import org.mockito
@@ -32,11 +33,14 @@ class FilteringJacksonJsonProviderSpec
it("serializes a FakeModel") {
val uriInfo = Mockito.mock(classOf[UriInfo])
+ val httpServletResponse = Mockito.mock(classOf[HttpServletResponse])
Mockito.when(uriInfo.getQueryParameters).thenReturn(new MultivaluedMapImpl[String, String])
+ Mockito.when(httpServletResponse.getStatus).thenReturn(200)
val output = Mockito.mock(classOf[OutputStream])
val filter = new FilteringJacksonJsonProvider(objectMapper)
filter.uriInfo = uriInfo
+ filter.httpServletResponse = httpServletResponse
filter.writeTo(fakeModel,
fakeModel.getClass,
@@ -56,6 +60,9 @@ class FilteringJacksonJsonProviderSpec
it("filters simple bean properties") {
val uriInfo = Mockito.mock(classOf[UriInfo])
+ val httpServletResponse = Mockito.mock(classOf[HttpServletResponse])
+ Mockito.when(httpServletResponse.getStatus).thenReturn(200)
+
val queryParams = new MultivaluedMapImpl[String, String]
queryParams.add("fields", "id,name,times")
Mockito.when(uriInfo.getQueryParameters).thenReturn(queryParams)
@@ -63,6 +70,7 @@ class FilteringJacksonJsonProviderSpec
val filter = new FilteringJacksonJsonProvider(objectMapper)
filter.uriInfo = uriInfo
+ filter.httpServletResponse = httpServletResponse
filter.writeTo(fakeModel,
fakeModel.getClass,
@@ -82,6 +90,9 @@ class FilteringJacksonJsonProviderSpec
it("filters nested bean properties") {
val uriInfo = Mockito.mock(classOf[UriInfo])
+ val httpServletResponse = Mockito.mock(classOf[HttpServletResponse])
+ Mockito.when(httpServletResponse.getStatus).thenReturn(200)
+
val queryParams = new MultivaluedMapImpl[String, String]
queryParams.add("fields", "id,name,inner_models/id")
Mockito.when(uriInfo.getQueryParameters).thenReturn(queryParams)
@@ -89,6 +100,7 @@ class FilteringJacksonJsonProviderSpec
val filter = new FilteringJacksonJsonProvider(objectMapper)
filter.uriInfo = uriInfo
+ filter.httpServletResponse = httpServletResponse
filter.writeTo(fakeModel,
fakeModel.getClass,
@@ -123,6 +135,9 @@ class FilteringJacksonJsonProviderSpec
Venue("venue_id1", "THE venue", "THE place", tenors, vocalists, guitarists, failures)
val uriInfo = Mockito.mock(classOf[UriInfo])
+ val httpServletResponse = Mockito.mock(classOf[HttpServletResponse])
+ Mockito.when(httpServletResponse.getStatus).thenReturn(200)
+
val queryParams = new MultivaluedMapImpl[String, String]
queryParams.add("fields",
"id,name,tenors/pitch_pipe,tenors/name,tenors/albums/name,vocalists/albums/id,vocalists/albums/name," +
@@ -132,6 +147,7 @@ class FilteringJacksonJsonProviderSpec
val filter = new FilteringJacksonJsonProvider(objectMapper)
filter.uriInfo = uriInfo
+ filter.httpServletResponse = httpServletResponse
filter.writeTo(venue,
venue.getClass,
@@ -154,11 +170,15 @@ class FilteringJacksonJsonProviderSpec
it("serializes a small FakeModel") {
val uriInfo = Mockito.mock(classOf[UriInfo])
+ val httpServletResponse = Mockito.mock(classOf[HttpServletResponse])
+ Mockito.when(httpServletResponse.getStatus).thenReturn(200)
+
Mockito.when(uriInfo.getQueryParameters).thenReturn(new MultivaluedMapImpl[String, String])
val output = Mockito.mock(classOf[OutputStream])
val filter = new FilteringJacksonJsonProvider(objectMapper)
filter.uriInfo = uriInfo
+ filter.httpServletResponse = httpServletResponse
val startTime = System.currentTimeMillis()
filter.writeTo(fakeModel,
@@ -173,10 +193,14 @@ class FilteringJacksonJsonProviderSpec
it("serializes a large FakeModel - about 6MB") {
(0 until 100000).foreach({ value => fakeModel.innerModels.add(fakeInnerModel1) })
val uriInfo = Mockito.mock(classOf[UriInfo])
+ val httpServletResponse = Mockito.mock(classOf[HttpServletResponse])
+ Mockito.when(httpServletResponse.getStatus).thenReturn(200)
+
Mockito.when(uriInfo.getQueryParameters).thenReturn(new MultivaluedMapImpl[String, String])
val filter = new FilteringJacksonJsonProvider(objectMapper)
filter.uriInfo = uriInfo
+ filter.httpServletResponse = httpServletResponse
(0 until 10).foreach { value =>
System.gc()
@@ -200,12 +224,16 @@ class FilteringJacksonJsonProviderSpec
it("serializes a large FakeModel with filtering - about 6MB") {
(0 until 100000).foreach({ value => fakeModel.innerModels.add(fakeInnerModel1) })
val uriInfo = Mockito.mock(classOf[UriInfo])
+ val httpServletResponse = Mockito.mock(classOf[HttpServletResponse])
+ Mockito.when(httpServletResponse.getStatus).thenReturn(200)
+
val queryParams = new MultivaluedMapImpl[String, String]
queryParams.add("fields", "id,name,inner_models/id")
Mockito.when(uriInfo.getQueryParameters).thenReturn(queryParams)
val filter = new FilteringJacksonJsonProvider(objectMapper)
filter.uriInfo = uriInfo
+ filter.httpServletResponse = httpServletResponse
(0 until 10).foreach { value =>
System.gc()
diff --git a/jaxrs/src/test/scala/com/cerner/beadledom/jaxrs/provider/MalformedRequestFilterSpec.scala b/jaxrs/src/test/scala/com/cerner/beadledom/jaxrs/provider/MalformedRequestFilterSpec.scala
new file mode 100644
index 00000000..7e6a9c4c
--- /dev/null
+++ b/jaxrs/src/test/scala/com/cerner/beadledom/jaxrs/provider/MalformedRequestFilterSpec.scala
@@ -0,0 +1,54 @@
+package com.cerner.beadledom.jaxrs.provider
+
+import java.net.URI
+
+import com.cerner.beadledom.json.common.model.JsonError
+import javax.ws.rs.container.ContainerRequestContext
+import javax.ws.rs.core.{MediaType, Response, UriInfo}
+import org.jboss.resteasy.specimpl.ResteasyUriInfo
+import org.mockito
+import org.mockito.{ArgumentCaptor, Mockito}
+import org.mockito.ArgumentMatchers.any
+import org.scalatest._
+
+import scala.collection.JavaConverters._
+import org.scalatestplus.mockito.MockitoSugar
+import org.scalatest.funspec.AnyFunSpec
+import org.scalatest.matchers.should.Matchers
+
+class MalformedRequestFilterSpec
+ extends AnyFunSpec with BeforeAndAfterAll with Matchers with MockitoSugar {
+
+ describe("MalformedRequestFilter with a valid URI") {
+
+ val containerRequestContext = Mockito.mock(classOf[ContainerRequestContext])
+ val uriInfo = new ResteasyUriInfo("http://localhost:8080", "")
+ Mockito.when(containerRequestContext.getUriInfo).thenReturn(uriInfo)
+
+ val malformedRequestFilter = new MalformedRequestFilter()
+
+ it("does not call abortWith") {
+ malformedRequestFilter.filter(containerRequestContext)
+
+ Mockito.verify(containerRequestContext).getUriInfo();
+ Mockito.verifyNoMoreInteractions(containerRequestContext);
+ }
+
+ }
+ describe("MalformedRequestFilter with an invalid URI") {
+
+ val containerRequestContext = Mockito.mock(classOf[ContainerRequestContext])
+ val uriInfo = new ResteasyUriInfo("http://localhost:8080", "?test=%9")
+ Mockito.when(containerRequestContext.getUriInfo).thenReturn(uriInfo)
+
+ val malformedRequestFilter = new MalformedRequestFilter()
+ it("calls abortWith") {
+ malformedRequestFilter.filter(containerRequestContext)
+
+ Mockito.verify(containerRequestContext)
+ .abortWith(any(classOf[Response]))
+
+ }
+
+ }
+}