Search in sources :

Example 1 with WebSocketState

use of io.micronaut.websocket.bind.WebSocketState in project micronaut-core by micronaut-projects.

the class NettyWebSocketClientHandler method channelRead0.

@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
    final Channel ch = ctx.channel();
    if (!handshaker.isHandshakeComplete()) {
        // web socket client connected
        FullHttpResponse res = (FullHttpResponse) msg;
        this.handshakeResponse = res;
        try {
            handshaker.finishHandshake(ch, res);
        } catch (Exception e) {
            try {
                emitter.error(new WebSocketClientException("Error finishing WebSocket handshake: " + e.getMessage(), e));
            } finally {
                // clientSession isn't set yet, so we do the close manually instead of through session.close
                ch.writeAndFlush(new CloseWebSocketFrame(CloseReason.INTERNAL_ERROR.getCode(), CloseReason.INTERNAL_ERROR.getReason()));
                ch.close();
            }
            return;
        }
        handshakeFuture.setSuccess();
        this.clientSession = createWebSocketSession(ctx);
        T targetBean = genericWebSocketBean.getTarget();
        if (targetBean instanceof WebSocketSessionAware) {
            ((WebSocketSessionAware) targetBean).setWebSocketSession(clientSession);
        }
        ExecutableBinder<WebSocketState> binder = new DefaultExecutableBinder<>();
        BoundExecutable<?, ?> bound = binder.tryBind(messageHandler.getExecutableMethod(), webSocketBinder, new WebSocketState(clientSession, originatingRequest));
        List<Argument<?>> unboundArguments = bound.getUnboundArguments();
        if (unboundArguments.size() == 1) {
            this.clientBodyArgument = unboundArguments.iterator().next();
        } else {
            this.clientBodyArgument = null;
            try {
                emitter.error(new WebSocketClientException("WebSocket @OnMessage method " + targetBean.getClass().getSimpleName() + "." + messageHandler.getExecutableMethod() + " should define exactly 1 message parameter, but found 2 possible candidates: " + unboundArguments));
            } finally {
                if (getSession().isOpen()) {
                    getSession().close(CloseReason.INTERNAL_ERROR);
                }
            }
            return;
        }
        if (pongHandler != null) {
            BoundExecutable<?, ?> boundPong = binder.tryBind(pongHandler.getExecutableMethod(), webSocketBinder, new WebSocketState(clientSession, originatingRequest));
            List<Argument<?>> unboundPongArguments = boundPong.getUnboundArguments();
            if (unboundPongArguments.size() == 1 && unboundPongArguments.get(0).isAssignableFrom(WebSocketPongMessage.class)) {
                this.clientPongArgument = unboundPongArguments.get(0);
            } else {
                this.clientPongArgument = null;
                try {
                    emitter.error(new WebSocketClientException("WebSocket @OnMessage pong handler method " + targetBean.getClass().getSimpleName() + "." + messageHandler.getExecutableMethod() + " should define exactly 1 pong message parameter, but found: " + unboundArguments));
                } finally {
                    if (getSession().isOpen()) {
                        getSession().close(CloseReason.INTERNAL_ERROR);
                    }
                }
                return;
            }
        }
        Optional<? extends MethodExecutionHandle<?, ?>> opt = webSocketBean.openMethod();
        if (opt.isPresent()) {
            MethodExecutionHandle<?, ?> openMethod = opt.get();
            WebSocketState webSocketState = new WebSocketState(clientSession, originatingRequest);
            try {
                BoundExecutable openMethodBound = binder.bind(openMethod.getExecutableMethod(), webSocketStateBinderRegistry, webSocketState);
                Object target = openMethod.getTarget();
                Object result = openMethodBound.invoke(target);
                if (Publishers.isConvertibleToPublisher(result)) {
                    Publisher<?> reactiveSequence = Publishers.convertPublisher(result, Publisher.class);
                    Flux.from(reactiveSequence).subscribe(o -> {
                    }, error -> emitter.error(new WebSocketSessionException("Error opening WebSocket client session: " + error.getMessage(), error)), () -> {
                        emitter.next(targetBean);
                        emitter.complete();
                    });
                } else {
                    emitter.next(targetBean);
                    emitter.complete();
                }
            } catch (Throwable e) {
                emitter.error(new WebSocketClientException("Error opening WebSocket client session: " + e.getMessage(), e));
                if (getSession().isOpen()) {
                    getSession().close(CloseReason.INTERNAL_ERROR);
                }
            }
        } else {
            emitter.next(targetBean);
            emitter.complete();
        }
        return;
    }
    if (msg instanceof WebSocketFrame) {
        handleWebSocketFrame(ctx, (WebSocketFrame) msg);
    } else {
        ctx.fireChannelRead(msg);
    }
}
Also used : CloseWebSocketFrame(io.netty.handler.codec.http.websocketx.CloseWebSocketFrame) Argument(io.micronaut.core.type.Argument) DefaultExecutableBinder(io.micronaut.core.bind.DefaultExecutableBinder) Channel(io.netty.channel.Channel) WebSocketClientException(io.micronaut.websocket.exceptions.WebSocketClientException) WebSocketSessionException(io.micronaut.websocket.exceptions.WebSocketSessionException) WebSocketSessionAware(io.micronaut.websocket.interceptor.WebSocketSessionAware) WebSocketClientException(io.micronaut.websocket.exceptions.WebSocketClientException) WebSocketSessionException(io.micronaut.websocket.exceptions.WebSocketSessionException) BoundExecutable(io.micronaut.core.bind.BoundExecutable) FullHttpResponse(io.netty.handler.codec.http.FullHttpResponse) CloseWebSocketFrame(io.netty.handler.codec.http.websocketx.CloseWebSocketFrame) WebSocketFrame(io.netty.handler.codec.http.websocketx.WebSocketFrame) WebSocketPongMessage(io.micronaut.websocket.WebSocketPongMessage) WebSocketState(io.micronaut.websocket.bind.WebSocketState)

