vert-x3 / vertx-web Goto Github PK
View Code? Open in Web Editor NEWHTTP web applications for Vert.x
License: Apache License 2.0
HTTP web applications for Vert.x
License: Apache License 2.0
In snapshot vertx-apex-3.0.0-20150302.172251-43, an ErrorHandler with displayExceptionDetails=true will itself throw an exception when the orginal exception have a null getMessage().
SEVERE: Unexpected exception in route
java.lang.NullPointerException: reasonPhrase
at io.netty.handler.codec.http.HttpResponseStatus.<init>(HttpResponseStatus.java:478)
at io.netty.handler.codec.http.HttpResponseStatus.<init>(HttpResponseStatus.java:468)
at io.vertx.core.http.impl.HttpServerResponseImpl.setStatusMessage(HttpServerResponseImpl.java:122)
at io.vertx.ext.apex.handler.impl.ErrorHandlerImpl.handle(ErrorHandlerImpl.java:77)
at io.vertx.ext.apex.handler.impl.ErrorHandlerImpl.handle(ErrorHandlerImpl.java:35)
at io.vertx.ext.apex.impl.RouteImpl.handleFailure(RouteImpl.java:203)
at io.vertx.ext.apex.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:65)
at io.vertx.ext.apex.impl.RoutingContextImpl.next(RoutingContextImpl.java:98)
at io.vertx.ext.apex.impl.RoutingContextImpl.doFail(RoutingContextImpl.java:290)
at io.vertx.ext.apex.impl.RoutingContextImpl.fail(RoutingContextImpl.java:124)
at io.vertx.ext.apex.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:73)
at io.vertx.ext.apex.impl.RoutingContextImpl.next(RoutingContextImpl.java:98)
at io.vertx.ext.apex.impl.RouterImpl.accept(RouterImpl.java:61)
at dk.innovasion.support.ApexTest$$Lambda$8/105099923.handle(Unknown Source)
at io.vertx.core.http.impl.ServerConnection.handleRequest(ServerConnection.java:262)
at io.vertx.core.http.impl.ServerConnection.processMessage(ServerConnection.java:364)
at io.vertx.core.http.impl.ServerConnection.handleMessage(ServerConnection.java:129)
at io.vertx.core.http.impl.HttpServerImpl$ServerHandler.doMessageReceived(HttpServerImpl.java:522)
at io.vertx.core.http.impl.HttpServerImpl$ServerHandler.doMessageReceived(HttpServerImpl.java:453)
at io.vertx.core.http.impl.VertxHttpHandler.lambda$channelRead$5(VertxHttpHandler.java:71)
at io.vertx.core.http.impl.VertxHttpHandler$$Lambda$14/1054055130.run(Unknown Source)
at io.vertx.core.impl.ContextImpl.lambda$wrapTask$3(ContextImpl.java:258)
at io.vertx.core.impl.ContextImpl$$Lambda$4/1237598030.run(Unknown Source)
at io.vertx.core.impl.ContextImpl.executeSync(ContextImpl.java:165)
at io.vertx.core.http.impl.VertxHttpHandler.channelRead(VertxHttpHandler.java:71)
at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:131)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:308)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:294)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:182)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:308)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:294)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:846)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:130)
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:116)
at java.lang.Thread.run(Thread.java:744)
A reproducer:
package dk.innovasion.support;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.ext.apex.Router;
import io.vertx.ext.apex.handler.ErrorHandler;
public class ApexTest extends AbstractVerticle {
@Override
public void start() throws Exception {
Router router = Router.router(vertx);
router.route().path("/ok").handler(ctx -> {
throw new NullPointerException("message");
});
router.route().path("/fail").handler(ctx -> {
throw new NullPointerException();
});
router.route().failureHandler(ErrorHandler.create(true));
HttpServerOptions options = new HttpServerOptions();
options.setPort(8080);
HttpServer server = vertx.createHttpServer(options);
server.requestHandler(router::accept).listen();
}
public static void main(String[] args) {
Vertx vertx = Vertx.vertx();
vertx.deployVerticle(ApexTest.class.getName(), res -> {
System.out.println(res.succeeded() + " " + res.cause());
});
}
}
Hitting localhost:8080/ok shows a nice stacktrace
Hitting localhost:8080/fail shows "Internal Server Error" and the cause exception stacktrace is lost.
Would be very handy if the StaticHandler could support range requests
and routes.
Currently we allow path matching to match part of a resource, e.g.
"foo*" matches "foobar" as well as "foo/bar"
Propose to simplify this so the path parameter should always be a resource, e.g.
foo/
foo/bar/
foo/bar/wibble.json
this would greatly simplify internal path handling
An Auth addon that can handle OAuth/CAS/SAML etc, most probably will delegate to pac4j for the actual logic of redirection etc.
Problem:
When a client uses vertxbus.js to bridge the eventbus to the browser and uses a path different from the root path, i.e. vertx.EventBus('http://localhost:8080/notarootpath'), then the first XHR request generated by vertxbus.js goes to "/notarootpath/info". This results in a 404 because the server-side method socketHandler(Handler sockHandler) in the class SockJSHandlerImpl registers this and some other handlers only for the root-path. For example:
router.get("/info").handler(BaseTransport.createInfoHandler(options));
Solution:
The registration of handlers within the method socketHandler() could be rewritten using the corresponding ..withRegex() method. For example: router.get("/info") -> router.getWithRegex(".*/info"). This should work as long as the server relies on the hope that the client only uses vertxbus.js for a bridged eventbus. A cleaner solution could be to pass the routing path into the method SockJSHandlerImpl.bridge:
@OverRide
public SockJSHandler bridge(BridgeOptions bridgeOptions) {
socketHandler(new EventBusBridgeImpl(vertx, bridgeOptions), bridgeOptions.getNonrootpath());
return this;
}
@OverRide
public SockJSHandler socketHandler(Handler sockHandler, String nonrootpath) {
router.get(nonrootpath+"/info").handler(BaseTransport.createInfoHandler(options));
...
}
This could be used as follows:
SockJSHandler sockJSHandler = SockJSHandler.create(vertx);
BridgeOptions bridgeOptions = new BridgeOptions();
bridgeOptions.setNonrootpath("/notarootpath");
sockJSHandler.bridge(bridgeOptions); // <-- Here we pass in the path inside the options
router.route("/notarootpath/*").handler(sockJSHandler);
Notice the asterisk * for the father sockJSHandler is also required.
Like this: pmlopes/yoke#139
Cross site forgery request tokens.
Port pmlopes/yoke#117
Current algorithm just URLDecodes and removes "..".
We can create an optimised implementation which URL decodes and removes "..", "." and extra "/" in a single iteration.
TemplateEngine engine = HandlebarsTemplateEngine.create(null, "tpl");
Handler<RoutingContext> templateHandler = PathTemplateHandler.templateHandler(engine, "templates", "text/html");
locates files into templates/path.tpl
TemplateEngine engine = HandlebarsTemplateEngine.create("", "tpl");
Handler<RoutingContext> templateHandler = PathTemplateHandler.templateHandler(engine, "templates", "text/html");
locates files into /templates/path.tpl
which fails.
From CachingTemplateEngine line 39 : we add a "/"
Maybe don't if prefix length is 0 ?
After about 100kb of worth of data is sent to the SockJS server over the Websocket transport, the connection is unexpectedly dropped.
Debugging shows that it happens in WebSocketTransport.java line 96:
//Invalid JSON - we close the connection
close();
This code path is entered because the message is truncated, which again is because in VertxHandler.java:139 a TextWebSocketFrame msg argument is passed which has .isFinalFragment() === false.
So handler can lookup session from a session store.
Implement basic auth handling as per Yoke.
Also should provide ability to plug-in more complex auth methods.
Using new docgen tool
Current StaticServer uses last-modified/if-modified-since to detect whether new content needs to be returned.
We should also consider supporting etags. Although I am not 100% sure what this would really buy us.
A route with a regex will match the whole request path and no only the segment that is mounted. I'm not sure whether this is intended behavior or not.
The matches() method is using the complete request path.
https://github.com/vert-x3/vertx-apex/blob/master/src/main/java/io/vertx/ext/apex/core/impl/RouteImpl.java#L207
It is possible to change the behavior by extracting the subpath from the request path.
This should be done when a mountPoint has been specified.
Example:
String path = request.path();
if (!StringUtils.isEmpty(mountPoint)) {
path = request.path().replace(mountPoint, "");
}
How to reproduce:
@Override
public void start() throws Exception {
Router rootRouter = Router.router(vertx);
Router apiRouter = Router.router(vertx);
rootRouter.mountSubRouter("/api, apiRouter);
apiRouter.routeWithRegex("\\/test").method(GET).handler(rc -> {
rc.response().end("test");
});
}
http://localhost:8080/api/test - won't match
Currently when using template handlers or static handler to load templates/serve files, the files that is looked for is relative to the path for the route on which the handler was set.
This makes sense in some cases, but in others the full path may be desirable. We should make this behaviour configurable by a switch.
In term of work to do, this probably means:
For the records, excerpt of a discussion on IRC:
aesteve: just one thing though
aesteve: the most interesting feature is "among the languages available, give me the best matching user locale"
aesteve: say you handle french, german in your context, and the user accept-language is : en_us, fr, it
aesteve: then getLocale should return "fr"
Allow different templating engines to be plugged into Apex
I have a simple reproducer that shows that the replyHandler of the client receives only 'undefined' when the server replies with message.fail():
https://github.com/leolux/vertx_replyfailinbridge
Why does the reply handler not receive the message that vertxbus.js actually received?
"a["{"address":"b24274d1-0d3c-4158-9dd9-cbe290e95956","failureCode":0,"failureType":"RECIPIENT_FAILURE","message":"send msg.fail() to the client"}"]"
Right now, sessions can only be stored on the server.
However, it would be nice to store sessions on the client (usually done using cookies).
Pros:
Cons:
What do you guys thing?
If you feel it's a good enhancement, I can take a stab at it
All the best,
Stéphane
On my laptop CookiesTest#testCookiesFields fails to parse the expired date because the date formatter produced in Utils.createISODateTimeFormatter() uses the default system locale (French here), instead of a fixed one (Locale.US, Locale.ENGLISH?)
-------------------------------------------------------------------------------
Test set: io.vertx.ext.apex.addons.test.CookiesTest
-------------------------------------------------------------------------------
Tests run: 5, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.098 sec <<< FAILURE! - in io.vertx.ext.apex.addons.test.CookiesTest
testCookieFields(io.vertx.ext.apex.addons.test.CookiesTest) Time elapsed: 0.032 sec <<< ERROR!
java.text.ParseException: Unparseable date: "Sat, 03 Jan 2015 02:17:02 GMT"
at java.text.DateFormat.parse(DateFormat.java:366)
at io.vertx.ext.apex.addons.test.CookiesTest.testCookieFields(CookiesTest.java:165)
mvn -v
yields :
Apache Maven 3.2.3 (33f8c3e1027c3ddde99d3cdebad2656a31e8fdf4; 2014-08-11T16:58:10-04:00)
Maven home: /usr/local/Cellar/maven/3.2.3/libexec
Java version: 1.8.0_25, vendor: Oracle Corporation
Java home: /Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/jre
Default locale: fr_FR, platform encoding: UTF-8
OS name: "mac os x", version: "10.10.1", arch: "x86_64", family: "mac"
Currently incomplete
Create some handlers to support OAuth (2)
Implement a nice ErrorHandler similarly to Yoke.
Allow Vert.x eventbusbridge to be configured as an Apex addon.
But Apex StaticServer doesn't currently support etags- this needs to be consistent.
We should be consistent about whether we use constructors to provide config to handlers or whether we should use the builder pattern.
NettyCookie which is being used to provide the cookie functionality, allows a setVersion method to be set on the cookie, to indicate if its http 1.1. Setting that allows the max-age header to be used rather than the deprecated 'expires' header. Therefore a setVersion method should be added to Cookie + CookieImpl
If you're using CookieHandler, it reads all the cookies at the start of the request, and adds them back at the end. However, there's a problem.
In Chrome, the request headers only contain the cookie's name and value, but not the expires / max-age Therefore when its read back by CookieHandlerImpl at the start, the decoded cookie doesn't contain an expires / maxAge.
At the end of the request when the cookie is added back, this time it doesn't have an expiry / max age set. That causes the browser to delete the initial expiry date, and use 'end of session' as the expiry date.
In my case, I was setting a cookie to not expire for 2 years, to keep the user logged in. However the cookie kept being reset to 'expire at end of session' and the user kept being logged out on closing browser window.
A simple workaround is to specify whether the CookieHandler should be read only, or read and write both. Or, it can be split into a CookieReadHandler and CookieWriteHandler
Either that, or it should note when context.addCookie is called, and unless its called, it doesn't add the cookies back and leaves them as they are.
I'd be happy to send a PR for these changes if they're acceptable.
Need to test all the cookie methods
Allow compression to be configured in Apex using an addon.
We should take accept quality settings into account when choosing a content type to use for directory listing.
https://github.com/pmlopes/yoke/blob/develop/framework/src/main/java/com/jetdrone/vertx/yoke/middleware/YokeRequest.java#L46
https://github.com/pmlopes/yoke/blob/develop/framework/src/main/java/com/jetdrone/vertx/yoke/middleware/YokeRequest.java#L463
You could have a weird accepts like:
text/html q=0.9; application/json
in this case you should reply json if you can since it has higher quality (100%)
It seems that the route handler is not very explicit in terms of routes. E.g if I specify a handler for / , then all my requests, even for /foo/bar are sent to that handler. /admin means /admin/foo/bar is sent to /admin, etc.
This can lead to gotchas where if you didn't create the handlers in the correct order (leaving / at the bottom), then the requests would go to a different handler than what you intended.
I think we should follow the conventions of servlets in this, in that the urls have to be specified exactly as you intend them. And to write a catch-all, you could use wildcards, e.g /* rather than just /. That way its much clearer that you intended it to be a catch all. And if a route isn't specified, then show a generic 404 error page.
I'd also be okay with wildcards like :int , :long, etc.
Allow cookies to lookup a session for the user and retrieve a session object.
It should be possible to store sessions in Vert.x 3.0 clustered shared data, redis and possibly other stores.
Right now the auth service handles login sessions, and the SessionHandler/SessionStore handles its own sessions.
It would be nice to somehow unifying them so we don't have to deal with two timeouts.
Implement a virtual host addon similarly to vhost in Yoke.
an attempt to use directory listing through StaticServer.staticServer().setDirectoryListing( true )
throws
ContextImpl Unhandled exception java.lang.NullPointerException
at java.lang.String.replace(String.java:2227)
at io.vertx.ext.apex.addons.impl.StaticServerImpl.lambda$sendDirectory$20(StaticServerImpl.java:433)...
the reason for such behavior is a lack of "title" value in routing context's data map:
directoryTemplate.replace("{title}", context.get("title")).replace("{directory}", normalizedDir)
it's not clear either how to set the title or why is it mandatory at all.
As reported at https://groups.google.com/forum/#!topic/vertx/X_n6e8PIoEo Apex does not properly handle small files in multipart uploads - while large buffers (> 8000 bytes) seem to be handled properly, smaller ones just lead to a timeout or other failure.
This is demonstrated by the project at https://github.com/millross/apex-small-file-failure which basically rips out a test from BodyHandlerTest within the Apex repository, and modifies it so that two near-identical tests can be run, only differing in the file size uploaded. The one with the small buffer fails, the one with the larger buffer succeeds.
Please review the reproducer and provide feedback if the code is wrong, or look into the problem. If I diagnose the problem I'll attach details to this issue.
Hey,
Here is a router hierarchy example:
Router apiRouter = Router.router(vertx);
apiRouter.route(HttpMethod.GET, "/product/:pid/items/:item1")
apiRouter.route(HttpMethod.GET, "/product/:pid/items")
apiRouter.route(HttpMethod.GET, "/product/:pid")
apiRouter.route(HttpMethod.GET, "/product")
startsWith
,With current matching algorithm, how is it possible to disallow a query such /api/product/4/f/2/3/1
Thanks
static files are allocated using vertx-core's FileResolver which in its turn uses java's Path.resolve().
on windows, WindowsPath.resolve() implementation returns a path with slashes (vs backslashes on linux). while used by URL class, slashes become urlencoded and are turning into %5C. as a result of it in FileResolver line 111 a NoSuchFileException is thrown in attempt to allocate a file with a path like webroot%5Cweb%5Cindex.html
if you have a route /users/:id you can add a param('id', regex) or param('id', handler) and it will be called before the handler
The implementation of
TemplateEngine.render(RoutingContext context, String templateFileName, Handler<AsyncResult<Buffer>> handler)
is a blocking operation in some (all?) engine implementations (Even though the signature implies that the result is returned asynchronously)
In some cases, it's OK that render() blocks, because it blocks only for a few milliseconds. But in others cases, calling render() may block for a longer period of time (10-20ms), or even more, depending on the engine implementation and the complexity of the template.
This means that the event loop could be blocked, stuck waiting for the template to finish its job
Ideally, a developper should be able to specify if a template engine should render in a non blocking way.
Weǘe got various options but we could for instance add a flag to the create method of the class MVELTemplateEngine:
static MVELTemplateEngine create(boolean async) { return new MVELTemplateEngineImpl(); }
Or we could create another method that explicitly tells that itś non blocking:
static MVELTemplateEngine createAsync() { return new MVELTemplateEngineImpl(); }
When a route has been configured to consume some specific content types but no Content-Type header is sent, an exception is thrown.
IMHO the route should not match the request unless consumes is set to accept /
Template handlers should be configurable more simply and allow a directory to be specified.
At SockJSHandlerImpl line 92 the EventBusBridgeHook does not get installed into the EventBusBridgeImpl, so the hook within SockJSHandlerImpl is useless.
Solution: Either call the method EventBusBrodgeImpl.setHook(this.hook) or create another constructor for the EventBusBridgeImpl and pass it to the constructor when creating the instance.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.