vertx-web icon indicating copy to clipboard operation
vertx-web copied to clipboard

Body ignored in OpenAPI requests sent to Vert.x using vertx-web-openapi version 4.3

Open computate opened this issue 2 years ago • 3 comments

I found a bug that did not exist in 4.2., but was introduced in vertx-web-openapi version 4.3.. If I send a PATCH request to an API defined with openapi for example, the body is ignored and set to null which results in an error.

Version

Which version(s) did you encounter this bug ?

vertx-web-openapi version 4.3

Context

I encountered an exception which looks suspicious while ...

Sending a REST request to an API defined with openapi and RouterBuilder.

Do you have a reproducer?

I have a reproducer in GitHub here: https://github.com/computate/vertx-bug

And a reproducer container image in quay.io: quay.io/computate/vertx-bug:latest

Steps to reproduce

You can run the Vert.x openapi spec with podman or docker like this:

podman run -p 12080:12080 quay.io/computate/vertx-bug:latest

Then you can send a PATCH request in a separate terminal to see the error like this:

curl -X PATCH -H 'Content-Type: application/json' -d '{"do": "stuff"}' http://localhost
:12080/

This results in the following error:

[vert.x-eventloop-thread-0] INFO org.computate.vertx.bug.verticle.MainVerticle - Configure OpenAPI succeeded
[vert.x-eventloop-thread-1] INFO org.computate.vertx.bug.verticle.MainVerticle - Configure OpenAPI succeeded
[vert.x-eventloop-thread-0] INFO org.computate.vertx.bug.verticle.MainVerticle - Configure API completed
[vert.x-eventloop-thread-1] INFO org.computate.vertx.bug.verticle.MainVerticle - Configure API completed
[vert.x-eventloop-thread-0] INFO org.computate.vertx.bug.verticle.MainVerticle - Start server
[vert.x-eventloop-thread-1] INFO org.computate.vertx.bug.verticle.MainVerticle - Start server
[vert.x-eventloop-thread-1] INFO org.computate.vertx.bug.verticle.MainVerticle - Start server succeeded
[vert.x-eventloop-thread-0] INFO org.computate.vertx.bug.verticle.MainVerticle - Start server succeeded
[vert.x-eventloop-thread-4] INFO org.computate.vertx.bug.verticle.MainVerticle - Started main verticle.  
[vert.x-eventloop-thread-1] ERROR io.vertx.ext.web.RoutingContext - Unhandled exception in router
io.vertx.ext.web.validation.BodyProcessorException: [Bad Request] Body application/json parsing error: Null body
       at io.vertx.ext.web.validation.BodyProcessorException.createParsingError(BodyProcessorException.java:52)
       at io.vertx.ext.web.validation.impl.body.JsonBodyProcessorImpl.process(JsonBodyProcessorImpl.java:33)
       at io.vertx.ext.web.validation.impl.ValidationHandlerImpl.validateBody(ValidationHandlerImpl.java:247)
       at io.vertx.ext.web.validation.impl.ValidationHandlerImpl.handle(ValidationHandlerImpl.java:143)
       at io.vertx.ext.web.validation.impl.ValidationHandlerImpl.handle(ValidationHandlerImpl.java:18)
       at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)
       at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:140)
       at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
       at io.vertx.ext.web.handler.impl.ResponseContentTypeHandlerImpl.handle(ResponseContentTypeHandlerImpl.java:54)
       at io.vertx.ext.web.handler.impl.ResponseContentTypeHandlerImpl.handle(ResponseContentTypeHandlerImpl.java:28)
       at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)
       at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
       at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
       at io.vertx.ext.web.handler.impl.SessionHandlerImpl.handle(SessionHandlerImpl.java:321)
       at io.vertx.ext.web.handler.impl.SessionHandlerImpl.handle(SessionHandlerImpl.java:37)
       at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)
       at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
       at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
       at io.vertx.ext.web.impl.RouterImpl.handle(RouterImpl.java:68)
       at io.vertx.ext.web.impl.RouterImpl.handle(RouterImpl.java:37)
       at io.vertx.core.http.impl.Http1xServerRequestHandler.handle(Http1xServerRequestHandler.java:67)
       at io.vertx.core.http.impl.Http1xServerRequestHandler.handle(Http1xServerRequestHandler.java:30)
       at io.vertx.core.impl.EventLoopContext.emit(EventLoopContext.java:55)
       at io.vertx.core.impl.DuplicatedContext.emit(DuplicatedContext.java:158)
       at io.vertx.core.http.impl.Http1xServerConnection.handleMessage(Http1xServerConnection.java:145)
       at io.vertx.core.net.impl.ConnectionBase.read(ConnectionBase.java:157)
       at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:153)
       at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
       at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
       at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
       at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:93)
       at io.netty.handler.codec.http.websocketx.extensions.WebSocketServerExtensionHandler.channelRead(WebSocketServerExtensionHand
ler.java:99)
       at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
       at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
       at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
       at io.vertx.core.http.impl.Http1xUpgradeToH2CHandler.channelRead(Http1xUpgradeToH2CHandler.java:120)
       at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
       at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
       at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
       at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:346)
       at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:333)
       at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:454)
       at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:290)
       at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
       at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
       at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
       at io.vertx.core.http.impl.Http1xOrH2CHandler.end(Http1xOrH2CHandler.java:61)
       at io.vertx.core.http.impl.Http1xOrH2CHandler.channelRead(Http1xOrH2CHandler.java:38)
       at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
       at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
       at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
       at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
       at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
       at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
       at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
       at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
       at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788)
       at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724)
       at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650)
       at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562)
       at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
       at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
       at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
       at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: io.vertx.ext.web.validation.MalformedValueException: Null body
       at io.vertx.ext.web.validation.impl.body.JsonBodyProcessorImpl.process(JsonBodyProcessorImpl.java:34)
       ... 62 more

