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

fail() in RoutingContextImpl constructor causes "Unhandled exception in router" even if error was handled #2459

Open
Darker opened this issue Aug 1, 2023 · 0 comments
Labels

Comments

@Darker
Copy link

Darker commented Aug 1, 2023

Version

Discovered in 4.3.8, but I can see it in master too.

Context

When a request path is empty or does not start with /, RoutingContext fails in its constructor:

    if (path == null || path.length() == 0) {
      // HTTP paths must start with a '/'
      fail(400);
    } else if (path.charAt(0) != '/') {
      // For compatiblity we return `Not Found` when a path does not start with `/`
      fail(404);
    }

This will trigger failure handler assigned to this route immediately. However this constructor is called in RouterImpl like this:

new RoutingContextImpl(null, this, request, state.getRoutes()).next();

What this means that the order of actions is such:

  1. Routing context constructor
    1. Call fail() and failed() now returns true
    2. doFail() causes iterateNext
    3. Error handler is found
    4. Error handler is executed
  2. Constructor was completed
  3. Back in RouterImpl, next() is called on constructed RoutingContextImpl
  4. Because routing context iterated already, it cannot find the error handler
  5. "Unhandled exception in router" appears in the log

Do you have a reproducer?

Try this:

final Router router = Router.router(vertx);

router.get("/*")
        .handler(routingContext -> {
            routingContext.response().setStatusCode(200);
            routingContext.end("Hello world!");
        })
        .failureHandler(routingContext->{
            int statusCode = routingContext.statusCode();
            final var failure = routingContext.failure();
            final var request = routingContext.request();
            if(failure != null) {
                if(failure instanceof HttpException) {
                    statusCode = ((HttpException)failure).getStatusCode();
                    LOGGER.info("Handling HttpException at path {} code {}", request.path(), statusCode);
                }
                else {
                    statusCode = 500;
                    LOGGER.warn("Handling unexpected exception at path {}", request.path(), statusCode);
                }
            }
            else if(statusCode == -1) {
                statusCode = 500;
                LOGGER.warn("status code was -1 at path {}", request.path(), statusCode);
            }
            else {
                LOGGER.info("Handling error code without exception at path {} code {}", request.path(), statusCode);
            }
            try {
                routingContext.end("error message here")
                        .onFailure((e)->{
                            LOGGER.error("cannot send error for path {}", request.path());
                            e.printStackTrace();
                        })
                        .onSuccess((unused)->{
                            LOGGER.info("sent error for path {}", request.path());
                        });
            }
            catch(Exception justSoYouKnowItDoesNotThrow) {
                LOGGER.error("cannot call end()", justSoYouKnowItDoesNotThrow);
            }
        });
return vertx
    .createHttpServer(new HttpServerOptions().setHandle100ContinueAutomatically(true).setTcpKeepAlive(true))
    .requestHandler(router)
    .listen(port, host)
    .onSuccess(res -> LOGGER.info("Listening on {}:{}", host, port))
    .onFailure(
        cause
        -> LOGGER.warn("Cannot listen on {}:{} error: {}", host, port, cause.toString()));

I can see this in my log if I make a request:

INFO  HttpServer Handling error code without exception at path blablabla code 404
ERROR RoutingContext Unhandled exception in router
INFO  HttpServer sent error for path blablabla

To make the request, I just used raw python socket and sent this:

    tcp_conn = socket.create_connection(("127.0.0.1", 8080))
    request_str = (f"GET blablabla HTTP/1.1\r\n"
                + f"Host: 127.0.0.1\r\n"
                + f"Content-Length: 0\r\n"
                + "Connection: close\r\n"
                + "\r\n")
    request_data = request_str.encode()
    tcp_conn.send(request_data)
@Darker Darker added the bug label Aug 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

No branches or pull requests

1 participant