Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PUT request with no body fails (Jersey) #147

Closed
boardthatpowder opened this issue Apr 20, 2018 · 8 comments
Closed

PUT request with no body fails (Jersey) #147

boardthatpowder opened this issue Apr 20, 2018 · 8 comments
Labels
Milestone

Comments

@boardthatpowder
Copy link

  • Framework version: 1.1
  • Implementations: Jersey

Scenario

REST PUT requests for managing relationships between resources generally have no request body compared with REST PUT requests that manage updating resource attributes do.

Attempting a REST PUT with no request body fails. This was working in framework version 0.7.

Expected behavior

REST PUT with no body returns successfully.

Actual behavior

Fails with java.io.IOException: Read after end of file exception.

Steps to reproduce

Create controller:

	@PUT
	@Path("/parent/{parent}/child/{child}")
	public Response attachChildToParent (
			@PathParam("parent") String parent,
			@PathParam("child") String child ) {
             System.out.println("If reach here, its worked!");
    }

Executer request PUT /parent/A/child/B with no request body.

Full log output

java.io.IOException: Read after end of file
at org.apache.commons.io.input.NullInputStream.read(NullInputStream.java:187)
at com.amazonaws.serverless.proxy.internal.servlet.AwsProxyHttpServletRequest$AwsServletInputStream.read(AwsProxyHttpServletRequest.java:835)
at java.io.InputStream.read(InputStream.java:170)
at org.glassfish.jersey.message.internal.EntityInputStream.read(EntityInputStream.java:103)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream.read(ReaderInterceptorExecutor.java:297)
at com.fasterxml.jackson.core.json.ByteSourceJsonBootstrapper.ensureLoaded(ByteSourceJsonBootstrapper.java:522)
at com.fasterxml.jackson.core.json.ByteSourceJsonBootstrapper.detectEncoding(ByteSourceJsonBootstrapper.java:150)
at com.fasterxml.jackson.core.json.ByteSourceJsonBootstrapper.constructParser(ByteSourceJsonBootstrapper.java:246)
at com.fasterxml.jackson.core.JsonFactory._createParser(JsonFactory.java:1271)
at com.fasterxml.jackson.core.JsonFactory.createParser(JsonFactory.java:810)
at org.glassfish.jersey.jackson.internal.jackson.jaxrs.base.ProviderBase._createParser(ProviderBase.java:852)
at org.glassfish.jersey.jackson.internal.jackson.jaxrs.base.ProviderBase.readFrom(ProviderBase.java:793)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.invokeReadFrom(ReaderInterceptorExecutor.java:257)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:236)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:156)
at org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundReadFrom(MappableExceptionWrapperInterceptor.java:73)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:156)
at org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:1091)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:874)
at org.glassfish.jersey.server.ContainerRequest.readEntity(ContainerRequest.java:271)
at org.glassfish.jersey.server.internal.inject.EntityParamValueParamProvider$EntityValueSupplier.apply(EntityParamValueParamProvider.java:97)
at org.glassfish.jersey.server.internal.inject.EntityParamValueParamProvider$EntityValueSupplier.apply(EntityParamValueParamProvider.java:80)
at org.glassfish.jersey.server.spi.internal.ParamValueFactoryWithSource.apply(ParamValueFactoryWithSource.java:74)
at org.glassfish.jersey.server.spi.internal.ParameterValueHelper.getParameterValues(ParameterValueHelper.java:92)
at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$AbstractMethodParamInvoker.getParamValues(JavaResourceMethodDispatcherProvider.java:133)
at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:200)
at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:103)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:493)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:415)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:104)
at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:277)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:272)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:268)
at org.glassfish.jersey.internal.Errors.process(Errors.java:316)
at org.glassfish.jersey.internal.Errors.process(Errors.java:298)
at org.glassfish.jersey.internal.Errors.process(Errors.java:268)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:289)
at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:256)
at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:703)
at com.amazonaws.serverless.proxy.jersey.JerseyHandlerFilter.doFilter(JerseyHandlerFilter.java:91)
at com.amazonaws.serverless.proxy.internal.servlet.FilterChainHolder.doFilter(FilterChainHolder.java:84)
at com.amazonaws.serverless.proxy.internal.servlet.AwsLambdaServletContainerHandler.doFilter(AwsLambdaServletContainerHandler.java:207)
at com.amazonaws.serverless.proxy.jersey.JerseyLambdaContainerHandler.handleRequest(JerseyLambdaContainerHandler.java:171)
at com.amazonaws.serverless.proxy.jersey.JerseyLambdaContainerHandler.handleRequest(JerseyLambdaContainerHandler.java:75)
at com.amazonaws.serverless.proxy.internal.LambdaContainerHandler.proxy(LambdaContainerHandler.java:168)
at com.amazonaws.serverless.proxy.internal.LambdaContainerHandler.proxyStream(LambdaContainerHandler.java:200)
at com.aws.cdf.assetlibrary.lambda.LambdaHandler.handleRequest(LambdaHandler.java:29)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at lambdainternal.EventHandlerLoader$StreamMethodRequestHandler.handleRequest(EventHandlerLoader.java:350)
at lambdainternal.EventHandlerLoader$2.call(EventHandlerLoader.java:888)
at lambdainternal.AWSLambda.startRuntime(AWSLambda.java:283)
at lambdainternal.AWSLambda.<clinit>(AWSLambda.java:64)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at lambdainternal.LambdaRTEntry.main(LambdaRTEntry.java:94)
@sapessi
Copy link
Collaborator

