Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions jaxrs/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.cerner.beadledom.jaxrs;

import com.cerner.beadledom.jaxrs.provider.CorrelationIdFilter;
import com.cerner.beadledom.jaxrs.provider.MalformedRequestFilter;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update CHANGELOG.md with the added class.

import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import javax.inject.Singleton;

/**
Expand All @@ -20,4 +22,9 @@ protected void configure() {
bind(CorrelationIdFilter.class).toProvider(CorrelationIdFilterProvider.class)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update the class Javadoc to indicate that the module now provides the MalformedRequestFilter

.in(Singleton.class);
}

@Provides
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add @Singleton

MalformedRequestFilter provideMalformedRequestFilter() {
return new MalformedRequestFilter();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -32,6 +33,9 @@ public class FilteringJacksonJsonProvider extends JacksonJsonProvider {
@Context
UriInfo uriInfo;

@Context
HttpServletResponse httpServletResponse;

/**
* Creates a new instance of {@link FilteringJacksonJsonProvider}.
*/
Expand All @@ -52,6 +56,12 @@ public long getSize(
public void writeTo(
Object o, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders, OutputStream os) throws IOException {

if (httpServletResponse.getStatus() >= 400 ) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of pulling in the servlet-api, can we update the code to something like

    String fields = null;
    try {
      fields = uriInfo.getQueryParameters() == null ? null
          : uriInfo.getQueryParameters().getFirst("fields");
    } catch (Throwable e) {
      // Nothing to do. URI does not conform to grammar of RFC 2396.
    }

super.writeTo(o, type, genericType, annotations, mediaType, httpHeaders, os);
return;
}

String fields = uriInfo.getQueryParameters() == null ? null
: uriInfo.getQueryParameters().getFirst("fields");

Expand Down
Original file line number Diff line number Diff line change
@@ -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)
Copy link
Copy Markdown
Contributor

@nathanschile nathanschile Feb 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we lower this to a USER priority? Probably Not. I see that ForwardedHeaderFilter also uses UriInfo.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this error occur at the first call of UriInfo in the stack?

@PreMatching
public class MalformedRequestFilter implements ContainerRequestFilter {

@Override
public void filter(ContainerRequestContext requestContext) {
try {
URI.create(requestContext.getUriInfo().getAbsolutePath().toString());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can do requestContext.getUriInfo().getRequestUri(); so it doesn't create a new URI.

} catch (IllegalArgumentException e) {
requestContext.abortWith(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's log the exception.

Response.status(400)
.type(MediaType.APPLICATION_JSON)
.entity(JsonError.builder().code(400).message("Malformed URI").build())
.build());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -56,13 +60,17 @@ 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)
val output = Mockito.mock(classOf[OutputStream])

val filter = new FilteringJacksonJsonProvider(objectMapper)
filter.uriInfo = uriInfo
filter.httpServletResponse = httpServletResponse

filter.writeTo(fakeModel,
fakeModel.getClass,
Expand All @@ -82,13 +90,17 @@ 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)
val output = new ByteArrayOutputStream()

val filter = new FilteringJacksonJsonProvider(objectMapper)
filter.uriInfo = uriInfo
filter.httpServletResponse = httpServletResponse

filter.writeTo(fakeModel,
fakeModel.getClass,
Expand Down Expand Up @@ -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," +
Expand All @@ -132,6 +147,7 @@ class FilteringJacksonJsonProviderSpec

val filter = new FilteringJacksonJsonProvider(objectMapper)
filter.uriInfo = uriInfo
filter.httpServletResponse = httpServletResponse

filter.writeTo(venue,
venue.getClass,
Expand All @@ -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,
Expand All @@ -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()
Expand All @@ -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()
Expand Down
Original file line number Diff line number Diff line change
@@ -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}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cleanup imports

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();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit. This can be verify(containerRequestContext).getUriInfo, since Scala doesn't require the parens and semicolon. You can also import org.mockito.Mockito.{verify, when} so you don't need to have Mockito.

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]))

}

}
}