Skip to content
Open
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
18 changes: 10 additions & 8 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,20 @@
<PackageVersion Include="Azure.Storage.Queues" Version="12.22.0" />
<PackageVersion Include="Castle.Core" Version="5.2.1" />
<PackageVersion Include="ImpromptuInterface" Version="6.2.2" Condition="'$(TargetFramework)' == 'net48' OR '$(TargetFramework)' == 'net472'" />
<PackageVersion Include="ImpromptuInterface" Version="7.0.1" Condition="'$(TargetFramework)' == 'netstandard2.0'" />
<PackageVersion Include="ImpromptuInterface" Version="7.0.1" Condition="'$(TargetFramework)' != 'net48' AND '$(TargetFramework)' != 'net472'" />
<PackageVersion Include="Microsoft.ApplicationInsights" Version="2.21.0" />
<PackageVersion Include="Microsoft.AspNet.WebApi.OwinSelfHost" Version="5.3.0" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.0" Condition="'$(TargetFramework)' == 'net8.0' OR '$(TargetFramework)' == 'net10.0'" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
<PackageVersion Include="Microsoft.CSharp" Version="4.7.0" />
<PackageVersion Include="Microsoft.Data.Services.Client" Version="5.8.5" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="2.1.1" />
<PackageVersion Include="Microsoft.ServiceFabric" Version="6.4.617" />
<PackageVersion Include="Microsoft.ServiceFabric.Data" Version="3.3.617" />
<PackageVersion Include="Microsoft.ServiceFabric.Services" Version="3.3.617" />
<PackageVersion Include="Microsoft.ServiceFabric" Version="11.3.475" />
<PackageVersion Include="Microsoft.ServiceFabric.AspNetCore.Kestrel" Version="8.3.475" />
<PackageVersion Include="Microsoft.ServiceFabric.Data" Version="8.3.475" />
<PackageVersion Include="Microsoft.ServiceFabric.Services" Version="8.3.475" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="1.0.0" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.1" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="System.Collections.Immutable" Version="1.2.0" />
<PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="6.0.1" />
<PackageVersion Include="System.Linq.Async" Version="6.0.1" />
Expand Down Expand Up @@ -101,13 +103,13 @@
<PackageVersion Include="Vio.DurableTask.Hosting" Version="2.2.1" />
</ItemGroup>

<!-- Dependencies specific to net8.0-->
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<!-- Dependencies specific to net8.0 or later -->
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0' OR '$(TargetFramework)' == 'net10.0'">
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net8.0'">
<ItemGroup Condition="'$(TargetFramework)' != 'net8.0' AND '$(TargetFramework)' != 'net10.0'">
<PackageVersion Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.1" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory),DurableTask.sln))\tools\DurableTask.props" />
<PropertyGroup>
<TargetFrameworks>net48;net472</TargetFrameworks>
<TargetFrameworks>net48;net472;net8.0;net10.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PackageId>Microsoft.Azure.DurableTask.AzureServiceFabric</PackageId>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
Expand All @@ -16,23 +16,26 @@
<NoWarn>$(NoWarn);NU5104</NoWarn>
</PropertyGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net48'">
<!-- .NET Framework: System.Web reference -->
<ItemGroup Condition="'$(TargetFramework)' == 'net48' OR '$(TargetFramework)' == 'net472'">
<Reference Include="System.Web" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net472'">
<Reference Include="System.Web" />
<!-- .NET Framework: OWIN + Web API -->
<ItemGroup Condition="'$(TargetFramework)' == 'net48' OR '$(TargetFramework)' == 'net472'">
<PackageReference Include="Microsoft.AspNet.WebApi.OwinSelfHost" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<!-- Restoring packages for netstandard2.0 causes warnings. As warnings are treated as errors, compilation will fail. -->
<!-- Once the packages support netstandard2.0, this project will support netstandard2.0. -->
<!-- Modern .NET: ASP.NET Core + Kestrel for Service Fabric -->
<ItemGroup Condition="'$(TargetFramework)' != 'net48' AND '$(TargetFramework)' != 'net472'">
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" />
<PackageReference Include="Microsoft.ServiceFabric.AspNetCore.Kestrel" />
<PackageReference Include="Newtonsoft.Json" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="ImpromptuInterface" />
<PackageReference Include="Microsoft.AspNet.WebApi.OwinSelfHost" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
<PackageReference Include="Microsoft.ServiceFabric.Data" />
<PackageReference Include="Microsoft.ServiceFabric.Services" />
Expand Down
5 changes: 4 additions & 1 deletion src/DurableTask.AzureServiceFabric/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@
namespace DurableTask.AzureServiceFabric
{
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
#if NETFRAMEWORK
using System.Web;
#endif

using DurableTask.AzureServiceFabric.Exceptions;

Expand All @@ -38,7 +41,7 @@ public static bool IsValidInstanceId(this string instanceId)
}

