Search in sources :

Example 1 with UriRouteMatch

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

the class NettyServerWebSocketUpgradeHandler method channelRead0.

@Override
protected final void channelRead0(ChannelHandlerContext ctx, NettyHttpRequest<?> msg) {
    ServerRequestContext.set(msg);
    Optional<UriRouteMatch<Object, Object>> optionalRoute = router.find(HttpMethod.GET, msg.getUri().toString(), msg).filter(rm -> rm.isAnnotationPresent(OnMessage.class) || rm.isAnnotationPresent(OnOpen.class)).findFirst();
    MutableHttpResponse<?> proceed = HttpResponse.ok();
    AtomicReference<HttpRequest<?>> requestReference = new AtomicReference<>(msg);
    Flux<MutableHttpResponse<?>> responsePublisher;
    if (optionalRoute.isPresent()) {
        UriRouteMatch<Object, Object> rm = optionalRoute.get();
        msg.setAttribute(HttpAttributes.ROUTE_MATCH, rm);
        msg.setAttribute(HttpAttributes.ROUTE_INFO, rm);
        proceed.setAttribute(HttpAttributes.ROUTE_MATCH, rm);
        proceed.setAttribute(HttpAttributes.ROUTE_INFO, rm);
        responsePublisher = Flux.just(proceed);
    } else {
        responsePublisher = routeExecutor.onError(new HttpStatusException(HttpStatus.NOT_FOUND, "WebSocket Not Found"), msg);
    }
    Publisher<? extends MutableHttpResponse<?>> finalPublisher = routeExecutor.filterPublisher(requestReference, responsePublisher);
    final Scheduler scheduler = Schedulers.fromExecutorService(ctx.channel().eventLoop());
    Mono.from(finalPublisher).publishOn(scheduler).subscribeOn(scheduler).contextWrite(reactorContext -> reactorContext.put(ServerRequestContext.KEY, requestReference.get())).subscribe((Consumer<MutableHttpResponse<?>>) actualResponse -> {
        if (actualResponse == proceed) {
            UriRouteMatch routeMatch = actualResponse.getAttribute(HttpAttributes.ROUTE_MATCH, UriRouteMatch.class).get();
            WebSocketBean<?> webSocketBean = webSocketBeanRegistry.getWebSocket(routeMatch.getTarget().getClass());
            handleHandshake(ctx, msg, webSocketBean, actualResponse);
            ChannelPipeline pipeline = ctx.pipeline();
            try {
                NettyServerWebSocketHandler webSocketHandler = new NettyServerWebSocketHandler(nettyEmbeddedServices, webSocketSessionRepository, handshaker, webSocketBean, msg, routeMatch, ctx, routeExecutor.getCoroutineHelper().orElse(null));
                pipeline.addBefore(ctx.name(), NettyServerWebSocketHandler.ID, webSocketHandler);
                pipeline.remove(ChannelPipelineCustomizer.HANDLER_HTTP_STREAM);
                pipeline.remove(NettyServerWebSocketUpgradeHandler.this);
                ChannelHandler accessLoggerHandler = pipeline.get(ChannelPipelineCustomizer.HANDLER_ACCESS_LOGGER);
                if (accessLoggerHandler != null) {
                    pipeline.remove(accessLoggerHandler);
                }
            } catch (Throwable e) {
                if (LOG.isErrorEnabled()) {
                    LOG.error("Error opening WebSocket: " + e.getMessage(), e);
                }
                ctx.writeAndFlush(new CloseWebSocketFrame(CloseReason.INTERNAL_ERROR.getCode(), CloseReason.INTERNAL_ERROR.getReason()));
            }
        } else {
            ctx.writeAndFlush(actualResponse);
        }
    });
}
Also used : RouteExecutor(io.micronaut.http.server.RouteExecutor) HttpHeaders(io.netty.handler.codec.http.HttpHeaders) WebSocketServerHandshakerFactory(io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory) LoggerFactory(org.slf4j.LoggerFactory) AsciiString(io.netty.util.AsciiString) Internal(io.micronaut.core.annotation.Internal) CloseReason(io.micronaut.websocket.CloseReason) OnOpen(io.micronaut.websocket.annotation.OnOpen) HttpStatus(io.micronaut.http.HttpStatus) Map(java.util.Map) HttpResponse(io.micronaut.http.HttpResponse) RequestBinderRegistry(io.micronaut.http.bind.RequestBinderRegistry) NettyHttpRequest(io.micronaut.http.server.netty.NettyHttpRequest) MutableHttpResponse(io.micronaut.http.MutableHttpResponse) MutableHttpHeaders(io.micronaut.http.MutableHttpHeaders) ChannelPipeline(io.netty.channel.ChannelPipeline) StringUtils(io.micronaut.core.util.StringUtils) List(java.util.List) DefaultHttpHeaders(io.netty.handler.codec.http.DefaultHttpHeaders) SslHandler(io.netty.handler.ssl.SslHandler) Optional(java.util.Optional) HttpAttributes(io.micronaut.http.HttpAttributes) WebSocketSessionRepository(io.micronaut.http.netty.websocket.WebSocketSessionRepository) WebSocketBean(io.micronaut.websocket.context.WebSocketBean) OnMessage(io.micronaut.websocket.annotation.OnMessage) Scheduler(reactor.core.scheduler.Scheduler) AtomicReference(java.util.concurrent.atomic.AtomicReference) WebSocketBeanRegistry(io.micronaut.websocket.context.WebSocketBeanRegistry) ChannelHandlerContext(io.netty.channel.ChannelHandlerContext) NettyHttpHeaders(io.micronaut.http.netty.NettyHttpHeaders) UriRouteMatch(io.micronaut.web.router.UriRouteMatch) Schedulers(reactor.core.scheduler.Schedulers) CloseWebSocketFrame(io.netty.handler.codec.http.websocketx.CloseWebSocketFrame) HttpRequest(io.micronaut.http.HttpRequest) ServerRequestContext(io.micronaut.http.context.ServerRequestContext) MediaTypeCodecRegistry(io.micronaut.http.codec.MediaTypeCodecRegistry) HttpStatusException(io.micronaut.http.exceptions.HttpStatusException) HttpMethod(io.micronaut.http.HttpMethod) Logger(org.slf4j.Logger) HttpHeaderValues(io.netty.handler.codec.http.HttpHeaderValues) Publisher(org.reactivestreams.Publisher) Mono(reactor.core.publisher.Mono) ChannelFuture(io.netty.channel.ChannelFuture) Channel(io.netty.channel.Channel) Consumer(java.util.function.Consumer) NonNull(io.micronaut.core.annotation.NonNull) Flux(reactor.core.publisher.Flux) NettyEmbeddedServices(io.micronaut.http.server.netty.NettyEmbeddedServices) SimpleChannelInboundHandler(io.netty.channel.SimpleChannelInboundHandler) ServerWebSocket(io.micronaut.websocket.annotation.ServerWebSocket) WebSocketServerHandshaker(io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker) ChannelPipelineCustomizer(io.micronaut.http.netty.channel.ChannelPipelineCustomizer) ChannelHandler(io.netty.channel.ChannelHandler) HttpHeaderNames(io.netty.handler.codec.http.HttpHeaderNames) Router(io.micronaut.web.router.Router) NettyHttpRequest(io.micronaut.http.server.netty.NettyHttpRequest) HttpRequest(io.micronaut.http.HttpRequest) CloseWebSocketFrame(io.netty.handler.codec.http.websocketx.CloseWebSocketFrame) MutableHttpResponse(io.micronaut.http.MutableHttpResponse) Scheduler(reactor.core.scheduler.Scheduler) WebSocketBean(io.micronaut.websocket.context.WebSocketBean) HttpStatusException(io.micronaut.http.exceptions.HttpStatusException) AtomicReference(java.util.concurrent.atomic.AtomicReference) OnMessage(io.micronaut.websocket.annotation.OnMessage) OnOpen(io.micronaut.websocket.annotation.OnOpen) ChannelHandler(io.netty.channel.ChannelHandler) ChannelPipeline(io.netty.channel.ChannelPipeline) UriRouteMatch(io.micronaut.web.router.UriRouteMatch)

