use of io.vertx.proton.ProtonConnection in project hono by eclipse.
the class MessageForwardingEndpointTest method testOnLinkAttachClosesLinkIfDownstreamIsNotAvailable.
/**
* Verifies that the endpoint does not open a link with a client if the
* downstream messaging network is not available.
*/
@SuppressWarnings({ "unchecked" })
@Test
public void testOnLinkAttachClosesLinkIfDownstreamIsNotAvailable() {
// GIVEN an endpoint without a connection to the downstream messaging network
final ResourceIdentifier targetAddress = ResourceIdentifier.fromString("telemetry/tenant");
final ProtonConnection connection = mock(ProtonConnection.class);
final ProtonReceiver receiver = mock(ProtonReceiver.class);
when(receiver.getRemoteQoS()).thenReturn(ProtonQoS.AT_MOST_ONCE);
final DownstreamAdapter adapter = mock(DownstreamAdapter.class);
doAnswer(invocation -> {
final Handler<AsyncResult<Void>> handler = invocation.getArgument(1);
handler.handle(Future.failedFuture("downstream not available"));
return null;
}).when(adapter).onClientAttach(any(UpstreamReceiver.class), any(Handler.class));
final MessageForwardingEndpoint<HonoMessagingConfigProperties> endpoint = getEndpoint();
endpoint.setDownstreamAdapter(adapter);
// WHEN a client tries to attach
endpoint.onLinkAttach(connection, receiver, targetAddress);
// THEN the endpoint closes the link
final ArgumentCaptor<ErrorCondition> errorCondition = ArgumentCaptor.forClass(ErrorCondition.class);
verify(receiver).setCondition(errorCondition.capture());
assertThat(errorCondition.getValue().getCondition(), is(AmqpError.PRECONDITION_FAILED));
verify(receiver).close();
}
use of io.vertx.proton.ProtonConnection in project hono by eclipse.
the class MessageForwardingEndpointTest method testOnLinkAttachClosesLinkIfClientWantsToUseUnsupportedDeliveryMode.
/**
* Verifies that the endpoint does not open a link with a client that uses an unsupported
* delivery mode.
*/
@Test
public void testOnLinkAttachClosesLinkIfClientWantsToUseUnsupportedDeliveryMode() {
// GIVEN an endpoint
MessageForwardingEndpoint<HonoMessagingConfigProperties> endpoint = getEndpoint();
// WHEN a client tries to attach using an unsupported delivery mode
final ProtonConnection connection = mock(ProtonConnection.class);
final ProtonReceiver receiver = mock(ProtonReceiver.class);
final ResourceIdentifier targetAddress = ResourceIdentifier.fromString("telemetry/tenant");
when(receiver.getRemoteQoS()).thenReturn(ProtonQoS.AT_LEAST_ONCE);
endpoint.onLinkAttach(connection, receiver, targetAddress);
// THEN the endpoint closes the link
final ArgumentCaptor<ErrorCondition> errorCondition = ArgumentCaptor.forClass(ErrorCondition.class);
verify(receiver).setCondition(errorCondition.capture());
assertThat(errorCondition.getValue(), is(ErrorConditions.ERROR_UNSUPPORTED_DELIVERY_MODE));
verify(receiver).close();
}
use of io.vertx.proton.ProtonConnection in project hono by eclipse.
the class MessageForwardingEndpointTest method testMessageHandlerRejectsMalformedMessage.
/**
* Verifies that the endpoint rejects messages that do not pass formal verification.
*/
@SuppressWarnings({ "unchecked" })
@Test
public void testMessageHandlerRejectsMalformedMessage() {
// GIVEN an endpoint with an attached client
final ResourceIdentifier targetAddress = ResourceIdentifier.fromString("telemetry/tenant");
final ProtonConnection connection = mock(ProtonConnection.class);
when(connection.getRemoteContainer()).thenReturn("test-client");
final ProtonReceiver receiver = mock(ProtonReceiver.class);
when(receiver.getRemoteQoS()).thenReturn(ProtonQoS.AT_MOST_ONCE);
final DownstreamAdapter adapter = mock(DownstreamAdapter.class);
doAnswer(invocation -> {
final Handler<AsyncResult<Void>> resultHandler = invocation.getArgument(1);
resultHandler.handle(Future.succeededFuture());
return null;
}).when(adapter).onClientAttach(any(UpstreamReceiver.class), any(Handler.class));
final MessageForwardingEndpoint<HonoMessagingConfigProperties> endpoint = getEndpoint(false);
endpoint.setDownstreamAdapter(adapter);
endpoint.onLinkAttach(connection, receiver, targetAddress);
final ArgumentCaptor<ProtonMessageHandler> messageHandler = ArgumentCaptor.forClass(ProtonMessageHandler.class);
verify(receiver).handler(messageHandler.capture());
// WHEN a client sends a malformed message
final Message message = ProtonHelper.message("malformed");
final ProtonDelivery upstreamDelivery = mock(ProtonDelivery.class);
messageHandler.getValue().handle(upstreamDelivery, message);
// THEN the endpoint rejects the message
final ArgumentCaptor<Rejected> deliveryState = ArgumentCaptor.forClass(Rejected.class);
verify(upstreamDelivery).disposition(deliveryState.capture(), eq(Boolean.TRUE));
assertThat(deliveryState.getValue().getError().getCondition(), is(AmqpError.DECODE_ERROR));
// but does not close the link
verify(receiver, never()).close();
// and the message is not forwarded to the downstream adapter
verify(adapter, never()).processMessage(any(UpstreamReceiver.class), eq(upstreamDelivery), eq(message));
}
use of io.vertx.proton.ProtonConnection in project hono by eclipse.
the class TestSupport method openConnection.
public static ProtonConnection openConnection(final TestContext ctx, final Vertx vertx, final String host, final int port) {
final Async connected = ctx.async();
final AtomicReference<ProtonConnection> protonConnection = new AtomicReference<>();
final ProtonClient client = ProtonClient.create(vertx);
client.connect(host, port, ar -> {
if (ar.succeeded()) {
protonConnection.set(ar.result());
protonConnection.get().setContainer(CLIENT_CONTAINER).open();
connected.complete();
} else {
ctx.fail(ar.cause());
}
});
connected.awaitSuccess(2000);
return protonConnection.get();
}
use of io.vertx.proton.ProtonConnection in project hono by eclipse.
the class RequestResponseEndpoint method onLinkAttach.
/**
* Handles a client's request to establish a link for receiving responses
* to service invocations.
* <p>
* This method registers a consumer on the vert.x event bus for the given reply-to address.
* Response messages received over the event bus are transformed into AMQP messages using
* the {@link #getAmqpReply(EventBusMessage)} method and sent to the client over the established
* link.
*
* @param con The AMQP connection that the link is part of.
* @param sender The link to establish.
* @param replyToAddress The reply-to address to create a consumer on the event bus for.
*/
@Override
public final void onLinkAttach(final ProtonConnection con, final ProtonSender sender, final ResourceIdentifier replyToAddress) {
if (isValidReplyToAddress(replyToAddress)) {
logger.debug("establishing sender link with client [{}]", sender.getName());
final MessageConsumer<JsonObject> replyConsumer = vertx.eventBus().consumer(replyToAddress.toString(), message -> {
// TODO check for correct session here...?
if (logger.isTraceEnabled()) {
logger.trace("forwarding reply to client [{}]: {}", sender.getName(), message.body().encodePrettily());
}
final EventBusMessage response = EventBusMessage.fromJson(message.body());
filterResponse(Constants.getClientPrincipal(con), response).recover(t -> {
final int status = Optional.of(t).map(cause -> {
if (cause instanceof ServiceInvocationException) {
return ((ServiceInvocationException) cause).getErrorCode();
} else {
return null;
}
}).orElse(HttpURLConnection.HTTP_INTERNAL_ERROR);
return Future.succeededFuture(response.getResponse(status));
}).map(filteredResponse -> {
final Message amqpReply = getAmqpReply(filteredResponse);
sender.send(amqpReply);
return null;
});
});
sender.setQoS(ProtonQoS.AT_LEAST_ONCE);
sender.closeHandler(senderClosed -> {
logger.debug("client [{}] closed sender link, removing associated event bus consumer [{}]", sender.getName(), replyConsumer.address());
replyConsumer.unregister();
if (senderClosed.succeeded()) {
senderClosed.result().close();
}
});
sender.open();
} else {
logger.debug("client [{}] provided invalid reply-to address", sender.getName());
sender.setCondition(ProtonHelper.condition(AmqpError.INVALID_FIELD, String.format("reply-to address must have the following format %s/<tenant>/<reply-address>", getName())));
sender.close();
}
}
Aggregations