Extra

  • Anything that can be relevant such as OS version, JVM version

computate avatar Feb 17 '23 17:02 computate

Hey, I have the same problem with vertx 4.4.4 and POST requests. If I go back to vertx 4.3.0 the issue does not appear. If i bump the version to 4.3.1, the problem appears. So the changes in 4.3.1 might have introduces the issue.

JonasTaulien avatar Jul 28 '23 11:07 JonasTaulien

Hi @computate and @JonasTaulien,

I encountered the same issue while using Vert.x >= 3.4.1 with POST requests. It seems that the changes introduced in Vert.x 4.3.1 might have altered the behaviour, specifically affecting the configuration of the BodyHandler. This alteration resulted in the BodyHandler being added only when global handlers are not configured. In my scenario, where I was adding global handlers, this unintentionally prevented the BodyHandler from being properly configured.

As stated in the Vert.x documentation, configuring the BodyHandler is essential to gain access to request bodies. To address this situation, if we are using global handlers and still require access to the request body, it becomes critical to configure the BodyHandler as early as possible in the handler hierarchy. This can be achieved by incorporating the following line:

rootHandler(BodyHandler.create())

I hope this information helps shed light on the issue. If you have any further questions or insights, please feel free to share.

ines-soares avatar Aug 29 '23 16:08 ines-soares

Hey @ines-soares thanks for your notes and especially the link to the commit changes! It helped my do understand and fix the issue.

I am using the io.vertx.reactivex.ext,web.openapi.RouterBuilder and until 4.3.1 I never had to manually add a BodyHandler. That is also stated in the Vert.x OpenAPI documentation

But I do add some other global handlers via the rootHandler. The code changes you have provided explain, why I now have the problem that there is no BodyHandler (globalHandlers.isEmpty() check instead bodyHandler != null).

So after adding the BodyHandler manually with the rootHandler method I was able to upgrade to 4.4.5 successfully. I do not get the error anymore. 🎉🎉🎉

@vietj I would suggest to update the Vert.x OpenAPI documentation for the BodyHandler and for the rootHandler method so it states, that the BodyHandler has to be manually added, if at least one global handler was added by using the rootHandler method.

JonasTaulien avatar Aug 31 '23 08:08 JonasTaulien

Hi, there is a complete new rebuild of the OpenAPI Router [1], which hopefully solves the problem. As I understood the problem is already solved and only a documentation change is requested. So I think this is also solved, because we removed the documentation for this deprecated version.

I am closing this issue now, as the ticket refers to a version of OpenAPI Router that is no longer supported. If the bug still occurs with the new OpenAPI router version, I would be very happy if you would open a new issue.

[1] https://vertx.io/docs/vertx-web-openapi-router/java/

pk-work avatar Apr 26 '24 09:04 pk-work