// InsanceId consists of valid url characters is treated as valid.
var encodedInstanceId = HttpUtility.UrlEncode(instanceId);
var encodedInstanceId = WebUtility.UrlEncode(instanceId);

return instanceId.Equals(encodedInstanceId, StringComparison.OrdinalIgnoreCase);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public Task<long> GeneratePartitionHashCodeAsync(string value, CancellationToken
long hashCode = 0;
if (!string.IsNullOrEmpty(value))
{
using (var sha256 = SHA256Managed.Create())
using (var sha256 = SHA256.Create())
{
var bytes = Encoding.UTF8.GetBytes(value);
var hash = sha256.ComputeHash(bytes);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;

using DurableTask.Core;
using DurableTask.Core.Exceptions;
Expand All @@ -31,7 +30,6 @@

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Web.Http.Results;

/// <summary>
/// Allows to interact with a remote IOrchestrationServiceClient
Expand Down Expand Up @@ -115,7 +113,7 @@
{
instanceId.EnsureValidInstanceId();

var fragment = $"{this.GetOrchestrationFragment(instanceId)}?reason={HttpUtility.UrlEncode(reason)}";
var fragment = $"{this.GetOrchestrationFragment(instanceId)}?reason={WebUtility.UrlEncode(reason)}";
using (var response = await this.ExecuteRequestWithRetriesAsync(
instanceId,
async (baseUri) => await this.HttpClient.DeleteAsync(new Uri(baseUri, fragment)),
Expand Down Expand Up @@ -193,7 +191,11 @@
foreach (var endpoint in await this.GetAllEndpointsAsync(CancellationToken.None))
{
var uri = $"{endpoint.ToString()}/{GetHistoryFragment()}";
var task = this.HttpClient.PostAsJsonAsync(uri, new PurgeOrchestrationHistoryParameters { ThresholdDateTimeUtc = thresholdDateTimeUtc, TimeRangeFilterType = timeRangeFilterType });
var task = this.HttpClient.PostAsync(uri,
new StringContent(
JsonConvert.SerializeObject(new PurgeOrchestrationHistoryParameters { ThresholdDateTimeUtc = thresholdDateTimeUtc, TimeRangeFilterType = timeRangeFilterType }),
Encoding.UTF8,
"application/json"));
allTasks.Add(task);
}

Expand Down Expand Up @@ -303,21 +305,26 @@

private async Task PutJsonAsync(string instanceId, string fragment, object @object, CancellationToken cancellationToken)
{
var mediaFormatter = new JsonMediaTypeFormatter()
{
SerializerSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }
};
var jsonSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
var jsonContent = new StringContent(
JsonConvert.SerializeObject(@object, jsonSettings),

Check warning

Code scanning / CodeQL

Cross-site scripting Medium

User-provided value
flows to here and is written to HTML or JavaScript.
User-provided value
flows to here and is written to HTML or JavaScript.
User-provided value
flows to here and is written to HTML or JavaScript.
User-provided value
flows to here and is written to HTML or JavaScript.
Encoding.UTF8,
"application/json");

using (var result = await this.ExecuteRequestWithRetriesAsync(
instanceId,
async (baseUri) => await this.HttpClient.PutAsync(new Uri(baseUri, fragment), @object, mediaFormatter),
async (baseUri) => await this.HttpClient.PutAsync(new Uri(baseUri, fragment), jsonContent),
cancellationToken))
{

// TODO: Improve exception handling
if (result.StatusCode == HttpStatusCode.Conflict)
{
throw await (result.Content?.ReadAsAsync<OrchestrationAlreadyExistsException>() ?? Task.FromResult(new OrchestrationAlreadyExistsException()));
var errorContent = await (result.Content?.ReadAsStringAsync() ?? Task.FromResult<string>(null));
throw errorContent != null
? JsonConvert.DeserializeObject<OrchestrationAlreadyExistsException>(errorContent)
?? new OrchestrationAlreadyExistsException()
: new OrchestrationAlreadyExistsException();
}

if (!result.IsSuccessStatusCode)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// limitations under the License.
// ----------------------------------------------------------------------------------

#if NETFRAMEWORK
namespace DurableTask.AzureServiceFabric.Service
{
using System;
Expand Down Expand Up @@ -47,3 +48,4 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
}
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// ----------------------------------------------------------------------------------
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------

#if !NETFRAMEWORK
namespace DurableTask.AzureServiceFabric.Service
{
using System;
using System.Net;
using System.Threading.Tasks;

using DurableTask.AzureServiceFabric.Tracing;

using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;

/// <summary>
/// ASP.NET Core middleware for activity logging of proxy service requests.
/// </summary>
internal class ActivityLoggingMiddleware
{
private readonly RequestDelegate next;

public ActivityLoggingMiddleware(RequestDelegate next)
{
this.next = next;
}

public async Task InvokeAsync(HttpContext context)
{
string requestMethod = context.Request.Method;
string requestPath = context.Request.Path.ToString();
string activityId = Guid.NewGuid().ToString("D");

ServiceFabricProviderEventSource.Tracing.LogProxyServiceRequestInformation(
$"{activityId} : Proxy service incoming request {requestPath} with method {requestMethod}");

try
{
await this.next(context);
ServiceFabricProviderEventSource.Tracing.LogProxyServiceRequestInformation(
$"{activityId} : Proxy service responding request {requestPath} with method {requestMethod} with status code {context.Response.StatusCode}");
}
catch (Exception exception)
{
ServiceFabricProviderEventSource.Tracing.LogProxyServiceError(activityId, requestPath, requestMethod, exception);
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(exception));
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On exception, the middleware serializes and returns the raw Exception object to the caller. This leaks stack traces / internal details and can produce very large or non-serializable payloads. Consider returning a sanitized error shape (e.g., message + activityId) and keep full exception details only in logs.

Suggested change
await context.Response.WriteAsync(JsonConvert.SerializeObject(exception));
var errorResponse = new
{
message = "An internal server error occurred.",
activityId = activityId
};
await context.Response.WriteAsync(JsonConvert.SerializeObject(errorResponse));

Copilot uses AI. Check for mistakes.
}

context.Response.Headers[Constants.ActivityIdHeaderName] = activityId;
}
Comment on lines +61 to +62
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ActivityId response header is set after the request pipeline runs. In ASP.NET Core the response may have already started (either in the normal path or after writing the error body), which can throw and/or silently drop the header. Register the header via context.Response.OnStarting(...) (or set it before calling next) so it’s guaranteed to be included.

Copilot uses AI. Check for mistakes.
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// limitations under the License.
// ----------------------------------------------------------------------------------

#if NETFRAMEWORK
namespace DurableTask.AzureServiceFabric.Service
{
using System;
Expand Down Expand Up @@ -71,3 +72,4 @@ public void Dispose()
#endregion
}
}
#endif
Loading
Loading