use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class VertxBasedAmqpProtocolAdapterTest method testAdapterClosesCommandConsumerWhenDeviceClosesReceiverLink.
/**
* Verify that if a client device closes the link for receiving commands, then the AMQP
* adapter sends an empty notification downstream with TTD 0 and closes the command
* consumer.
*/
@Test
public void testAdapterClosesCommandConsumerWhenDeviceClosesReceiverLink() {
// GIVEN an AMQP adapter
givenAnAdapter(properties);
givenAnEventSenderForAnyTenant();
// and a device that wants to receive commands
final CommandConsumer commandConsumer = mock(CommandConsumer.class);
when(commandConsumer.close(any())).thenReturn(Future.succeededFuture());
when(commandConsumerFactory.createCommandConsumer(eq(TEST_TENANT_ID), eq(TEST_DEVICE), VertxMockSupport.anyHandler(), any(), any())).thenReturn(Future.succeededFuture(commandConsumer));
final String sourceAddress = String.format("%s", getCommandEndpoint());
final ProtonSender sender = getSender(sourceAddress);
final Device authenticatedDevice = new Device(TEST_TENANT_ID, TEST_DEVICE);
final ProtonConnection deviceConnection = mock(ProtonConnection.class);
final Record attachments = mock(Record.class);
when(attachments.get(AmqpAdapterConstants.KEY_CLIENT_DEVICE, Device.class)).thenReturn(authenticatedDevice);
when(deviceConnection.attachments()).thenReturn(attachments);
adapter.handleRemoteSenderOpenForCommands(deviceConnection, sender);
// WHEN the client device closes its receiver link (unsubscribe)
final ArgumentCaptor<Handler<AsyncResult<ProtonSender>>> closeHookCaptor = VertxMockSupport.argumentCaptorHandler();
verify(sender).closeHandler(closeHookCaptor.capture());
closeHookCaptor.getValue().handle(null);
// THEN the adapter closes the command consumer
verify(commandConsumer).close(any());
// AND sends an empty notification downstream
assertEmptyNotificationHasBeenSentDownstream(TEST_TENANT_ID, TEST_DEVICE, 0);
}
use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class VertxBasedAmqpProtocolAdapterTest method testUploadTelemetryMessageFailsForDisabledAdapter.
/**
* Verifies that a request to upload an "unsettled" telemetry message from a device that belongs to a tenant for which the AMQP
* adapter is disabled fails and that the device is notified when the message cannot be processed.
*
* @param ctx The vert.x test context.
*/
@Test
public void testUploadTelemetryMessageFailsForDisabledAdapter(final VertxTestContext ctx) {
// GIVEN an adapter configured to use a user-define server.
givenAnAdapter(properties);
givenATelemetrySenderForAnyTenant();
// AND given a tenant for which the AMQP Adapter is disabled
final TenantObject tenantObject = givenAConfiguredTenant(TEST_TENANT_ID, false);
// WHEN a device uploads telemetry data to the adapter (and wants to be notified of failure)
final ProtonDelivery delivery = mock(ProtonDelivery.class);
// AT LEAST ONCE
when(delivery.remotelySettled()).thenReturn(false);
final String to = ResourceIdentifier.from(TelemetryConstants.TELEMETRY_ENDPOINT, TEST_TENANT_ID, TEST_DEVICE).toString();
final Buffer payload = Buffer.buffer("some payload");
adapter.onMessageReceived(AmqpContext.fromMessage(delivery, getFakeMessage(to, payload), span, null)).onComplete(ctx.failing(t -> {
ctx.verify(() -> {
// THEN the adapter does not send the message (regardless of the delivery mode).
assertNoTelemetryMessageHasBeenSentDownstream();
// AND notifies the device by sending back a REJECTED disposition
verify(delivery).disposition(any(Rejected.class), eq(true));
// AND has reported the message as unprocessable
verify(metrics).reportTelemetry(eq(EndpointType.TELEMETRY), eq(TEST_TENANT_ID), eq(tenantObject), eq(ProcessingOutcome.UNPROCESSABLE), eq(MetricsTags.QoS.AT_LEAST_ONCE), eq(payload.length()), any());
});
ctx.completeNow();
}));
}
use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class VertxBasedAmqpProtocolAdapterTest method testConnectionCount.
/**
* Verifies that the adapter increments the connection count when
* a device connects and decrement the count when the device disconnects.
*/
@Test
public void testConnectionCount() {
// GIVEN an AMQP adapter
final ConnectionEventProducer connectionEventProducer = mock(ConnectionEventProducer.class);
when(connectionEventProducer.connected(any(ConnectionEventProducer.Context.class), anyString(), anyString(), any(), any(), any())).thenReturn(Future.succeededFuture());
when(connectionEventProducer.disconnected(any(ConnectionEventProducer.Context.class), anyString(), anyString(), any(), any(), any())).thenReturn(Future.succeededFuture());
givenAnAdapter(properties);
adapter.setConnectionEventProducer(connectionEventProducer);
// with an enabled tenant
givenAConfiguredTenant(TEST_TENANT_ID, true);
// WHEN a device connects
final Device authenticatedDevice = new Device(TEST_TENANT_ID, TEST_DEVICE);
final Record record = new RecordImpl();
record.set(AmqpAdapterConstants.KEY_CLIENT_DEVICE, Device.class, authenticatedDevice);
final ProtonConnection deviceConnection = mock(ProtonConnection.class);
when(deviceConnection.attachments()).thenReturn(record);
when(deviceConnection.getRemoteContainer()).thenReturn("deviceContainer");
adapter.onConnectRequest(deviceConnection);
final ArgumentCaptor<Handler<AsyncResult<ProtonConnection>>> openHandler = VertxMockSupport.argumentCaptorHandler();
verify(deviceConnection).openHandler(openHandler.capture());
openHandler.getValue().handle(Future.succeededFuture(deviceConnection));
// THEN the connection count is incremented
verify(metrics).incrementConnections(TEST_TENANT_ID);
// and a connected event has been fired
verify(connectionEventProducer).connected(any(ConnectionEventProducer.Context.class), anyString(), eq(adapter.getTypeName()), eq(authenticatedDevice), any(), any());
// WHEN the connection to the device is lost
final ArgumentCaptor<Handler<ProtonConnection>> disconnectHandler = VertxMockSupport.argumentCaptorHandler();
verify(deviceConnection).disconnectHandler(disconnectHandler.capture());
disconnectHandler.getValue().handle(deviceConnection);
// THEN the connection count is decremented
verify(metrics).decrementConnections(TEST_TENANT_ID);
// and a disconnected event has been fired
verify(connectionEventProducer).disconnected(any(ConnectionEventProducer.Context.class), eq("deviceContainer"), eq(adapter.getTypeName()), eq(authenticatedDevice), any(), any());
// WHEN the device closes its connection to the adapter
final ArgumentCaptor<Handler<AsyncResult<ProtonConnection>>> closeHandler = VertxMockSupport.argumentCaptorHandler();
verify(deviceConnection).closeHandler(closeHandler.capture());
closeHandler.getValue().handle(Future.succeededFuture());
// THEN the connection count is decremented
verify(metrics, times(2)).decrementConnections(TEST_TENANT_ID);
// and a disconnected event has been fired
verify(connectionEventProducer, times(2)).disconnected(any(ConnectionEventProducer.Context.class), eq("deviceContainer"), eq(adapter.getTypeName()), eq(authenticatedDevice), any(), any());
}
use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class VertxBasedAmqpProtocolAdapter method processRemoteOpen.
private void processRemoteOpen(final ProtonConnection con) {
final Span span = Optional.ofNullable(con.attachments().get(AmqpAdapterConstants.KEY_CURRENT_SPAN, Span.class)).orElseGet(() -> tracer.buildSpan("open connection").ignoreActiveSpan().withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER).withTag(Tags.COMPONENT.getKey(), getTypeName()).start());
final Device authenticatedDevice = getAuthenticatedDevice(con);
TracingHelper.TAG_AUTHENTICATED.set(span, authenticatedDevice != null);
if (authenticatedDevice != null) {
TracingHelper.setDeviceTags(span, authenticatedDevice.getTenantId(), authenticatedDevice.getDeviceId());
}
final String cipherSuite = con.attachments().get(AmqpAdapterConstants.KEY_TLS_CIPHER_SUITE, String.class);
checkConnectionLimitForAdapter().compose(ok -> checkAuthorizationAndResourceLimits(authenticatedDevice, con, span)).compose(ok -> sendConnectedEvent(Optional.ofNullable(con.getRemoteContainer()).orElse("unknown"), authenticatedDevice, span.context())).map(ok -> {
con.setContainer(getTypeName());
con.setOfferedCapabilities(new Symbol[] { Constants.CAP_ANONYMOUS_RELAY });
con.open();
log.debug("connection with device [container: {}] established", con.getRemoteContainer());
span.log("connection established");
Optional.ofNullable(authenticatedDevice).ifPresent(device -> authenticatedDeviceConnections.put(con, device));
metrics.reportConnectionAttempt(ConnectionAttemptOutcome.SUCCEEDED, Optional.ofNullable(authenticatedDevice).map(Device::getTenantId).orElse(null), cipherSuite);
return null;
}).otherwise(t -> {
con.setCondition(getErrorCondition(t));
con.close();
TracingHelper.logError(span, t);
metrics.reportConnectionAttempt(AbstractProtocolAdapterBase.getOutcome(t), Optional.ofNullable(authenticatedDevice).map(Device::getTenantId).orElse(null), cipherSuite);
return null;
}).onComplete(s -> span.finish());
}
use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class VertxBasedAmqpProtocolAdapter method onConnectionLoss.
/**
* To be called by the connection closeHandler / disconnectHandler.
*/
private void onConnectionLoss(final ProtonConnection con) {
final String spanOperationName = stopCalled() ? "close device connection (server shutdown)" : "handle closing of connection";
final Device authenticatedDevice = getAuthenticatedDevice(con);
final Span span = newSpan(spanOperationName, authenticatedDevice, getTraceSamplingPriority(con));
handleConnectionLossInternal(con, span, authenticatedDevice, true).onComplete(ar -> span.finish());
}
Aggregations