Handling multi-part forms from a blocking handler?

349 views
Skip to first unread message

Deven Phillips

unread,
Aug 25, 2016, 2:13:12 PM8/25/16
to vert.x
I have a route using vertx-web which does:

router.get("/some/path").blockingHandler(this::method, false);

And in the method I would like to do:

ctx.request().setExpectMultipart(true).endHandler(v -> anotherMethod(ctx));

But it fails every time, and even worse it is completely silent.

Any suggestions?

Thanks!

Deven

Deven Phillips

unread,
Aug 25, 2016, 3:02:53 PM8/25/16
to vert.x
Switching back to using executeBlocking and futures definitely works, but I would prefer to reduce the amount of complexity involved if this is possible...

I will try to put together an example program and put it up on GitHub later tonight.

Deven

Deven Phillips

unread,
Aug 25, 2016, 3:45:35 PM8/25/16
to vert.x
And here is an example project which demonstrates the problem: 


Deven

Julien Viet

unread,
Aug 26, 2016, 3:24:40 AM8/26/16
to ve...@googlegroups.com
you need to set an exceptionHandler on the request / response to get aware of failures.

--
You received this message because you are subscribed to the Google Groups "vert.x" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vertx+un...@googlegroups.com.
Visit this group at https://groups.google.com/group/vertx.
To view this discussion on the web, visit https://groups.google.com/d/msgid/vertx/e3f01831-ddc0-4e0f-988e-237c3566a8be%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Deven Phillips

unread,
Aug 26, 2016, 7:31:02 AM8/26/16
to vert.x
I tried this in a number of different combinations, but it still doesn't solve the problem. Whenever I use blockingHandler in conjunction with setExpectMultipart(true), I get unreliable results. In my example project I at least get some output:

Connected to the target VM, address: '127.0.0.1:45505', transport: 'socket'
Aug 26, 2016 7:11:40 AM io.vertx.ext.web.impl.RoutingContextImplBase
SEVERE
: Unexpected exception in route
java
.lang.IllegalStateException: Request has already been read
 at io
.vertx.core.http.impl.HttpServerRequestImpl.checkEnded(HttpServerRequestImpl.java:426)
 at io
.vertx.core.http.impl.HttpServerRequestImpl.setExpectMultipart(HttpServerRequestImpl.java:312)
 at io
.vertx.ext.web.impl.HttpServerRequestWrapper.setExpectMultipart(HttpServerRequestWrapper.java:192)
 at us
.juggl.vertx.blocking.ExampleVerticle.blockingHandler(ExampleVerticle.java:77)
 at io
.vertx.ext.web.impl.BlockingHandlerDecorator.lambda$handle$0(BlockingHandlerDecorator.java:48)
 at io
.vertx.core.impl.ContextImpl.lambda$executeBlocking$2(ContextImpl.java:303)
 at io
.vertx.core.impl.OrderedExecutorFactory$OrderedExecutor.lambda$new$0(OrderedExecutorFactory.java:94)
 at java
.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
 at java
.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
 at java
.lang.Thread.run(Thread.java:745)

This exception is thrown on the line where I call setExpectMultipart(true), the very first thing I do as part of the blockingHandler. As you can see from this output, it's very intermittent:


07:28:58-dphillips@ideapad-~/Videos$ curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' -d 'email=john.doe%40comecompany.com' http://localhost:1080/blocking && echo
Internal Server Error
07:28:58-dphillips@ideapad-~/Videos$ curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' -d 'email=john.doe%40comecompany.com' http://localhost:1080/blocking && echo
Internal Server Error
07:28:59-dphillips@ideapad-~/Videos$ curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' -d 'email=john.doe%40comecompany.com' http://localhost:1080/blocking && echo
{
 
"status" : "OK",
 
"type" : "blocking",
 
"email" : "john...@comecompany.com"
}
07:29:03-dphillips@ideapad-~/Videos$ curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' -d 'email=john.doe%40comecompany.com' http://localhost:1080/blocking && echo
Internal Server Error
07:29:04-dphillips@ideapad-~/Videos$ curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' -d 'email=john.doe%40comecompany.com' http://localhost:1080/blocking && echo
Internal Server Error
07:29:05-dphillips@ideapad-~/Videos$ curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' -d 'email=john.doe%40comecompany.com' http://localhost:1080/blocking && echo
{
 
"status" : "OK",
 
"type" : "blocking",
 
"email" : "john...@comecompany.com"
}
07:29:09-dphillips@ideapad-~/Videos$ curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' -d 'email=john.doe%40comecompany.com' http://localhost:1080/blocking && echo
Internal Server Error
07:29:10-dphillips@ideapad-~/Videos$ curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' -d 'email=john.doe%40comecompany.com' http://localhost:1080/blocking && echo
Internal Server Error

