Running a single-page direct application using the java vert.x web server

I use the Vert.x web server to serve the React application as static content. I want this to be served from the / path, then in the React application it has its own routing using react-router , which should decide which page to show.

So far I have the following:

 Vertx vertx = Vertx.vertx(); HttpServer server = vertx.createHttpServer(); Router router = Router.router(vertx); router.route().handler(BodyHandler.create()); router.route(HttpMethod.POST, "/rest/foo").handler(new FooHandler()); router.route(HttpMethod.GET, "/*").handler(StaticHandler.create()).failureHandler(event -> { // This serves up the React app event.response().sendFile("webroot/index.html").end(); }); server.requestHandler(router::accept).listen(12001); 

This works as expected if I start with the localhost:12001 request, and it also handles path changes correctly from now on. However, if I try to update one of the pages that has a path processed by react router , then I get a bunch of errors generated in the server logs (the page does load correctly, though).

Does anyone know what the problem is and how to fix it?

 SEVERE: Unexpected exception in route java.lang.IllegalStateException: Response has already been written at io.vertx.core.http.impl.HttpServerResponseImpl.checkWritten(HttpServerResponseImpl.java:561) at io.vertx.core.http.impl.HttpServerResponseImpl.end0(HttpServerResponseImpl.java:389) at io.vertx.core.http.impl.HttpServerResponseImpl.end(HttpServerResponseImpl.java:328) at co.uk.foo.webserver.server.WebServer.lambda$initialiseRoutes$0(WebServer.java:67) at co.uk.foo.webserver.server.WebServer$$Lambda$4/1197365356.handle(Unknown Source) at io.vertx.ext.web.impl.RouteImpl.handleFailure(RouteImpl.java:227) at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:76) at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:94) at io.vertx.ext.web.impl.RoutingContextImpl.doFail(RoutingContextImpl.java:355) at io.vertx.ext.web.impl.RoutingContextImpl.fail(RoutingContextImpl.java:119) at io.vertx.ext.web.handler.impl.StaticHandlerImpl.lambda$sendStatic$2(StaticHandlerImpl.java:198) at io.vertx.ext.web.handler.impl.StaticHandlerImpl$$Lambda$17/1050258443.handle(Unknown Source) at io.vertx.ext.web.handler.impl.StaticHandlerImpl.wrapInTCCLSwitch(StaticHandlerImpl.java:245) at io.vertx.ext.web.handler.impl.StaticHandlerImpl.getFileProps(StaticHandlerImpl.java:264) at io.vertx.ext.web.handler.impl.StaticHandlerImpl.sendStatic(StaticHandlerImpl.java:184) at io.vertx.ext.web.handler.impl.StaticHandlerImpl.handle(StaticHandlerImpl.java:141) at io.vertx.ext.web.handler.impl.StaticHandlerImpl.handle(StaticHandlerImpl.java:51) at io.vertx.ext.web.impl.RouteImpl.handleContext(RouteImpl.java:221) at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:78) at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:94) at io.vertx.ext.web.handler.impl.BodyHandlerImpl$BHandler.doEnd(BodyHandlerImpl.java:155) at io.vertx.ext.web.handler.impl.BodyHandlerImpl$BHandler.end(BodyHandlerImpl.java:141) at io.vertx.ext.web.handler.impl.BodyHandlerImpl.lambda$handle$34(BodyHandlerImpl.java:61) at io.vertx.ext.web.handler.impl.BodyHandlerImpl$$Lambda$14/1403708668.handle(Unknown Source) at io.vertx.core.http.impl.HttpServerRequestImpl.handleEnd(HttpServerRequestImpl.java:411) at io.vertx.core.http.impl.ServerConnection.handleEnd(ServerConnection.java:286) at io.vertx.core.http.impl.ServerConnection.processMessage(ServerConnection.java:404) at io.vertx.core.http.impl.ServerConnection.handleMessage(ServerConnection.java:134) at io.vertx.core.http.impl.HttpServerImpl$ServerHandler.doMessageReceived(HttpServerImpl.java:515) at io.vertx.core.http.impl.HttpServerImpl$ServerHandler.doMessageReceived(HttpServerImpl.java:421) at io.vertx.core.http.impl.VertxHttpHandler.lambda$channelRead$20(VertxHttpHandler.java:80) at io.vertx.core.http.impl.VertxHttpHandler$$Lambda$16/1532360211.run(Unknown Source) at io.vertx.core.impl.ContextImpl.lambda$wrapTask$18(ContextImpl.java:333) at io.vertx.core.impl.ContextImpl$$Lambda$11/511598695.run(Unknown Source) at io.vertx.core.impl.ContextImpl.executeFromIO(ContextImpl.java:225) at io.vertx.core.http.impl.VertxHttpHandler.channelRead(VertxHttpHandler.java:80) at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:124) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:318) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:304) at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:276) at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:263) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:318) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:304) at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:846) at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:131) at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:511) at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:468) at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:382) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:354) at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:112) at java.lang.Thread.run(Thread.java:745) Jun 26, 2016 4:22:08 PM io.vertx.ext.web.impl.RoutingContextImplBase SEVERE: Unexpected exception in route java.lang.IllegalStateException: Head already written at io.vertx.core.http.impl.HttpServerResponseImpl.doSendFile(HttpServerResponseImpl.java:434) at io.vertx.core.http.impl.HttpServerResponseImpl.sendFile(HttpServerResponseImpl.java:334) at io.vertx.core.http.impl.HttpServerResponseImpl.sendFile(HttpServerResponseImpl.java:52) at io.vertx.core.http.HttpServerResponse.sendFile(HttpServerResponse.java:275) at io.vertx.core.http.HttpServerResponse.sendFile(HttpServerResponse.java:262) at co.uk.foo.webserver.server.WebServer.lambda$initialiseRoutes$0(WebServer.java:67) at co.uk.foo.webserver.server.WebServer$$Lambda$4/1197365356.handle(Unknown Source) at io.vertx.ext.web.impl.RouteImpl.handleFailure(RouteImpl.java:227) at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:76) at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:94) at io.vertx.ext.web.impl.RoutingContextImpl.doFail(RoutingContextImpl.java:355) at io.vertx.ext.web.impl.RoutingContextImpl.fail(RoutingContextImpl.java:119) at io.vertx.ext.web.handler.impl.StaticHandlerImpl.lambda$sendStatic$2(StaticHandlerImpl.java:189) at io.vertx.ext.web.handler.impl.StaticHandlerImpl$$Lambda$17/1050258443.handle(Unknown Source) at io.vertx.ext.web.handler.impl.StaticHandlerImpl.getFileProps(StaticHandlerImpl.java:284) at io.vertx.ext.web.handler.impl.StaticHandlerImpl.sendStatic(StaticHandlerImpl.java:184) at io.vertx.ext.web.handler.impl.StaticHandlerImpl.handle(StaticHandlerImpl.java:141) at io.vertx.ext.web.handler.impl.StaticHandlerImpl.handle(StaticHandlerImpl.java:51) at io.vertx.ext.web.impl.RouteImpl.handleContext(RouteImpl.java:221) at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:78) at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:94) at io.vertx.ext.web.handler.impl.BodyHandlerImpl$BHandler.doEnd(BodyHandlerImpl.java:155) at io.vertx.ext.web.handler.impl.BodyHandlerImpl$BHandler.end(BodyHandlerImpl.java:141) at io.vertx.ext.web.handler.impl.BodyHandlerImpl.lambda$handle$34(BodyHandlerImpl.java:61) at io.vertx.ext.web.handler.impl.BodyHandlerImpl$$Lambda$14/1403708668.handle(Unknown Source) at io.vertx.core.http.impl.HttpServerRequestImpl.handleEnd(HttpServerRequestImpl.java:411) at io.vertx.core.http.impl.ServerConnection.handleEnd(ServerConnection.java:286) at io.vertx.core.http.impl.ServerConnection.processMessage(ServerConnection.java:404) at io.vertx.core.http.impl.ServerConnection.handleMessage(ServerConnection.java:134) at io.vertx.core.http.impl.HttpServerImpl$ServerHandler.doMessageReceived(HttpServerImpl.java:515) at io.vertx.core.http.impl.HttpServerImpl$ServerHandler.doMessageReceived(HttpServerImpl.java:421) at io.vertx.core.http.impl.VertxHttpHandler.lambda$channelRead$20(VertxHttpHandler.java:80) at io.vertx.core.http.impl.VertxHttpHandler$$Lambda$16/1532360211.run(Unknown Source) at io.vertx.core.impl.ContextImpl.lambda$wrapTask$18(ContextImpl.java:333) at io.vertx.core.impl.ContextImpl$$Lambda$11/511598695.run(Unknown Source) at io.vertx.core.impl.ContextImpl.executeFromIO(ContextImpl.java:225) at io.vertx.core.http.impl.VertxHttpHandler.channelRead(VertxHttpHandler.java:80) at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:124) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:318) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:304) at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:276) at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:263) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:318) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:304) at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:846) at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:131) at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:511) at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:468) at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:382) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:354) at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:112) at java.lang.Thread.run(Thread.java:745) 
