static MessageWrapper BuildMessageWrapper(IOutgoingTransportOperation operation, TimeSpan?timeToBeReceived, string destinationQueue) { var msg = operation.Message; var headers = new Dictionary <string, string>(msg.Headers); var messageIntent = default(MessageIntentEnum); string messageIntentString; if (headers.TryGetValue(Headers.MessageIntent, out messageIntentString)) { Enum.TryParse(messageIntentString, true, out messageIntent); } return(new MessageWrapper { Id = msg.MessageId, Body = msg.Body, CorrelationId = headers.GetValueOrDefault(Headers.CorrelationId), Recoverable = operation.GetDeliveryConstraint <NonDurableDelivery>() == null, ReplyToAddress = headers.GetValueOrDefault(Headers.ReplyToAddress), TimeToBeReceived = timeToBeReceived ?? TimeSpan.MaxValue, Headers = headers, MessageIntent = messageIntent }); }
MessageWrapper BuildMessageWrapper(IOutgoingTransportOperation operation, QueueAddress destinationQueue) { var msg = operation.Message; var headers = new Dictionary <string, string>(msg.Headers); addressing.ApplyMappingOnOutgoingHeaders(headers, destinationQueue); var messageIntent = default(MessageIntentEnum); if (headers.TryGetValue(Headers.MessageIntent, out var messageIntentString)) { Enum.TryParse(messageIntentString, true, out messageIntent); } return(new MessageWrapper { Id = msg.MessageId, Body = msg.Body, CorrelationId = headers.GetValueOrDefault(Headers.CorrelationId), Recoverable = operation.GetDeliveryConstraint <NonDurableDelivery>() == null, ReplyToAddress = headers.GetValueOrDefault(Headers.ReplyToAddress), Headers = headers, MessageIntent = messageIntent }); }
static EventData ToEventData(IOutgoingTransportOperation operation, MessageMetadata meta = null) { var metadata = meta ?? new MessageMetadata(); var timeToBeReceived = operation.DeliveryConstraints.OfType <DiscardIfNotReceivedBefore>().FirstOrDefault(); if (timeToBeReceived != null && timeToBeReceived.MaxTime != TimeSpan.Zero) { metadata.TimeToBeReceived = DateTime.UtcNow + timeToBeReceived.MaxTime; } metadata.MessageId = operation.Message.MessageId; metadata.Headers = operation.Message.Headers; var type = operation.Message.Headers.ContainsKey(Headers.ControlMessageHeader) ? "ControlMessage" : operation.Message.Headers[Headers.EnclosedMessageTypes]; byte[] data; string contentType; if (operation.Message.Headers.TryGetValue(Headers.ContentType, out contentType)) { data = contentType != ContentTypes.Json ? Encoding.UTF8.GetBytes(Convert.ToBase64String(operation.Message.Body)) : operation.Message.Body; } else { data = new byte[0]; metadata.Empty = true; } return(new EventData(Guid.NewGuid(), type, true, data, metadata.ToJsonBytes())); }
/// <summary> /// Gets the operation intent from the headers. /// </summary> /// <param name="operation">The operation.</param> /// <returns>The operation intent.</returns> public static MessageIntentEnum GetMessageIntent(this IOutgoingTransportOperation operation) { var messageIntent = default(MessageIntentEnum); if (operation.Message.Headers.TryGetValue(Headers.MessageIntent, out var messageIntentString)) { Enum.TryParse(messageIntentString, true, out messageIntent); } return(messageIntent); }
MessageWrapper BuildMessageWrapper(IOutgoingTransportOperation operation, QueueAddress destinationQueue) { var msg = operation.Message; var headers = new Dictionary<string, string>(msg.Headers); addressing.ApplyMappingOnOutgoingHeaders(headers, destinationQueue); return new MessageWrapper { Id = msg.MessageId, Body = msg.Body, CorrelationId = headers.GetValueOrDefault(Headers.CorrelationId), // TODO: Will be addresses in another PR // Recoverable = operation.GetDeliveryConstraint<NonDurableDelivery>() == null, ReplyToAddress = headers.GetValueOrDefault(Headers.ReplyToAddress), Headers = headers, MessageIntent = operation.GetMessageIntent() }; }
static void ApplyCustomizationToOutgoingNativeMessage(IOutgoingTransportOperation transportOperation, ServiceBusMessage message, TransportTransaction transportTransaction) { if (!transportOperation.Properties.TryGetValue(NativeMessageCustomizationBehavior.CustomizationKey, out var key)) { return; } var messageCustomizer = transportTransaction.Get <NativeMessageCustomizer>(); if (!messageCustomizer.Customizations.TryGetValue(key, out var action)) { Log.Warn( $"Message {transportOperation.Message.MessageId} was configured with a native message customization but the customization was not found in {nameof(NativeMessageCustomizer)}"); return; } action(message); }
static bool TryProcessDelayedRetry(IOutgoingTransportOperation operation, out UnicastTransportOperation operationToSchedule, out DateTimeOffset scheduleDate) { var messageHeaders = operation.Message.Headers; if (messageHeaders.TryGetValue(TimeoutManagerHeaders.Expire, out var expire)) { var expiration = DateTimeExtensions.ToUtcDateTime(expire); var destination = messageHeaders[TimeoutManagerHeaders.RouteExpiredTimeoutTo]; messageHeaders.Remove(TimeoutManagerHeaders.Expire); messageHeaders.Remove(TimeoutManagerHeaders.RouteExpiredTimeoutTo); operationToSchedule = new UnicastTransportOperation(operation.Message, destination, operation.RequiredDispatchConsistency, operation.DeliveryConstraints); scheduleDate = expiration; return(true); } operationToSchedule = null; scheduleDate = default(DateTimeOffset); return(false); }
static bool TryGetConstraint <T>(IOutgoingTransportOperation operation, out T constraint) where T : DeliveryConstraint { constraint = operation.DeliveryConstraints.OfType <T>().FirstOrDefault(); return(constraint != null); }
async Task WriteMessage(string destination, IOutgoingTransportOperation transportOperation, TransportTransaction transaction) { var message = transportOperation.Message; var headerPayload = HeaderSerializer.Serialize(message.Headers); var headerSize = Encoding.UTF8.GetByteCount(headerPayload); if (headerSize + message.Body.Length > maxMessageSizeKB * 1024) { throw new Exception($"The total size of the '{message.Headers[Headers.EnclosedMessageTypes]}' message body ({message.Body.Length} bytes) plus headers ({headerSize} bytes) is larger than {maxMessageSizeKB} KB and will not be supported on some production transports. Consider using the NServiceBus DataBus or the claim check pattern to avoid messages with a large payload. Use 'EndpointConfiguration.UseTransport<LearningTransport>().NoPayloadSizeRestriction()' to disable this check and proceed with the current message size."); } var nativeMessageId = Guid.NewGuid().ToString(); var destinationPath = Path.Combine(basePath, destination); var bodyDir = Path.Combine(destinationPath, LearningTransportMessagePump.BodyDirName); Directory.CreateDirectory(bodyDir); var bodyPath = Path.Combine(bodyDir, nativeMessageId) + LearningTransportMessagePump.BodyFileSuffix; await AsyncFile.WriteBytes(bodyPath, message.Body) .ConfigureAwait(false); DateTime?timeToDeliver = null; if (transportOperation.DeliveryConstraints.TryGet(out DoNotDeliverBefore doNotDeliverBefore)) { timeToDeliver = doNotDeliverBefore.At; } else if (transportOperation.DeliveryConstraints.TryGet(out DelayDeliveryWith delayDeliveryWith)) { timeToDeliver = DateTime.UtcNow + delayDeliveryWith.Delay; } if (timeToDeliver.HasValue) { if (transportOperation.DeliveryConstraints.TryGet(out DiscardIfNotReceivedBefore timeToBeReceived) && timeToBeReceived.MaxTime < TimeSpan.MaxValue) { throw new Exception($"Postponed delivery of messages with TimeToBeReceived set is not supported. Remove the TimeToBeReceived attribute to postpone messages of type '{message.Headers[Headers.EnclosedMessageTypes]}'."); } // we need to "ceil" the seconds to guarantee that we delay with at least the requested value // since the folder name has only second resolution. if (timeToDeliver.Value.Millisecond > 0) { timeToDeliver += TimeSpan.FromSeconds(1); } destinationPath = Path.Combine(destinationPath, LearningTransportMessagePump.DelayedDirName, timeToDeliver.Value.ToString("yyyyMMddHHmmss")); Directory.CreateDirectory(destinationPath); } var messagePath = Path.Combine(destinationPath, nativeMessageId) + ".metadata.txt"; if (transportOperation.RequiredDispatchConsistency != DispatchConsistency.Isolated && transaction.TryGet(out ILearningTransportTransaction directoryBasedTransaction)) { await directoryBasedTransaction.Enlist(messagePath, headerPayload) .ConfigureAwait(false); } else { // atomic avoids the file being locked when the receiver tries to process it await AsyncFile.WriteTextAtomic(messagePath, headerPayload) .ConfigureAwait(false); } }
public static T GetDeliveryConstraint <T>(this IOutgoingTransportOperation operation) where T : DeliveryConstraint { return(operation.DeliveryConstraints.OfType <T>().FirstOrDefault()); }
async Task <TMessage> PrepareMessage <TMessage>(IOutgoingTransportOperation transportOperation, HashSet <string> messageIdsOfMulticastedEvents) where TMessage : PreparedMessage, new() { var unicastTransportOperation = transportOperation as UnicastTransportOperation; // these conditions are carefully chosen to only execute the code if really necessary if (unicastTransportOperation != null && messageIdsOfMulticastedEvents.Contains(unicastTransportOperation.Message.MessageId) && unicastTransportOperation.Message.GetMessageIntent() == MessageIntentEnum.Publish && unicastTransportOperation.Message.Headers.ContainsKey(Headers.EnclosedMessageTypes)) { var mostConcreteEnclosedMessageType = unicastTransportOperation.Message.GetEnclosedMessageTypes()[0]; var existingTopic = await topicCache.GetTopicArn(mostConcreteEnclosedMessageType).ConfigureAwait(false); if (existingTopic != null) { var matchingSubscriptionArn = await snsClient.FindMatchingSubscription(queueCache, existingTopic, unicastTransportOperation.Destination) .ConfigureAwait(false); if (matchingSubscriptionArn != null) { return(null); } } } var delayDeliveryWith = transportOperation.DeliveryConstraints.OfType <DelayDeliveryWith>().SingleOrDefault(); var doNotDeliverBefore = transportOperation.DeliveryConstraints.OfType <DoNotDeliverBefore>().SingleOrDefault(); long delaySeconds = 0; if (delayDeliveryWith != null) { delaySeconds = Convert.ToInt64(Math.Ceiling(delayDeliveryWith.Delay.TotalSeconds)); } else if (doNotDeliverBefore != null) { delaySeconds = Convert.ToInt64(Math.Ceiling((doNotDeliverBefore.At - DateTime.UtcNow).TotalSeconds)); } if (!configuration.IsDelayedDeliveryEnabled && delaySeconds > TransportConfiguration.AwsMaximumQueueDelayTime) { throw new NotSupportedException($"To send messages with a delay time greater than '{TimeSpan.FromSeconds(TransportConfiguration.AwsMaximumQueueDelayTime)}', call '.UseTransport<SqsTransport>().UnrestrictedDurationDelayedDelivery()'."); } var sqsTransportMessage = new TransportMessage(transportOperation.Message, transportOperation.DeliveryConstraints); var serializedMessage = SimpleJson.SerializeObject(sqsTransportMessage, serializerStrategy); var messageId = transportOperation.Message.MessageId; var preparedMessage = new TMessage(); await ApplyUnicastOperationMappingIfNecessary(unicastTransportOperation, preparedMessage as SqsPreparedMessage, delaySeconds, messageId).ConfigureAwait(false); await ApplyMulticastOperationMappingIfNecessary(transportOperation as MulticastTransportOperation, preparedMessage as SnsPreparedMessage).ConfigureAwait(false); preparedMessage.Body = serializedMessage; preparedMessage.MessageId = messageId; preparedMessage.CalculateSize(); if (preparedMessage.Size <= TransportConfiguration.MaximumMessageSize) { return(preparedMessage); } if (string.IsNullOrEmpty(configuration.S3BucketForLargeMessages)) { throw new Exception("Cannot send large message because no S3 bucket was configured. Add an S3 bucket name to your configuration."); } var key = $"{configuration.S3KeyPrefix}/{messageId}"; using (var bodyStream = new MemoryStream(transportOperation.Message.Body)) { var putObjectRequest = new PutObjectRequest { BucketName = configuration.S3BucketForLargeMessages, InputStream = bodyStream, Key = key }; ApplyServerSideEncryptionConfiguration(putObjectRequest); await s3Client.PutObjectAsync(putObjectRequest).ConfigureAwait(false); } sqsTransportMessage.S3BodyKey = key; sqsTransportMessage.Body = string.Empty; preparedMessage.Body = SimpleJson.SerializeObject(sqsTransportMessage, serializerStrategy); preparedMessage.CalculateSize(); return(preparedMessage); }