Deven Phillips

unread,
Aug 26, 2016, 7:34:43 AM8/26/16
to vert.x
Additionally, even when I set my exception handlers BEFORE setExpectMultipart(true), the errors are never handled by my error handlers.

Deven

Deven Phillips

unread,
Aug 26, 2016, 7:44:39 AM8/26/16
to vert.x
I updated the example code with a few more variations I have tried which still do not work.


Deven

Deven Phillips

unread,
Aug 29, 2016, 4:34:03 PM8/29/16
to vert.x
Julien/Tim,

    Any thoughts? Bump?!

Thanks in advance!

Deven

Paulo Lopes

unread,
Aug 30, 2016, 3:49:16 AM8/30/16
to vert.x
Hi Deven,

Some comments here, the setExpectMultipart(true) must be called on the first handler and should not be called as part of an async handler. The reason is that we need to inform netty that while the request has been received and not totally processed yet, we will setup and body handler to process the form + any other data in the request. If we let that initial handler complete then netty assumes we are ignoring the body and you get those messages "body has been read".

So either you want a blocking or not blocking handler you should call setExpectMultipart outside the handler you're implementing.

Second note is that even though you call the expect multipart you're not processing the form completly (no file uploads are handled for example).

Finally in your logs sometimes the code works some times it doesn't, I believe that is because the handlers are being executed in different order and sometimes it manages to set the endhandler before netty executes the check if body should be processed or skipped (expect multipart).

Deven Phillips

unread,
Aug 30, 2016, 7:11:37 AM8/30/16
to ve...@googlegroups.com
Paulo,

    Thanks for the feedback... It wasn't quite clear, but your response got me to the answer I needed... For future Vert.x users, I will make it abundantly clear here what had to happen to make this work.

BEFORE the call to blockingHandler, you need to have a regular "handler" which will call "setExpectMultipart" for the appropriate routes:

@Override
public void start() throws Exception {
Router router = Router.router(vertx);
router.post("/blocking*").handler(this::setExpectMultipart);
router.post("/nonblocking*").handler(this::nonBlockingHandler);
router.post("/blockingA").blockingHandler(this::blockingHandlerA, false);
router.post("/blockingB").blockingHandler(this::blockingHandlerB, false);
router.post("/blockingC").blockingHandler(this::blockingHandlerC, false);
router.post("/blockingPA").blockingHandler(this::blockingHandlerA);
router.post("/blockingPB").blockingHandler(this::blockingHandlerB);
router.post("/blockingPC").blockingHandler(this::blockingHandlerC);

vertx.createHttpServer().requestHandler(router::accept).listen(1080);
}

void setExpectMultipart(RoutingContext ctx) {
ctx.request().setExpectMultipart(true);
ctx.request().exceptionHandler(this::execptionHandler);
ctx.response().exceptionHandler(this::execptionHandler);
ctx.next();
}

This will let Vert.x know to process the form data. I have also updated my GitHub project with the appropriate changes and more comments/documentation.


Thank you all!!!

Deven Phillips

--
You received this message because you are subscribed to a topic in the Google Groups "vert.x" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/vertx/cbWEsWUW86k/unsubscribe.
To unsubscribe from this group and all its topics, send an email to vertx+unsubscribe@googlegroups.com.

Tim Fox

unread,
Sep 1, 2016, 4:59:47 AM9/1/16
to vert.x
I think the docs are pretty clear on this:

"If you want to retrieve the attributes of a multi-part form you should tell Vert.x that you expect to receive such a form before any of the body is read by calling setExpectMultipart with true"


Before any part of the body has been read means in the request handler. If you actually do this some time later in a blocking handler (or a timer or anything else that's not in the request handler) then it's not going to work

Deven Phillips

unread,
Sep 3, 2016, 2:14:19 AM9/3/16
to ve...@googlegroups.com
Tim,

    While I agree that this is how the documentation reads, it was certainly not clear to me that I needed an async handler to call setExpectMultipart BEFORE creating the blockingHandler... Could I suggest a note be added to the docs for blockingHandler?

Thanks in advance!

Deven

Thomas SEGISMONT

unread,
Sep 5, 2016, 3:49:21 AM9/5/16
to ve...@googlegroups.com
Hi,

Feel free to rephrase that part and submit a pull request. Documentation is open sourced too!

Thanks,
Thomas

--
You received this message because you are subscribed to the Google Groups "vert.x" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vertx+unsubscribe@googlegroups.com.

Deven Phillips

unread,
Sep 5, 2016, 1:10:21 PM9/5/16
to vert.x
Reply all
Reply to author
Forward
0 new messages