+5
source share
2 answers

I tried many different approaches to this problem and did not get anywhere. Instead, I decided to write my own handler , which behaves the way I want, and I don't have the errors shown in my original question.

 import io.vertx.core.Handler; import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class ReactAppHandler implements Handler<RoutingContext> { private static final Logger LOGGER = LogManager.getLogger(ReactAppHandler.class); private static final String WEB_ROOT_DIR = "webroot"; private static final String INDEX_HTML = "/index.html"; @Override public void handle(RoutingContext event) { HttpServerRequest request = event.request(); String path = event.normalisedPath(); LOGGER.info("Received a request for [" + path + "]."); String requestedFilepath = path; if ("/".equals(requestedFilepath)) { LOGGER.info("Requested file is root path. Remapping to return the index page."); requestedFilepath = INDEX_HTML; } final String fileToCheck = WEB_ROOT_DIR + requestedFilepath; LOGGER.info("Checking if file exists at [" + fileToCheck + "]."); event.vertx().fileSystem().exists(fileToCheck, fileExistsCheck -> { String fileToSend = WEB_ROOT_DIR + INDEX_HTML; if (fileExistsCheck.succeeded() && fileExistsCheck.result()) { LOGGER.info("File exists at path."); fileToSend = fileToCheck; } else { LOGGER.info("Could not find requested file, the index page will be returned instead."); } LOGGER.info("Returning file [" + fileToSend + "]."); request.response().sendFile(fileToSend); }); } } 
+1
source

When returning files, you should not call the end method, here:

 router.route(HttpMethod.GET, "/*").handler(StaticHandler.create()).failureHandler(event -> { // This serves up the React app event.response().sendFile("webroot/index.html").end(); }); 

The reason is that sending the file is an asynchronous call, which optimizes the process of answering the file for the answer, and when you end the conversation, you close the answer NOW !

What happens is that your file is quite small, so the OS can download it right away at a time (so your browser accepts it correctly anyway), but you are trying to close the connection when the OS has already done this for you.

What you need is:

 router.route(HttpMethod.GET, "/*").handler(StaticHandler.create()).failureHandler(event -> { // This serves up the React app event.response().sendFile("webroot/index.html"); }); 
+2
source

All Articles