Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/DurableTask.AzureStorage/MessageManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,9 @@ private async Task<string> DownloadAndDecompressAsBytesAsync(Blob blob, Cancella

public string GetBlobUrl(string blobName)
{
return Uri.UnescapeDataString(this.blobContainer.GetBlobReference(blobName).Uri.AbsoluteUri);
string baseUri = this.blobContainer.GetBlobContainerUri().ToString();

return $"{baseUri}/{EscapeBlobNamePreservingSlashes(blobName)}";
}

public MessageFormatFlags GetMessageFormatFlags(MessageData messageData)
Expand Down Expand Up @@ -304,6 +306,12 @@ public async Task<int> DeleteLargeMessageBlobs(string sanitizedInstanceId, Cance

return storageOperationCount;
}

/// Escapes a blob name for safe inclusion in a URI while preserving path structure.
static string EscapeBlobNamePreservingSlashes(string blobName)
{
return string.Join("/", blobName.Split('/').Select(Uri.EscapeDataString));
}
}

#if NETSTANDARD2_0
Expand Down
5 changes: 5 additions & 0 deletions src/DurableTask.AzureStorage/Storage/BlobContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ public Task RenewLeaseAsync(string leaseId, CancellationToken cancellationToken
.DecorateFailure();
}

public Uri GetBlobContainerUri()
{
return this.blobContainerClient.Uri;
}

static bool IsHnsFolder(BlobItem item)
{
// Check the optional "hdi_isfolder" value in the metadata to determine whether
Expand Down
29 changes: 29 additions & 0 deletions test/DurableTask.AzureStorage.Tests/AzureStorageScenarioTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1600,6 +1600,35 @@ await ValidateLargeMessageBlobUrlAsync(
}
}

/// <summary>
/// End-to-end test that validates large (>60KB) messages stored in blob storage can be retrieved successfully,
/// when the instance ID includes special characters like '|' that can affect blob URL encoding.
/// </summary>
[TestMethod]
public async Task LargeMessage_WithEscapedInstanceId_CanBeStoredAndFetchedSuccessfully()
{
// Genereates a random large message.
const int largeMessageSize = 60 * 1024;

using (TestOrchestrationHost host = TestHelpers.GetTestOrchestrationHost(enableExtendedSessions: false))
{
await host.StartAsync();

string message = this.GenerateMediumRandomStringPayload(largeMessageSize, utf8ByteSize: 1, utf16ByteSize: 2).ToString();

// Use an instanceId that contains special characters which must be escaped in URIs
string id = "test|123:with white spcae";
var client = await host.StartOrchestrationAsync(typeof(Orchestrations.Echo), input:message, instanceId: id);
var status = await client.WaitForCompletionAsync(TimeSpan.FromMinutes(2));

Assert.AreEqual(OrchestrationStatus.Completed, status?.OrchestrationStatus);

// Verify that the output matches the original message (the blob was successfully downloaded and not returned as a URL)
StringAssert.Contains(status.Output.ToString(), message);
await host.StopAsync();
}
}

[TestMethod]
public async Task TagsAreAvailableInOrchestrationState()
{
Expand Down
15 changes: 8 additions & 7 deletions test/DurableTask.AzureStorage.Tests/MessageManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@
#nullable enable
namespace DurableTask.AzureStorage.Tests
{
using System;
using System.Collections.Generic;
using DurableTask.AzureStorage.Storage;
using DurableTask.Core.History;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;

[TestClass]
public class MessageManagerTests
Expand Down Expand Up @@ -70,10 +70,10 @@ public void DeserializesCustomTypes()
}

[DataTestMethod]
[DataRow("blob.bin")]
[DataRow("@#$%!")]
[DataRow("foo/bar/[email protected]")]
public void GetBlobUrlUnescaped(string blob)
[DataRow("blob.bin", "blob.bin")]
[DataRow("@#$%!", "%40%23%24%25%21")]
[DataRow("foo/bar/[email protected]", "foo/bar/b%40z.tar.gz")]
public void GetBlobUrlEscaped(string blob, string blobUrl)
{
var settings = new AzureStorageOrchestrationServiceSettings
{
Expand All @@ -82,7 +82,8 @@ public void GetBlobUrlUnescaped(string blob)

const string container = "@entity12345";
var manager = new MessageManager(settings, new AzureStorageClient(settings), container);
var expected = $"http://127.0.0.1:10000/devstoreaccount1/{container}/{blob}";

var expected = $"http://127.0.0.1:10000/devstoreaccount1/{container}/{blobUrl}";
Assert.AreEqual(expected, manager.GetBlobUrl(blob));
}

Expand Down
Loading