Example 2 with UriRouteMatch

use of io.micronaut.web.router.UriRouteMatch 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)2 HttpStatus (io.micronaut.http.HttpStatus)2 UriRouteMatch (io.micronaut.web.router.UriRouteMatch)2 Internal (io.micronaut.core.annotation.Internal)1 NonNull (io.micronaut.core.annotation.NonNull)1 StringUtils (io.micronaut.core.util.StringUtils)1 HttpAttributes (io.micronaut.http.HttpAttributes)1 HttpRequest (io.micronaut.http.HttpRequest)1 HttpResponse (io.micronaut.http.HttpResponse)1 MediaType (io.micronaut.http.MediaType)1 MutableHttpHeaders (io.micronaut.http.MutableHttpHeaders)1 MutableHttpResponse (io.micronaut.http.MutableHttpResponse)1 RequestBinderRegistry (io.micronaut.http.bind.RequestBinderRegistry)1 MediaTypeCodecRegistry (io.micronaut.http.codec.MediaTypeCodecRegistry)1 ServerRequestContext (io.micronaut.http.context.ServerRequestContext)1 HttpStatusException (io.micronaut.http.exceptions.HttpStatusException)1 AbstractNettyHttpRequest (io.micronaut.http.netty.AbstractNettyHttpRequest)1 NettyHttpHeaders (io.micronaut.http.netty.NettyHttpHeaders)1 ChannelPipelineCustomizer (io.micronaut.http.netty.channel.ChannelPipelineCustomizer)1 WebSocketSessionRepository (io.micronaut.http.netty.websocket.WebSocketSessionRepository)1