Example 2 with WebSocketState

use of io.micronaut.websocket.bind.WebSocketState in project micronaut-core by micronaut-projects.

the class AbstractNettyWebSocketHandler method handleWebSocketFrame.

/**
 * Handles WebSocket frame request.
 *
 * @param ctx The context
 * @param msg The frame
 */
protected void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame msg) {
    if (msg instanceof TextWebSocketFrame || msg instanceof BinaryWebSocketFrame || msg instanceof ContinuationWebSocketFrame) {
        if (messageHandler == null) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("WebSocket bean [" + webSocketBean.getTarget() + "] received message, but defined no @OnMessage handler. Dropping frame...");
            }
            writeCloseFrameAndTerminate(ctx, CloseReason.UNSUPPORTED_DATA);
        } else {
            ByteBuf msgContent = msg.content().retain();
            if (!msg.isFinalFragment()) {
                frameBuffer.updateAndGet((buffer) -> {
                    if (buffer == null) {
                        buffer = ctx.alloc().compositeBuffer();
                    }
                    buffer.addComponent(true, msgContent);
                    return buffer;
                });
                return;
            }
            ByteBuf content;
            CompositeByteBuf buffer = frameBuffer.getAndSet(null);
            if (buffer == null) {
                content = msgContent;
            } else {
                buffer.addComponent(true, msgContent);
                content = buffer;
            }
            Argument<?> bodyArgument = this.getBodyArgument();
            Optional<?> converted = ConversionService.SHARED.convert(content, bodyArgument);
            content.release();
            if (!converted.isPresent()) {
                MediaType mediaType;
                try {
                    mediaType = messageHandler.stringValue(Consumes.class).map(MediaType::of).orElse(MediaType.APPLICATION_JSON_TYPE);
                } catch (IllegalArgumentException e) {
                    exceptionCaught(ctx, e);
                    return;
                }
                try {
                    converted = mediaTypeCodecRegistry.findCodec(mediaType).map(codec -> codec.decode(bodyArgument, new NettyByteBufferFactory(ctx.alloc()).wrap(msg.content())));
                } catch (CodecException e) {
                    messageProcessingException(ctx, e);
                    return;
                }
            }
            if (converted.isPresent()) {
                Object v = converted.get();
                NettyWebSocketSession currentSession = getSession();
                ExecutableBinder<WebSocketState> executableBinder = new DefaultExecutableBinder<>(Collections.singletonMap(bodyArgument, v));
                try {
                    BoundExecutable boundExecutable = executableBinder.bind(messageHandler.getExecutableMethod(), webSocketBinder, new WebSocketState(currentSession, originatingRequest));
                    Object result = invokeExecutable(boundExecutable, messageHandler);
                    if (Publishers.isConvertibleToPublisher(result)) {
                        Flux<?> flowable = Flux.from(instrumentPublisher(ctx, result));
                        flowable.subscribe(o -> {
                        }, error -> messageProcessingException(ctx, error), () -> messageHandled(ctx, session, v));
                    } else {
                        messageHandled(ctx, session, v);
                    }
                } catch (Throwable e) {
                    messageProcessingException(ctx, e);
                }
            } else {
                writeCloseFrameAndTerminate(ctx, CloseReason.UNSUPPORTED_DATA.getCode(), CloseReason.UNSUPPORTED_DATA.getReason() + ": " + "Received data cannot be converted to target type: " + bodyArgument);
            }
        }
    } else if (msg instanceof PingWebSocketFrame) {
        // respond with pong
        PingWebSocketFrame frame = (PingWebSocketFrame) msg.retain();
        ctx.writeAndFlush(new PongWebSocketFrame(frame.content()));
    } else if (msg instanceof PongWebSocketFrame) {
        if (pongHandler != null) {
            ByteBuf content = msg.content();
            WebSocketPongMessage message = new WebSocketPongMessage(NettyByteBufferFactory.DEFAULT.wrap(content));
            NettyWebSocketSession currentSession = getSession();
            ExecutableBinder<WebSocketState> executableBinder = new DefaultExecutableBinder<>(Collections.singletonMap(getPongArgument(), message));
            try {
                BoundExecutable boundExecutable = executableBinder.bind(pongHandler.getExecutableMethod(), webSocketBinder, new WebSocketState(currentSession, originatingRequest));
                Object result = invokeExecutable(boundExecutable, pongHandler);
                if (Publishers.isConvertibleToPublisher(result)) {
                    // delay the buffer release until the publisher has completed
                    content.retain();
                    Flux<?> flowable = Flux.from(instrumentPublisher(ctx, result));
                    flowable.subscribe(o -> {
                    }, error -> {
                        if (LOG.isErrorEnabled()) {
                            LOG.error("Error Processing WebSocket Pong Message [" + webSocketBean + "]: " + error.getMessage(), error);
                        }
                        exceptionCaught(ctx, error);
                    }, content::release);
                }
            } catch (Throwable e) {
                if (LOG.isErrorEnabled()) {
                    LOG.error("Error Processing WebSocket Message [" + webSocketBean + "]: " + e.getMessage(), e);
                }
                exceptionCaught(ctx, e);
            }
        }
    } else if (msg instanceof CloseWebSocketFrame) {
        CloseWebSocketFrame cwsf = (CloseWebSocketFrame) msg;
        handleCloseFrame(ctx, cwsf);
    } else {
        writeCloseFrameAndTerminate(ctx, CloseReason.UNSUPPORTED_DATA);
    }
}
Also used : Publishers(io.micronaut.core.async.publisher.Publishers) ArgumentBinderRegistry(io.micronaut.core.bind.ArgumentBinderRegistry) LoggerFactory(org.slf4j.LoggerFactory) Internal(io.micronaut.core.annotation.Internal) UnsatisfiedArgumentException(io.micronaut.core.bind.exceptions.UnsatisfiedArgumentException) CloseReason(io.micronaut.websocket.CloseReason) MediaType(io.micronaut.http.MediaType) Map(java.util.Map) RequestBinderRegistry(io.micronaut.http.bind.RequestBinderRegistry) DefaultExecutableBinder(io.micronaut.core.bind.DefaultExecutableBinder) CodecException(io.micronaut.http.codec.CodecException) ContinuationWebSocketFrame(io.netty.handler.codec.http.websocketx.ContinuationWebSocketFrame) WebSocketState(io.micronaut.websocket.bind.WebSocketState) CompositeByteBuf(io.netty.buffer.CompositeByteBuf) List(java.util.List) Optional(java.util.Optional) NettyByteBufferFactory(io.micronaut.buffer.netty.NettyByteBufferFactory) WebSocketBean(io.micronaut.websocket.context.WebSocketBean) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) HashMap(java.util.HashMap) AtomicReference(java.util.concurrent.atomic.AtomicReference) ExecutableMethod(io.micronaut.inject.ExecutableMethod) ChannelHandlerContext(io.netty.channel.ChannelHandlerContext) WebSocketVersion(io.netty.handler.codec.http.websocketx.WebSocketVersion) ExecutableBinder(io.micronaut.core.bind.ExecutableBinder) ByteBuf(io.netty.buffer.ByteBuf) PongWebSocketFrame(io.netty.handler.codec.http.websocketx.PongWebSocketFrame) Schedulers(reactor.core.scheduler.Schedulers) CloseWebSocketFrame(io.netty.handler.codec.http.websocketx.CloseWebSocketFrame) Argument(io.micronaut.core.type.Argument) HttpRequest(io.micronaut.http.HttpRequest) BoundExecutable(io.micronaut.core.bind.BoundExecutable) ConversionService(io.micronaut.core.convert.ConversionService) MediaTypeCodecRegistry(io.micronaut.http.codec.MediaTypeCodecRegistry) Logger(org.slf4j.Logger) WebSocketFrame(io.netty.handler.codec.http.websocketx.WebSocketFrame) PingWebSocketFrame(io.netty.handler.codec.http.websocketx.PingWebSocketFrame) Publisher(org.reactivestreams.Publisher) IOException(java.io.IOException) MethodExecutionHandle(io.micronaut.inject.MethodExecutionHandle) Channel(io.netty.channel.Channel) BinaryWebSocketFrame(io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame) Consumer(java.util.function.Consumer) Flux(reactor.core.publisher.Flux) WebSocketStateBinderRegistry(io.micronaut.websocket.bind.WebSocketStateBinderRegistry) TextWebSocketFrame(io.netty.handler.codec.http.websocketx.TextWebSocketFrame) SimpleChannelInboundHandler(io.netty.channel.SimpleChannelInboundHandler) Consumes(io.micronaut.http.annotation.Consumes) WebSocketPongMessage(io.micronaut.websocket.WebSocketPongMessage) Collections(java.util.Collections) BinaryWebSocketFrame(io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame) CompositeByteBuf(io.netty.buffer.CompositeByteBuf) ByteBuf(io.netty.buffer.ByteBuf) PingWebSocketFrame(io.netty.handler.codec.http.websocketx.PingWebSocketFrame) CompositeByteBuf(io.netty.buffer.CompositeByteBuf) PongWebSocketFrame(io.netty.handler.codec.http.websocketx.PongWebSocketFrame) Consumes(io.micronaut.http.annotation.Consumes) TextWebSocketFrame(io.netty.handler.codec.http.websocketx.TextWebSocketFrame) MediaType(io.micronaut.http.MediaType) CodecException(io.micronaut.http.codec.CodecException) CloseWebSocketFrame(io.netty.handler.codec.http.websocketx.CloseWebSocketFrame) NettyByteBufferFactory(io.micronaut.buffer.netty.NettyByteBufferFactory) ContinuationWebSocketFrame(io.netty.handler.codec.http.websocketx.ContinuationWebSocketFrame) DefaultExecutableBinder(io.micronaut.core.bind.DefaultExecutableBinder) BoundExecutable(io.micronaut.core.bind.BoundExecutable) WebSocketPongMessage(io.micronaut.websocket.WebSocketPongMessage) WebSocketState(io.micronaut.websocket.bind.WebSocketState)

Example 3 with WebSocketState

use of io.micronaut.websocket.bind.WebSocketState in project micronaut-core by micronaut-projects.

the class AbstractNettyWebSocketHandler method bindMethod.

private BoundExecutable bindMethod(HttpRequest<?> request, ArgumentBinderRegistry<WebSocketState> binderRegistry, MethodExecutionHandle<?, ?> openMethod, List<?> parameters) {
    ExecutableMethod<?, ?> executable = openMethod.getExecutableMethod();
    Map<Argument<?>, Object> preBound = prepareBoundVariables(executable, parameters);
    ExecutableBinder<WebSocketState> executableBinder = new DefaultExecutableBinder<>(preBound);
    return executableBinder.bind(executable, binderRegistry, new WebSocketState(getSession(), request));
}
Also used : Argument(io.micronaut.core.type.Argument) DefaultExecutableBinder(io.micronaut.core.bind.DefaultExecutableBinder) WebSocketState(io.micronaut.websocket.bind.WebSocketState)

Aggregations

DefaultExecutableBinder (io.micronaut.core.bind.DefaultExecutableBinder)3 Argument (io.micronaut.core.type.Argument)3 WebSocketState (io.micronaut.websocket.bind.WebSocketState)3 BoundExecutable (io.micronaut.core.bind.BoundExecutable)2 WebSocketPongMessage (io.micronaut.websocket.WebSocketPongMessage)2 Channel (io.netty.channel.Channel)2 CloseWebSocketFrame (io.netty.handler.codec.http.websocketx.CloseWebSocketFrame)2 WebSocketFrame (io.netty.handler.codec.http.websocketx.WebSocketFrame)2 NettyByteBufferFactory (io.micronaut.buffer.netty.NettyByteBufferFactory)1 Internal (io.micronaut.core.annotation.Internal)1 Publishers (io.micronaut.core.async.publisher.Publishers)1 ArgumentBinderRegistry (io.micronaut.core.bind.ArgumentBinderRegistry)1 ExecutableBinder (io.micronaut.core.bind.ExecutableBinder)1 UnsatisfiedArgumentException (io.micronaut.core.bind.exceptions.UnsatisfiedArgumentException)1 ConversionService (io.micronaut.core.convert.ConversionService)1 HttpRequest (io.micronaut.http.HttpRequest)1 MediaType (io.micronaut.http.MediaType)1 Consumes (io.micronaut.http.annotation.Consumes)1 RequestBinderRegistry (io.micronaut.http.bind.RequestBinderRegistry)1 CodecException (io.micronaut.http.codec.CodecException)1