Search in sources :

Example 1 with DuplicateRouteException

use of io.micronaut.web.router.exceptions.DuplicateRouteException in project micronaut-core by micronaut-projects.

the class RoutingInBoundHandler method channelRead0.

@Override
protected void channelRead0(ChannelHandlerContext ctx, io.micronaut.http.HttpRequest<?> request) {
    ctx.channel().config().setAutoRead(false);
    io.micronaut.http.HttpMethod httpMethod = request.getMethod();
    String requestPath = request.getUri().getPath();
    ServerRequestContext.set(request);
    if (LOG.isDebugEnabled()) {
        LOG.debug("Request {} {}", httpMethod, request.getUri());
    }
    NettyHttpRequest nettyHttpRequest = (NettyHttpRequest) request;
    io.netty.handler.codec.http.HttpRequest nativeRequest = nettyHttpRequest.getNativeRequest();
    // handle decoding failure
    DecoderResult decoderResult = nativeRequest.decoderResult();
    if (decoderResult.isFailure()) {
        Throwable cause = decoderResult.cause();
        HttpStatus status = cause instanceof TooLongFrameException ? HttpStatus.REQUEST_ENTITY_TOO_LARGE : HttpStatus.BAD_REQUEST;
        handleStatusError(ctx, nettyHttpRequest, HttpResponse.status(status), status.getReason());
        return;
    }
    MediaType contentType = request.getContentType().orElse(null);
    final String requestMethodName = request.getMethodName();
    if (!multipartEnabled && contentType != null && contentType.equals(MediaType.MULTIPART_FORM_DATA_TYPE)) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Multipart uploads have been disabled via configuration. Rejected request for URI {}, method {}, and content type {}", request.getUri(), requestMethodName, contentType);
        }
        handleStatusError(ctx, nettyHttpRequest, HttpResponse.status(HttpStatus.UNSUPPORTED_MEDIA_TYPE), "Content Type [" + contentType + "] not allowed");
        return;
    }
    UriRouteMatch<Object, Object> routeMatch = null;
    List<UriRouteMatch<Object, Object>> uriRoutes = router.findAllClosest(request);
    if (uriRoutes.size() > 1) {
        throw new DuplicateRouteException(requestPath, uriRoutes);
    } else if (uriRoutes.size() == 1) {
        UriRouteMatch<Object, Object> establishedRoute = uriRoutes.get(0);
        request.setAttribute(HttpAttributes.ROUTE, establishedRoute.getRoute());
        request.setAttribute(HttpAttributes.ROUTE_MATCH, establishedRoute);
        request.setAttribute(HttpAttributes.ROUTE_INFO, establishedRoute);
        request.setAttribute(HttpAttributes.URI_TEMPLATE, establishedRoute.getRoute().getUriMatchTemplate().toString());
        routeMatch = establishedRoute;
    }
    RouteMatch<?> route;
    if (routeMatch == null) {
        // Check if there is a file for the route before returning route not found
        Optional<? extends FileCustomizableResponseType> optionalFile = matchFile(requestPath);
        if (optionalFile.isPresent()) {
            filterAndEncodeResponse(ctx, nettyHttpRequest, Flux.just(HttpResponse.ok(optionalFile.get())));
            return;
        }
        if (LOG.isDebugEnabled()) {
            LOG.debug("No matching route: {} {}", httpMethod, request.getUri());
        }
        // if there is no route present try to locate a route that matches a different HTTP method
        final List<UriRouteMatch<?, ?>> anyMatchingRoutes = router.findAny(request.getUri().toString(), request).collect(Collectors.toList());
        final Collection<MediaType> acceptedTypes = request.accept();
        final boolean hasAcceptHeader = CollectionUtils.isNotEmpty(acceptedTypes);
        Set<MediaType> acceptableContentTypes = contentType != null ? new HashSet<>(5) : null;
        Set<String> allowedMethods = new HashSet<>(5);
        Set<MediaType> produceableContentTypes = hasAcceptHeader ? new HashSet<>(5) : null;
        for (UriRouteMatch<?, ?> anyRoute : anyMatchingRoutes) {
            final String routeMethod = anyRoute.getRoute().getHttpMethodName();
            if (!requestMethodName.equals(routeMethod)) {
                allowedMethods.add(routeMethod);
            }
            if (contentType != null && !anyRoute.doesConsume(contentType)) {
                acceptableContentTypes.addAll(anyRoute.getRoute().getConsumes());
            }
            if (hasAcceptHeader && !anyRoute.doesProduce(acceptedTypes)) {
                produceableContentTypes.addAll(anyRoute.getRoute().getProduces());
            }
        }
        if (CollectionUtils.isNotEmpty(acceptableContentTypes)) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Content type not allowed for URI {}, method {}, and content type {}", request.getUri(), requestMethodName, contentType);
            }
            handleStatusError(ctx, nettyHttpRequest, HttpResponse.status(HttpStatus.UNSUPPORTED_MEDIA_TYPE), "Content Type [" + contentType + "] not allowed. Allowed types: " + acceptableContentTypes);
            return;
        }
        if (CollectionUtils.isNotEmpty(produceableContentTypes)) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Content type not allowed for URI {}, method {}, and content type {}", request.getUri(), requestMethodName, contentType);
            }
            handleStatusError(ctx, nettyHttpRequest, HttpResponse.status(HttpStatus.NOT_ACCEPTABLE), "Specified Accept Types " + acceptedTypes + " not supported. Supported types: " + produceableContentTypes);
            return;
        }
        if (!allowedMethods.isEmpty()) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Method not allowed for URI {} and method {}", request.getUri(), requestMethodName);
            }
            handleStatusError(ctx, nettyHttpRequest, HttpResponse.notAllowedGeneric(allowedMethods), "Method [" + requestMethodName + "] not allowed for URI [" + request.getUri() + "]. Allowed methods: " + allowedMethods);
            return;
        } else {
            handleStatusError(ctx, nettyHttpRequest, HttpResponse.status(HttpStatus.NOT_FOUND), "Page Not Found");
        }
        return;
    } else {
        route = routeMatch;
    }
    if (LOG.isTraceEnabled()) {
        if (route instanceof MethodBasedRouteMatch) {
            LOG.trace("Matched route {} - {} to controller {}", requestMethodName, requestPath, route.getDeclaringType());
        } else {
            LOG.trace("Matched route {} - {}", requestMethodName, requestPath);
        }
    }
    // all ok proceed to try and execute the route
    if (route.isWebSocketRoute()) {
        handleStatusError(ctx, nettyHttpRequest, HttpResponse.status(HttpStatus.BAD_REQUEST), "Not a WebSocket request");
    } else {
        handleRouteMatch(route, nettyHttpRequest, ctx);
    }
}
Also used : TooLongFrameException(io.netty.handler.codec.TooLongFrameException) MethodBasedRouteMatch(io.micronaut.web.router.MethodBasedRouteMatch) DecoderResult(io.netty.handler.codec.DecoderResult) MediaType(io.micronaut.http.MediaType) HashSet(java.util.HashSet) HttpMethod(io.micronaut.http.HttpMethod) HttpStatus(io.micronaut.http.HttpStatus) DuplicateRouteException(io.micronaut.web.router.exceptions.DuplicateRouteException) UriRouteMatch(io.micronaut.web.router.UriRouteMatch) AbstractNettyHttpRequest(io.micronaut.http.netty.AbstractNettyHttpRequest)

Aggregations

HttpMethod (io.micronaut.http.HttpMethod)1 HttpStatus (io.micronaut.http.HttpStatus)1 MediaType (io.micronaut.http.MediaType)1 AbstractNettyHttpRequest (io.micronaut.http.netty.AbstractNettyHttpRequest)1 MethodBasedRouteMatch (io.micronaut.web.router.MethodBasedRouteMatch)1 UriRouteMatch (io.micronaut.web.router.UriRouteMatch)1 DuplicateRouteException (io.micronaut.web.router.exceptions.DuplicateRouteException)1 DecoderResult (io.netty.handler.codec.DecoderResult)1 TooLongFrameException (io.netty.handler.codec.TooLongFrameException)1 HashSet (java.util.HashSet)1