private void initializeKeepAliveTimeout(Channel channel, MqttConnectMessage msg, String clientId) { int keepAlive = msg.variableHeader().keepAliveTimeSeconds(); NettyUtils.keepAlive(channel, keepAlive); NettyUtils.cleanSession(channel, msg.variableHeader().isCleanSession()); NettyUtils.clientID(channel, clientId); int idleTime = Math.round(keepAlive * 1.5f); setIdleTime(channel.pipeline(), idleTime); LOG.debug("Connection has been configured CId={}, keepAlive={}, removeTemporaryQoS2={}, idleTime={}", clientId, keepAlive, msg.variableHeader().isCleanSession(), idleTime); }
void processSubscribe(MqttSubscribeMessage msg) { final String clientID = NettyUtils.clientID(channel); if (!connected) { LOG.warn("SUBSCRIBE received on already closed connection, CId={}, channel: {}", clientID, channel); dropConnection(); return; } postOffice.subscribeClientToTopics(msg, clientID, NettyUtils.userName(channel), this); }
String getClientId() { return NettyUtils.clientID(channel); }
String getUsername() { return NettyUtils.userName(channel); }
@Test public void testConnAckContainsSessionPresentFlag() { MqttConnectMessage msg = connMsg.clientId(FAKE_CLIENT_ID) .protocolVersion(MqttVersion.MQTT_3_1_1) .build(); NettyUtils.clientID(channel, FAKE_CLIENT_ID); NettyUtils.cleanSession(channel, false); // Connect a first time sut.bindToSession(connection, msg, FAKE_CLIENT_ID); // disconnect sut.disconnect(FAKE_CLIENT_ID); // Exercise, reconnect EmbeddedChannel anotherChannel = new EmbeddedChannel(); MQTTConnection anotherConnection = createMQTTConnection(ALLOW_ANONYMOUS_AND_ZEROBYTE_CLIENT_ID, anotherChannel); sut.bindToSession(anotherConnection, msg, FAKE_CLIENT_ID); // Verify assertEqualsConnAck(CONNECTION_ACCEPTED, anotherChannel.readOutbound()); assertTrue("Connection is accepted and therefore should remain open", anotherChannel.isOpen()); }
return; MqttMessage msg = NettyUtils.validateMessage(message); String clientID = NettyUtils.clientID(ctx.channel()); MqttMessageType messageType = msg.fixedHeader().messageType(); switch (messageType) {
@Override public void channelRead(ChannelHandlerContext ctx, Object message) throws Exception { MqttMessage msg = NettyUtils.validateMessage(message); final MQTTConnection mqttConnection = mqttConnection(ctx.channel()); try { mqttConnection.handleMessage(msg); } catch (Throwable ex) { //ctx.fireExceptionCaught(ex); LOG.error("Error processing protocol message: {}", msg.fixedHeader().messageType(), ex); ctx.channel().close().addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) { LOG.info("Closed client channel due to exception in processing"); } }); } finally { ReferenceCountUtil.release(msg); } }
void sendSubAckMessage(int messageID, MqttSubAckMessage ackMessage) { final String clientId = NettyUtils.clientID(channel); LOG.trace("Sending SUBACK response CId={}, messageId: {}", clientId, messageID); channel.writeAndFlush(ackMessage).addListener(FIRE_EXCEPTION_ON_FAILURE); }
private boolean login(MqttConnectMessage msg, final String clientId) { // handle user authentication if (msg.variableHeader().hasUserName()) { byte[] pwd = null; if (msg.variableHeader().hasPassword()) { pwd = msg.payload().password().getBytes(StandardCharsets.UTF_8); } else if (!brokerConfig.isAllowAnonymous()) { LOG.error("Client didn't supply any password and MQTT anonymous mode is disabled CId={}", clientId); return false; } final String login = msg.payload().userName(); if (!authenticator.checkValid(clientId, login, pwd)) { LOG.error("Authenticator has rejected the MQTT credentials CId={}, username={}", clientId, login); return false; } NettyUtils.userName(channel, login); } else if (!brokerConfig.isAllowAnonymous()) { LOG.error("Client didn't supply any credentials and MQTT anonymous mode is disabled. CId={}", clientId); return false; } return true; }
@Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { LOG.error("Unexpected exception while processing MQTT message. Closing Netty channel. CId={}", NettyUtils.clientID(ctx.channel()), cause); ctx.close().addListener(CLOSE_ON_FAILURE); }
void processDisconnect(MqttMessage msg) { final String clientID = NettyUtils.clientID(channel); LOG.trace("Start DISCONNECT CId={}, channel: {}", clientID, channel); if (!connected) { LOG.info("DISCONNECT received on already closed connection, CId={}, channel: {}", clientID, channel); return; } sessionRegistry.disconnect(clientID); connected = false; channel.close().addListener(FIRE_EXCEPTION_ON_FAILURE); LOG.trace("Processed DISCONNECT CId={}, channel: {}", clientID, channel); String userName = NettyUtils.userName(channel); postOffice.dispatchDisconnection(clientID,userName); LOG.trace("dispatch disconnection: clientId={}, userName={}", clientID, userName); }
public void unsubscribe(List<String> topics, MQTTConnection mqttConnection, int messageId) { final String clientID = mqttConnection.getClientId(); for (String t : topics) { Topic topic = new Topic(t); boolean validTopic = topic.isValid(); if (!validTopic) { // close the connection, not valid topicFilter is a protocol violation mqttConnection.dropConnection(); LOG.warn("Topic filter is not valid. CId={}, topics: {}, offending topic filter: {}", clientID, topics, topic); return; } LOG.trace("Removing subscription. CId={}, topic={}", clientID, topic); subscriptions.removeSubscription(topic, clientID); // TODO remove the subscriptions to Session // clientSession.unsubscribeFrom(topic); String username = NettyUtils.userName(mqttConnection.channel); interceptor.notifyTopicUnsubscribed(topic.toString(), clientID, username); } // ack the client mqttConnection.sendUnsubAckMessage(topics, clientID, messageId); }
/** * Is called when the write timeout expire. * * @param ctx the channel context. */ private void channelIdle(ChannelHandlerContext ctx) { // ctx.fireUserEventTriggered(evt); if (LOG.isTraceEnabled()) { LOG.trace("Flushing idle Netty channel {} Cid: {}", ctx.channel(), NettyUtils.clientID(ctx.channel())); } ctx.channel().flush(); }
void handleConnectionLost() { String clientID = NettyUtils.clientID(channel); if (clientID == null || clientID.isEmpty()) { return; } LOG.info("Notifying connection lost event. CId: {}, channel: {}", clientID, channel); Session session = sessionRegistry.retrieve(clientID); if (session.hasWill()) { postOffice.fireWill(session.getWill()); } if (session.isClean()) { sessionRegistry.remove(clientID); } else { sessionRegistry.disconnect(clientID); } connected = false; //dispatch connection lost to intercept. String userName = NettyUtils.userName(channel); postOffice.dispatchConnectionLost(clientID,userName); LOG.trace("dispatch disconnection: clientId={}, userName={}", clientID, userName); }
@Test public void testSubscribedToNotAuthorizedTopic() { NettyUtils.userName(channel, FAKE_USER_NAME); IAuthorizatorPolicy prohibitReadOnNewsTopic = mock(IAuthorizatorPolicy.class); when(prohibitReadOnNewsTopic.canRead(eq(new Topic(NEWS_TOPIC)), eq(FAKE_USER_NAME), eq(FAKE_CLIENT_ID))) .thenReturn(false); sut = new PostOffice(subscriptions, new MemoryRetainedRepository(), sessionRegistry, ConnectionTestUtils.NO_OBSERVERS_INTERCEPTOR, new Authorizator(prohibitReadOnNewsTopic)); connection.processConnect(connectMessage); ConnectionTestUtils.assertConnectAccepted(channel); //Exercise MqttSubscribeMessage subscribe = MqttMessageBuilders.subscribe() .addSubscription(AT_MOST_ONCE, NEWS_TOPIC) .messageId(1) .build(); sut.subscribeClientToTopics(subscribe, FAKE_CLIENT_ID, FAKE_USER_NAME, connection); // Verify MqttSubAckMessage subAckMsg = channel.readOutbound(); verifyFailureQos(subAckMsg); }
@Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { String clientID = NettyUtils.clientID(ctx.channel()); if (clientID != null && !clientID.isEmpty()) { LOG.info("Channel closed <{}>", clientID); } ctx.fireChannelInactive(); }
void processPublish(MqttPublishMessage msg) { final MqttQoS qos = msg.fixedHeader().qosLevel(); final String username = NettyUtils.userName(channel); final String topicName = msg.variableHeader().topicName(); final String clientId = getClientId();
private void resendNotAcked(ChannelHandlerContext ctx/* , IdleStateEvent evt */) { if (LOG.isTraceEnabled()) { LOG.trace("Flushing idle Netty channel {} for clientId: {}", ctx.channel(), NettyUtils.clientID(ctx.channel())); } ctx.fireUserEventTriggered(new ResendNotAckedPublishes()); } }
@Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { String clientID = NettyUtils.clientID(ctx.channel()); if (clientID != null && !clientID.isEmpty()) { this.connectedClientsMetrics.dec(); } ctx.fireChannelInactive(); }
@Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt instanceof IdleStateEvent) { IdleState e = ((IdleStateEvent) evt).state(); if (e == IdleState.READER_IDLE) { LOG.info("Firing channel inactive event. MqttClientId = {}.", NettyUtils.clientID(ctx.channel())); // fire a close that then fire channelInactive to trigger publish of Will ctx.close().addListener(CLOSE_ON_FAILURE); } } else { if (LOG.isTraceEnabled()) { LOG.trace("Firing Netty event CId = {}, eventClass = {}", NettyUtils.clientID(ctx.channel()), evt.getClass().getName()); } super.userEventTriggered(ctx, evt); } } }