sapessi commented Apr 20, 2018

Mh, I cannot reproduce in local with a unit test. My resource is defined like this:

@Path("/empty-stream/{paramId}") @PUT
@Produces(MediaType.APPLICATION_JSON)
public Response emptyStream(@PathParam("paramId") String paramId) {
    SingleValueModel sv = new SingleValueModel();
    sv.setValue(paramId);
    return Response.ok(sv).build();
}

The test is defined like this:

@Test
public void emptyStream_putNullBody_expectPutToSucceed() {
    AwsProxyRequest request = new AwsProxyRequestBuilder("/echo/empty-stream/" + QUERY_STRING_KEY, "PUT").body(null).build();
    AwsProxyResponse resp = handler.proxy(request, lambdaContext);
    assertEquals(200, resp.getStatusCode());
}

I'm getting a 200 response. What am I missing?

@sapessi
Copy link
Collaborator

sapessi commented Jun 19, 2018

Hey @boardthatpowder, any hint you can give me on this?

@sapessi
Copy link
Collaborator

sapessi commented Jun 25, 2018

Closing since I haven't heard back from @boardthatpowder. If you are still running into this issue with the latest version - 1.1.3 - feel free to re-open.

@sapessi sapessi closed this as completed Oct 23, 2018
@eduramiba
Copy link

eduramiba commented May 6, 2019

Hi,
We can reproduce this issue with version 1.3.1.

Our handling code looks like this:

@Override
public void handleRequest(InputStream input, OutputStream output, Context context) {
        HANDLER.proxyStream(input, output, context);
}

And the stack trace at our jersey unhandled exceptions mapper:

Unhandled exception: java.io.IOException: Read after end of file
at org.apache.commons.io.input.NullInputStream.read(NullInputStream.java:189)
at com.amazonaws.serverless.proxy.internal.servlet.AwsProxyHttpServletRequest$AwsServletInputStream.read(AwsProxyHttpServletRequest.java:895)
at java.io.InputStream.read(InputStream.java:170)
at org.glassfish.jersey.message.internal.EntityInputStream.read(EntityInputStream.java:79)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream.read(ReaderInterceptorExecutor.java:273)
at com.fasterxml.jackson.core.json.ByteSourceJsonBootstrapper.ensureLoaded(ByteSourceJsonBootstrapper.java:522)
[........]
at xxxxxxxxxxx.LambdaHandler.handleRequest(LambdaHandler.java:215)

It seems that NullInputStream is created by AwsProxyHttpServletRequest when the body is null, but then this input stream is read here: https://github.com/awslabs/aws-serverless-java-container/blob/master/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequest.java#L895 without checking that the stream is available.

We use apache commons-io 2.6 (latest)

@sapessi
Copy link
Collaborator

sapessi commented May 6, 2019

Thanks @eduramiba. Let me reopen this. I'll look into it over the next couple of days.

@sapessi sapessi reopened this May 6, 2019
@sapessi sapessi added the bug label May 6, 2019
@sapessi sapessi added this to the Release 1.3.2 milestone Jun 26, 2019
@sapessi
Copy link
Collaborator

sapessi commented Jun 26, 2019

I still can't seem to replicate the issue you are seeing. This unit test succeeds. Regardless, seems like a sensible idea to put a null guard around that read operation from the input stream so I will make that change.

@sapessi
Copy link
Collaborator

sapessi commented Jun 27, 2019

Release 1.3.2 - which includes this fix - is on its way to maven central! Resolving this issue.

@sapessi sapessi closed this as completed Jun 27, 2019
@eduramiba
Copy link

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants