-
Notifications
You must be signed in to change notification settings - Fork 90
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
integrated client side keep alive timeout #129
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -653,21 +653,46 @@ private void initChannel(ChannelPipeline pipeline) { | |
if (this.options.isAutoKeepAlive() && | ||
this.options.getKeepAliveTimeSeconds() != 0) { | ||
|
||
int keepAliveInterval = this.options.getKeepAliveTimeSeconds(); | ||
|
||
// handler for sending PINGREQ (keepAlive) if reader- or writer-channel become idle | ||
pipeline.addBefore("handler", "idle", | ||
new IdleStateHandler(0, this.options.getKeepAliveTimeSeconds(), 0)); | ||
new IdleStateHandler(keepAliveInterval, keepAliveInterval, 0)); | ||
pipeline.addBefore("handler", "keepAliveHandler", new ChannelDuplexHandler() { | ||
|
||
@Override | ||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { | ||
|
||
@Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { | ||
if (evt instanceof IdleStateEvent) { | ||
IdleStateEvent e = (IdleStateEvent) evt; | ||
if (e.state() == IdleState.WRITER_IDLE) { | ||
switch (e.state()) { | ||
case READER_IDLE: | ||
// verify that server is still connected (e.g. when using QoS-0) | ||
ping(); | ||
break; | ||
case WRITER_IDLE: | ||
// send ping or broker will close connection | ||
ping(); | ||
break; | ||
} | ||
} | ||
} | ||
}); | ||
|
||
if(this.options.getKeepAliveTimeout() > 0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Space after |
||
int keepAliveTimeout = keepAliveInterval + this.options.getKeepAliveTimeout(); | ||
// handler for ping-response timeout. connection will be closed if broker READER_IDLE extends timeout | ||
pipeline.addBefore("handler", "idleTimeout", new IdleStateHandler(keepAliveTimeout, 0, 0)); | ||
pipeline.addBefore("handler", "idleTimeoutHandler", new ChannelDuplexHandler() { | ||
|
||
@Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { | ||
if (evt instanceof IdleStateEvent) { | ||
IdleStateEvent e = (IdleStateEvent) evt; | ||
if (e.state() == IdleState.READER_IDLE) { | ||
ctx.close(); | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -243,11 +243,10 @@ private void handleConnect(MqttConnectMessage msg) { | |
if (msg.variableHeader().keepAliveTimeSeconds() != 0) { | ||
|
||
// the server waits for one and a half times the keep alive time period (MQTT spec) | ||
int timeout = msg.variableHeader().keepAliveTimeSeconds() + | ||
msg.variableHeader().keepAliveTimeSeconds() / 2; | ||
int keepAliveTimeout =(int) (msg.variableHeader().keepAliveTimeSeconds() * 1.5); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. space after |
||
|
||
// modifying the channel pipeline for adding the idle state handler with previous timeout | ||
chctx.pipeline().addBefore("handler", "idle", new IdleStateHandler(timeout, 0, 0)); | ||
chctx.pipeline().addBefore("handler", "idle", new IdleStateHandler(keepAliveTimeout, 0, 0)); | ||
chctx.pipeline().addBefore("handler", "keepAliveHandler", new ChannelDuplexHandler() { | ||
|
||
@Override | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/* | ||
* Copyright 2016 Red Hat Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package io.vertx.mqtt.test.client; | ||
|
||
import io.vertx.core.Vertx; | ||
import io.vertx.ext.unit.Async; | ||
import io.vertx.ext.unit.TestContext; | ||
import io.vertx.ext.unit.junit.VertxUnitRunner; | ||
import io.vertx.mqtt.MqttClient; | ||
import io.vertx.mqtt.MqttClientOptions; | ||
import io.vertx.mqtt.MqttServer; | ||
import io.vertx.mqtt.impl.MqttServerImpl; | ||
import org.junit.After; | ||
import org.junit.Before; | ||
import org.junit.Rule; | ||
import org.junit.Test; | ||
import org.junit.rules.ExpectedException; | ||
import org.junit.runner.RunWith; | ||
|
||
import java.util.concurrent.TimeoutException; | ||
import java.util.concurrent.atomic.AtomicInteger; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
|
||
/** | ||
* MQTT client keep alive tests using a Vert.x MQTT server to accomodate testing. | ||
*/ | ||
@RunWith(VertxUnitRunner.class) | ||
public class MqttClientKeepAliveTest { | ||
|
||
private Vertx vertx; | ||
private MqttServer server; | ||
|
||
private void startServer(TestContext ctx) { | ||
Async async = ctx.async(); | ||
server.listen(1884, "localhost", ctx.asyncAssertSuccess(server -> async.complete())); | ||
async.awaitSuccess(10000); | ||
} | ||
|
||
@Before | ||
public void before(TestContext ctx) { | ||
vertx = Vertx.vertx(); | ||
server = MqttServer.create(vertx); | ||
} | ||
|
||
@After | ||
public void after(TestContext ctx) { | ||
vertx.close(ctx.asyncAssertSuccess()); | ||
} | ||
|
||
@Test | ||
public void clientWillDisconnectOnMissingPingResponse(TestContext ctx) { | ||
AtomicInteger pings = new AtomicInteger(); | ||
server.endpointHandler(endpoint -> { | ||
endpoint.autoKeepAlive(false); // Tell the server not to respond to PINGREQ | ||
endpoint.accept(false); | ||
endpoint.pingHandler(v -> pings.incrementAndGet()); | ||
}); | ||
startServer(ctx); | ||
Async async = ctx.async(); | ||
MqttClientOptions options = new MqttClientOptions(); | ||
options.setKeepAliveTimeSeconds(2); | ||
options.setKeepAliveTimeout(1); | ||
MqttClient client = MqttClient.create(vertx, options); | ||
client.connect(1884, "localhost", ctx.asyncAssertSuccess(ack -> { | ||
client.closeHandler(v -> { | ||
assertEquals(2, pings.get()); // READER & WRITER idle | ||
async.complete(); | ||
}); | ||
})); | ||
async.await(); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
/* | ||
* Copyright 2016 Red Hat Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package io.vertx.mqtt.test.server; | ||
|
||
import io.vertx.core.Vertx; | ||
import io.vertx.ext.unit.Async; | ||
import io.vertx.ext.unit.TestContext; | ||
import io.vertx.ext.unit.junit.VertxUnitRunner; | ||
import io.vertx.mqtt.MqttClient; | ||
import io.vertx.mqtt.MqttClientOptions; | ||
import io.vertx.mqtt.MqttServer; | ||
import org.junit.After; | ||
import org.junit.Before; | ||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
|
||
import java.util.concurrent.atomic.AtomicInteger; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
|
||
/** | ||
* MQTT client keep alive tests using a Vert.x MQTT server to accomodate testing. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess this comment is wrong, the test is about MQTT server not client. |
||
*/ | ||
@RunWith(VertxUnitRunner.class) | ||
public class MqttServerKeepAliveTest { | ||
|
||
private Vertx vertx; | ||
private MqttServer server; | ||
|
||
private void startServer(TestContext ctx) { | ||
Async async = ctx.async(); | ||
server.listen(1884, "localhost", ctx.asyncAssertSuccess(server -> async.complete())); | ||
async.awaitSuccess(10000); | ||
} | ||
|
||
@Before | ||
public void before(TestContext ctx) { | ||
vertx = Vertx.vertx(); | ||
server = MqttServer.create(vertx); | ||
} | ||
|
||
@After | ||
public void after(TestContext ctx) { | ||
vertx.close(ctx.asyncAssertSuccess()); | ||
} | ||
|
||
@Test | ||
public void serverWillDisconnectOnTimeout(TestContext ctx) { | ||
Async async = ctx.async(); | ||
server.endpointHandler(endpoint -> { | ||
endpoint.accept(false); | ||
endpoint.pingHandler(v -> ctx.fail()); | ||
}); | ||
startServer(ctx); | ||
MqttClientOptions options = new MqttClientOptions(); | ||
options.setAutoKeepAlive(false); // The client will manage pings manually | ||
options.setKeepAliveTimeSeconds(2); // Tell the server to disconnects the client after 3 seconds of inactivity | ||
MqttClient client = MqttClient.create(vertx, options); | ||
client.connect(1884, "localhost", ctx.asyncAssertSuccess(ack -> { | ||
client.closeHandler(v -> { | ||
async.complete(); | ||
}); | ||
})); | ||
async.await(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we do "fall through" here? Both cases have the same
ping()
instruction