use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class AbstractVertxBasedHttpProtocolAdapter method createCommandConsumer.
/**
* Creates a consumer for command messages to be sent to a device.
*
* @param ttdSecs The number of seconds the device waits for a command.
* @param tenantObject The tenant configuration object.
* @param deviceId The identifier of the device.
* @param gatewayId The identifier of the gateway that is acting on behalf of the device
* or {@code null} otherwise.
* @param ctx The device's currently executing HTTP request.
* @param responseReady A future to complete once one of the following conditions are met:
* <ul>
* <li>the request did not include a <em>hono-ttd</em> parameter or</li>
* <li>a command has been received and the response ready future has not yet been
* completed or</li>
* <li>the ttd has expired</li>
* </ul>
* @param uploadMessageSpan The OpenTracing Span used for tracking the processing
* of the request.
* @return A future indicating the outcome of the operation.
* <p>
* The future will be completed with the created message consumer or {@code null}, if
* the response can be sent back to the device without waiting for a command.
* <p>
* The future will be failed with a {@code ServiceInvocationException} if the
* message consumer could not be created.
* @throws NullPointerException if any of the parameters other than TTD or gatewayId is {@code null}.
*/
protected final Future<CommandConsumer> createCommandConsumer(final Integer ttdSecs, final TenantObject tenantObject, final String deviceId, final String gatewayId, final RoutingContext ctx, final Handler<AsyncResult<Void>> responseReady, final Span uploadMessageSpan) {
Objects.requireNonNull(tenantObject);
Objects.requireNonNull(deviceId);
Objects.requireNonNull(ctx);
Objects.requireNonNull(responseReady);
Objects.requireNonNull(uploadMessageSpan);
if (ttdSecs == null || ttdSecs <= 0) {
// no need to wait for a command
responseReady.handle(Future.succeededFuture());
return Future.succeededFuture();
}
final AtomicBoolean requestProcessed = new AtomicBoolean(false);
uploadMessageSpan.setTag(MessageHelper.APP_PROPERTY_DEVICE_TTD, ttdSecs);
final Span waitForCommandSpan = TracingHelper.buildChildSpan(tracer, uploadMessageSpan.context(), "create consumer and wait for command", getTypeName()).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).withTag(TracingHelper.TAG_TENANT_ID, tenantObject.getTenantId()).withTag(TracingHelper.TAG_DEVICE_ID, deviceId).start();
final Handler<CommandContext> commandHandler = commandContext -> {
final Span processCommandSpan = TracingHelper.buildFollowsFromSpan(tracer, waitForCommandSpan.context(), "process received command").withTag(Tags.COMPONENT.getKey(), getTypeName()).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).withTag(TracingHelper.TAG_TENANT_ID, tenantObject.getTenantId()).withTag(TracingHelper.TAG_DEVICE_ID, deviceId).addReference(References.FOLLOWS_FROM, commandContext.getTracingContext()).start();
Tags.COMPONENT.set(commandContext.getTracingSpan(), getTypeName());
commandContext.logCommandToSpan(processCommandSpan);
final Command command = commandContext.getCommand();
final Sample commandSample = getMetrics().startTimer();
if (isCommandValid(command, processCommandSpan)) {
if (requestProcessed.compareAndSet(false, true)) {
waitForCommandSpan.finish();
checkMessageLimit(tenantObject, command.getPayloadSize(), processCommandSpan.context()).onComplete(result -> {
if (result.succeeded()) {
addMicrometerSample(commandContext, commandSample);
// put command context to routing context and notify
ctx.put(CommandContext.KEY_COMMAND_CONTEXT, commandContext);
} else {
commandContext.reject(result.cause());
TracingHelper.logError(processCommandSpan, "rejected command for device", result.cause());
metrics.reportCommand(command.isOneWay() ? Direction.ONE_WAY : Direction.REQUEST, tenantObject.getTenantId(), tenantObject, ProcessingOutcome.from(result.cause()), command.getPayloadSize(), commandSample);
}
cancelCommandReceptionTimer(ctx);
setTtdStatus(ctx, TtdStatus.COMMAND);
responseReady.handle(Future.succeededFuture());
processCommandSpan.finish();
});
} else {
final String errorMsg = "waiting time for command has elapsed or another command has already been processed";
log.debug("{} [tenantId: {}, deviceId: {}]", errorMsg, tenantObject.getTenantId(), deviceId);
getMetrics().reportCommand(command.isOneWay() ? Direction.ONE_WAY : Direction.REQUEST, tenantObject.getTenantId(), tenantObject, ProcessingOutcome.UNDELIVERABLE, command.getPayloadSize(), commandSample);
commandContext.release(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, errorMsg));
TracingHelper.logError(processCommandSpan, errorMsg);
processCommandSpan.finish();
}
} else {
getMetrics().reportCommand(command.isOneWay() ? Direction.ONE_WAY : Direction.REQUEST, tenantObject.getTenantId(), tenantObject, ProcessingOutcome.UNPROCESSABLE, command.getPayloadSize(), commandSample);
log.debug("command message is invalid: {}", command);
commandContext.reject("malformed command message");
TracingHelper.logError(processCommandSpan, "malformed command message");
processCommandSpan.finish();
}
};
final Future<CommandConsumer> commandConsumerFuture;
if (gatewayId != null) {
// gateway scenario
commandConsumerFuture = getCommandConsumerFactory().createCommandConsumer(tenantObject.getTenantId(), deviceId, gatewayId, commandHandler, Duration.ofSeconds(ttdSecs), waitForCommandSpan.context());
} else {
commandConsumerFuture = getCommandConsumerFactory().createCommandConsumer(tenantObject.getTenantId(), deviceId, commandHandler, Duration.ofSeconds(ttdSecs), waitForCommandSpan.context());
}
return commandConsumerFuture.onFailure(thr -> {
TracingHelper.logError(waitForCommandSpan, thr);
waitForCommandSpan.finish();
}).map(consumer -> {
if (!requestProcessed.get()) {
// if the request was not responded already, add a timer for triggering an empty response
addCommandReceptionTimer(ctx, requestProcessed, responseReady, ttdSecs, waitForCommandSpan);
}
// for unregistering the command consumer (which is something the parent request span doesn't wait for)
return new CommandConsumer() {
@Override
public Future<Void> close(final SpanContext ignored) {
final Span closeConsumerSpan = TracingHelper.buildFollowsFromSpan(tracer, waitForCommandSpan.context(), "close consumer").withTag(Tags.COMPONENT.getKey(), getTypeName()).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).withTag(TracingHelper.TAG_TENANT_ID, tenantObject.getTenantId()).withTag(TracingHelper.TAG_DEVICE_ID, deviceId).start();
return consumer.close(closeConsumerSpan.context()).onFailure(thr -> TracingHelper.logError(closeConsumerSpan, thr)).onComplete(ar -> closeConsumerSpan.finish());
}
};
});
}
use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class VertxBasedHttpProtocolAdapter method handlePostCommandResponse.
void handlePostCommandResponse(final RoutingContext ctx) {
if (Device.class.isInstance(ctx.user())) {
final Device device = (Device) ctx.user();
uploadCommandResponseMessage(HttpContext.from(ctx), device.getTenantId(), device.getDeviceId(), getCommandRequestIdParam(ctx), getCommandResponseStatusParam(ctx));
} else {
handle401(ctx);
}
}
use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class VertxBasedHttpProtocolAdapter method handlePostEvent.
void handlePostEvent(final RoutingContext ctx) {
if (Device.class.isInstance(ctx.user())) {
final Device device = (Device) ctx.user();
uploadEventMessage(HttpContext.from(ctx), device.getTenantId(), device.getDeviceId());
} else {
handle401(ctx);
}
}
use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class LoraProtocolAdapter method handleOptionsRoute.
void handleOptionsRoute(final RoutingContext ctx) {
final Span currentSpan = TracingHelper.buildServerChildSpan(tracer, TracingHandler.serverSpanContext(ctx), "process OPTIONS request", getClass().getSimpleName()).start();
if (ctx.user() instanceof Device) {
currentSpan.finish();
// Some providers use OPTIONS request to check if request works. Therefore returning 200.
handle200(ctx);
} else {
logUnsupportedUserType(ctx, currentSpan);
currentSpan.finish();
handle401(ctx);
}
}
use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class AbstractProtocolAdapterBaseTest method testCheckConnectionDurationLimit.
/**
* Verifies that the connection duration check fails if the tenant's connection duration
* limit has been already reached.
*
* @param ctx The vert.x test context.
*/
@Test
public void testCheckConnectionDurationLimit(final VertxTestContext ctx) {
// Given a tenant for which the maximum connection duration usage already exceeds the limit.
final TenantObject tenant = TenantObject.from("tenant", Boolean.TRUE);
final ResourceLimitChecks checks = mock(ResourceLimitChecks.class);
when(checks.isConnectionDurationLimitReached(any(TenantObject.class), any(SpanContext.class))).thenReturn(Future.succeededFuture(Boolean.TRUE));
adapter.setResourceLimitChecks(checks);
// When a device tries to connect
adapter.checkConnectionDurationLimit(tenant, mock(SpanContext.class)).onComplete(ctx.failing(t -> {
// Then the connection duration limit check fails
ctx.verify(() -> {
assertThat(t).isInstanceOf(ConnectionDurationExceededException.class);
assertThat(t).hasMessageThat().isEqualTo(ServiceInvocationException.getLocalizedMessage(ConnectionDurationExceededException.MESSAGE_KEY));
});
ctx.completeNow();
}));
}
Aggregations