use of org.apache.qpid.proton.amqp.messaging.Rejected in project hono by eclipse.
the class AbstractRequestResponseClient method sendRequest.
/**
* Sends a request message via this client's sender link to the peer.
* <p>
* This method first checks if the sender has any credit left. If not, the result handler is failed immediately.
* Otherwise, the request message is sent and a timer is started which fails the result handler,
* if no response is received within <em>requestTimeoutMillis</em> milliseconds.
*
* @param request The message to send.
* @param resultHandler The handler to notify about the outcome of the request.
* @param cacheKey The key to use for caching the response (if the service allows caching).
*/
private final void sendRequest(final Message request, final Handler<AsyncResult<R>> resultHandler, final Object cacheKey) {
context.runOnContext(req -> {
if (sender.sendQueueFull()) {
LOG.debug("cannot send request to peer, no credit left for link [target: {}]", targetAddress);
resultHandler.handle(Future.failedFuture(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "no credit available for sending request")));
} else {
final Object correlationId = Optional.ofNullable(request.getCorrelationId()).orElse(request.getMessageId());
final TriTuple<Handler<AsyncResult<R>>, Object, Object> handler = TriTuple.of(resultHandler, cacheKey, null);
replyMap.put(correlationId, handler);
sender.send(request, deliveryUpdated -> {
if (Rejected.class.isInstance(deliveryUpdated.getRemoteState())) {
final Rejected rejected = (Rejected) deliveryUpdated.getRemoteState();
if (rejected.getError() != null) {
LOG.debug("service did not accept request [target address: {}, subject: {}, correlation ID: {}]: {}", targetAddress, request.getSubject(), correlationId, rejected.getError());
cancelRequest(correlationId, Future.failedFuture(StatusCodeMapper.from(rejected.getError())));
} else {
LOG.debug("service did not accept request [target address: {}, subject: {}, correlation ID: {}]", targetAddress, request.getSubject(), correlationId);
cancelRequest(correlationId, Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST)));
}
} else if (Accepted.class.isInstance(deliveryUpdated.getRemoteState())) {
LOG.trace("service has accepted request [target address: {}, subject: {}, correlation ID: {}]", targetAddress, request.getSubject(), correlationId);
} else {
LOG.debug("service did not accept request [target address: {}, subject: {}, correlation ID: {}]: {}", targetAddress, request.getSubject(), correlationId, deliveryUpdated.getRemoteState());
cancelRequest(correlationId, Future.failedFuture(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE)));
}
});
if (requestTimeoutMillis > 0) {
context.owner().setTimer(requestTimeoutMillis, tid -> {
cancelRequest(correlationId, Future.failedFuture(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "request timed out after " + requestTimeoutMillis + "ms")));
});
}
if (LOG.isDebugEnabled()) {
final String deviceId = MessageHelper.getDeviceId(request);
if (deviceId == null) {
LOG.debug("sent request [target address: {}, subject: {}, correlation ID: {}] to service", targetAddress, request.getSubject(), correlationId);
} else {
LOG.debug("sent request [target address: {}, subject: {}, correlation ID: {}, device ID: {}] to service", targetAddress, request.getSubject(), correlationId, deviceId);
}
}
}
});
}
use of org.apache.qpid.proton.amqp.messaging.Rejected in project hono by eclipse.
the class AbstractRequestResponseClientTest method testCreateAndSendRequestFailsOnRejectedMessage.
/**
* Verifies that the client fails the result handler if the peer rejects
* the request message.
*
* @param ctx The vert.x test context.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void testCreateAndSendRequestFailsOnRejectedMessage(final TestContext ctx) {
// GIVEN a request-response client that times out requests after 200 ms
client.setRequestTimeout(200);
// WHEN sending a request message with some headers and payload
final Async sendFailure = ctx.async();
final JsonObject payload = new JsonObject().put("key", "value");
client.createAndSendRequest("get", null, payload, ctx.asyncAssertFailure(t -> {
sendFailure.complete();
}));
// and the peer rejects the message
final Rejected rejected = new Rejected();
rejected.setError(ProtonHelper.condition("bad-request", "request message is malformed"));
final ProtonDelivery delivery = mock(ProtonDelivery.class);
when(delivery.getRemoteState()).thenReturn(rejected);
final ArgumentCaptor<Handler> dispositionHandlerCaptor = ArgumentCaptor.forClass(Handler.class);
verify(sender).send(any(Message.class), dispositionHandlerCaptor.capture());
dispositionHandlerCaptor.getValue().handle(delivery);
// THEN the result handler is failed
sendFailure.await();
}
use of org.apache.qpid.proton.amqp.messaging.Rejected in project hono by eclipse.
the class EventSenderImplTest method testSendMessageFailsForRejectedOutcome.
/**
* Verifies that the sender fails if the peer does not accept a message.
*
* @param ctx The vert.x test context.
*/
@SuppressWarnings({ "unchecked" })
@Test
public void testSendMessageFailsForRejectedOutcome(final TestContext ctx) {
// GIVEN a sender that has credit
when(sender.sendQueueFull()).thenReturn(Boolean.FALSE);
MessageSender messageSender = new EventSenderImpl(config, sender, "tenant", "telemetry/tenant", context);
final AtomicReference<Handler<ProtonDelivery>> handlerRef = new AtomicReference<>();
doAnswer(invocation -> {
handlerRef.set(invocation.getArgument(1));
return mock(ProtonDelivery.class);
}).when(sender).send(any(Message.class), any(Handler.class));
// WHEN trying to send a message
final Future<ProtonDelivery> result = messageSender.send("device", "some payload", "application/text", "token");
// THEN the message has been sent
// and the result is not completed yet
verify(sender).send(any(Message.class), eq(handlerRef.get()));
assertFalse(result.isComplete());
// and the result fails once the peer rejects the message
ProtonDelivery rejected = mock(ProtonDelivery.class);
when(rejected.remotelySettled()).thenReturn(Boolean.TRUE);
when(rejected.getRemoteState()).thenReturn(new Rejected());
handlerRef.get().handle(rejected);
assertFalse(result.succeeded());
}
use of org.apache.qpid.proton.amqp.messaging.Rejected in project hono by eclipse.
the class MessageForwardingEndpointTest method testProcessMessageRejectsRegistrationAssertionForWrongTenant.
/**
* Verifies that a message containing a registration assertion for a tenant
* other than the one from the message's target address is rejected.
*/
@Test
public void testProcessMessageRejectsRegistrationAssertionForWrongTenant() {
final String invalidToken = getToken(SECRET, "wrong-tenant", "4711");
final UpstreamReceiver client = mock(UpstreamReceiver.class);
final ProtonDelivery delivery = mock(ProtonDelivery.class);
when(tokenValidator.isValid(invalidToken, "tenant", "4711")).thenReturn(Boolean.FALSE);
final MessageForwardingEndpoint<HonoMessagingConfigProperties> endpoint = getEndpoint();
endpoint.setRegistrationAssertionValidator(tokenValidator);
final Message msg = ProtonHelper.message();
MessageHelper.addRegistrationAssertion(msg, invalidToken);
MessageHelper.addAnnotation(msg, MessageHelper.APP_PROPERTY_RESOURCE, "telemetry/tenant/4711");
endpoint.forwardMessage(client, delivery, msg);
verify(delivery).disposition(any(Rejected.class), anyBoolean());
verify(client, never()).close(any(ErrorCondition.class));
}
use of org.apache.qpid.proton.amqp.messaging.Rejected in project hono by eclipse.
the class RequestResponseEndpointTest method testHandleMessageRejectsMalformedMessage.
/**
* Verifies that the endpoint rejects malformed request messages.
*/
@Test
public void testHandleMessageRejectsMalformedMessage() {
Message msg = ProtonHelper.message();
ProtonConnection con = mock(ProtonConnection.class);
ProtonDelivery delivery = mock(ProtonDelivery.class);
RequestResponseEndpoint<ServiceConfigProperties> endpoint = getEndpoint(false);
// WHEN a malformed message is received
endpoint.handleMessage(con, receiver, resource, delivery, msg);
// THEN the link is closed and the message is rejected
ArgumentCaptor<DeliveryState> deliveryState = ArgumentCaptor.forClass(DeliveryState.class);
verify(delivery).disposition(deliveryState.capture(), booleanThat(is(Boolean.TRUE)));
assertThat(deliveryState.getValue(), instanceOf(Rejected.class));
verify(receiver, never()).close();
}
Aggregations