Skip to content

Commit

Permalink
Fix .NET 8 compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
Philippe Lécaillon committed Jan 15, 2025
1 parent 0b69941 commit 5caf814
Show file tree
Hide file tree
Showing 13 changed files with 338 additions and 368 deletions.
15 changes: 2 additions & 13 deletions Cassette.sln
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28922.388
# Visual Studio Version 17
VisualStudioVersion = 17.12.35707.178 d17.12
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cassette", "src\Cassette\Cassette.csproj", "{B46DF27A-9DEF-44F7-AB0F-1AF20F810EEE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cassette.Tests", "test\Cassette.Tests\Cassette.Tests.csproj", "{6471FBA4-D2E8-482B-8147-306D7545EA69}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{DB3B6B50-50BA-46C5-8283-A0F9CAB0E77F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetCore.HttpClientFactory.QuickStart", "samples\AspNetCore.HttpClientFactory.QuickStart\AspNetCore.HttpClientFactory.QuickStart.csproj", "{215AAACA-830E-46E8-BF6B-59F6425A67AF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -25,17 +21,10 @@ Global
{6471FBA4-D2E8-482B-8147-306D7545EA69}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6471FBA4-D2E8-482B-8147-306D7545EA69}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6471FBA4-D2E8-482B-8147-306D7545EA69}.Release|Any CPU.Build.0 = Release|Any CPU
{215AAACA-830E-46E8-BF6B-59F6425A67AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{215AAACA-830E-46E8-BF6B-59F6425A67AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{215AAACA-830E-46E8-BF6B-59F6425A67AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{215AAACA-830E-46E8-BF6B-59F6425A67AF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{215AAACA-830E-46E8-BF6B-59F6425A67AF} = {DB3B6B50-50BA-46C5-8283-A0F9CAB0E77F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {074568C1-D794-4AF0-91E0-E5D733D2E92D}
EndGlobalSection
Expand Down
6 changes: 3 additions & 3 deletions build/common.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<Version>1.1.0</Version>
<AssemblyVersion>1.1.0.0</AssemblyVersion>
<FileVersion>1.1.0.0</FileVersion>
<Version>1.1.1</Version>
<AssemblyVersion>1.1.1.0</AssemblyVersion>
<FileVersion>1.1.1.0</FileVersion>
</PropertyGroup>
</Project>
172 changes: 79 additions & 93 deletions src/Cassette/Cassette.cs
Original file line number Diff line number Diff line change
@@ -1,125 +1,111 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

namespace Cassette
namespace Cassette;

[Serializable]
internal class Cassette
{
[Serializable]
internal class Cassette
public Request Request { get; set; }
public Response Response { get; set; }
public DateTimeOffset RecordedAt { get; set; }

public byte[] ToByteArray()
{
public Request Request { get; set; }
public Response Response { get; set; }
public DateTimeOffset RecordedAt { get; set; }
return JsonSerializer.SerializeToUtf8Bytes(this);
}

public byte[] ToByteArray()
{
var formatter = new BinaryFormatter();
using (var stream = new MemoryStream())
{
formatter.Serialize(stream, this);
return stream.ToArray();
}
}
public static async Task<Cassette> Record(HttpRequestMessage httpRequest, HttpResponseMessage httpResponse)
{
ArgumentNullException.ThrowIfNull(httpRequest);
ArgumentNullException.ThrowIfNull(httpResponse);

public static async Task<Cassette> Record(HttpRequestMessage httpRequest, HttpResponseMessage httpResponse)
return new Cassette
{
if (httpRequest is null)
{
throw new ArgumentNullException(nameof(httpRequest));
}

if (httpResponse is null)
{
throw new ArgumentNullException(nameof(httpResponse));
}
Request = await httpRequest.ToRequest(),
Response = await httpResponse.ToResponse(),
RecordedAt = DateTimeOffset.UtcNow
};
}

return new Cassette
{
Request = await httpRequest.ToRequest(),
Response = await httpResponse.ToResponse(),
RecordedAt = DateTimeOffset.UtcNow
};
public HttpResponseMessage Replay()
{
var httpResponse = new HttpResponseMessage
{
StatusCode = Response.Status,
Version = Response.Version,
ReasonPhrase = Response.ReasonPhrase,
RequestMessage = Request.ToHttpRequestMessage(),
Content = Response.Body.ToHttpContent()
};

foreach (var header in Response.Headers)
{
httpResponse.Headers.TryAddWithoutValidation(header.Key, header.Value);
}

public HttpResponseMessage Replay()
if (httpResponse.Content != null)
{
var httpResponse = new HttpResponseMessage
foreach (var header in Response.ContentHeaders)
{
StatusCode = Response.Status,
Version = Response.Version,
ReasonPhrase = Response.ReasonPhrase,
RequestMessage = Request.ToHttpRequestMessage(),
Content = Response.Body.ToHttpContent()
};

foreach (var header in Response.Headers)
{
httpResponse.Headers.TryAddWithoutValidation(header.Key, header.Value);
httpResponse.Content.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
}

if (httpResponse.Content != null)
{
foreach (var header in Response.ContentHeaders)
{
httpResponse.Content.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
}
return httpResponse;
}

return httpResponse;
public static string GetKey(Request request, CassetteOptions options)
{
if (request.Headers.ContainsKey(CassetteOptions.NoRecord))
{
return null;
}

public static string GetKey(Request request, CassetteOptions options)
{
if (request.Headers.ContainsKey(CassetteOptions.NoRecord))
{
return null;
}
string requestMethod = request.Method;
string requestUri = request.Headers.ContainsKey(CassetteOptions.ExcludeLastUriSegment) ? request.GetUriWithoutLastSegment() : request.Uri;

string requestMethod = request.Method;
string requestUri = request.Headers.ContainsKey(CassetteOptions.ExcludeLastUriSegment) ? request.GetUriWithoutLastSegment() : request.Uri;
using (var hasher = IncrementalHash.CreateHash(HashAlgorithmName.SHA1))
{
hasher.AppendData(Encoding.UTF8.GetBytes(requestMethod + requestUri));

using (var hasher = IncrementalHash.CreateHash(HashAlgorithmName.SHA1))
if (request.Body != null && !request.Headers.ContainsKey(CassetteOptions.ExcludeRequestBody))
{
hasher.AppendData(Encoding.UTF8.GetBytes(requestMethod + requestUri));

if (request.Body != null && !request.Headers.ContainsKey(CassetteOptions.ExcludeRequestBody))
{
hasher.AppendData(request.Body);
}

return options.KeyPrefix is null ? "" : options.KeyPrefix + options.KeySeparator
+ requestMethod + options.KeySeparator
+ requestUri.Replace("http://", "http//").Replace("https://", "https//") + options.KeySeparator
+ Convert.ToBase64String(hasher.GetHashAndReset());
hasher.AppendData(request.Body);
}

return options.KeyPrefix is null ? "" : options.KeyPrefix + options.KeySeparator
+ requestMethod + options.KeySeparator
+ requestUri.Replace("http://", "http//").Replace("https://", "https//") + options.KeySeparator
+ Convert.ToBase64String(hasher.GetHashAndReset());
}
}
}

[Serializable]
internal class Request
{
public string Method { get; set; }
public string Uri { get; set; }
public Dictionary<string, IEnumerable<string>> Headers { get; set; }
public Dictionary<string, IEnumerable<string>> ContentHeaders { get; set; }
public byte[] Body { get; set; }
public Version Version { get; set; }
}
[Serializable]
internal class Request
{
public string Method { get; set; }
public string Uri { get; set; }
public Dictionary<string, IEnumerable<string>> Headers { get; set; }
public Dictionary<string, IEnumerable<string>> ContentHeaders { get; set; }
public byte[] Body { get; set; }
public Version Version { get; set; }
}

[Serializable]
internal class Response
{
public HttpStatusCode Status { get; set; }
public string ReasonPhrase { get; set; }
public Dictionary<string, IEnumerable<string>> Headers { get; set; }
public Dictionary<string, IEnumerable<string>> ContentHeaders { get; set; }
public byte[] Body { get; set; }
public Version Version { get; set; }
}
[Serializable]
internal class Response
{
public HttpStatusCode Status { get; set; }
public string ReasonPhrase { get; set; }
public Dictionary<string, IEnumerable<string>> Headers { get; set; }
public Dictionary<string, IEnumerable<string>> ContentHeaders { get; set; }
public byte[] Body { get; set; }
public Version Version { get; set; }
}
11 changes: 4 additions & 7 deletions src/Cassette/Cassette.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,16 @@
<Import Project="..\..\build\common.props" />

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<LangVersion>latest</LangVersion>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="2.1.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Http" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Options" Version="2.1.1" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-19270-01">
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
16 changes: 5 additions & 11 deletions src/Cassette/Cassette.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,14 @@
<repository type="git" url="https://github.com/lecaillon/Cassette.git" />
<iconUrl>https://raw.githubusercontent.com/lecaillon/Cassette/master/images/logo128.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<releaseNotes>- #23 Change to work with non-text HTTP requests and responses</releaseNotes>
<releaseNotes>- Fix .NET 8 compatibility</releaseNotes>
<tags>cassette cassette.http integration-tests continuous-integration httpclient middleware message-handler cache distributed-cache</tags>
<dependencies>
<group targetFramework="netstandard2.0">
<dependency id="Microsoft.Extensions.Caching.Abstractions" version="2.1.2" />
<dependency id="Microsoft.Extensions.DependencyInjection.Abstractions" version="2.1.1" />
<dependency id="Microsoft.Extensions.Http" version="2.1.1" />
<dependency id="Microsoft.Extensions.Logging" version="2.1.1" />
<dependency id="Microsoft.Extensions.Options" version="2.1.1" />
</group>
<group targetFramework="net8.0" />
</dependencies>
</metadata>
<files>
<file src="bin\Release\netstandard2.0\Cassette.dll" target="lib\netstandard2.0" />
<file src="bin\Release\netstandard2.0\Cassette.pdb" target="lib\netstandard2.0" />
<file src="bin\Release\net8.0\Cassette.dll" target="lib\net8.0" />
<file src="bin\Release\net8.0\Cassette.pdb" target="lib\net8.0" />
</files>
</package>
</package>
65 changes: 29 additions & 36 deletions src/Cassette/CassetteConfigurationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,51 +3,44 @@
using Cassette;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace Microsoft.Extensions.DependencyInjection
namespace Microsoft.Extensions.DependencyInjection;

/// <summary>
/// Extensions methods for configuring Cassette.
/// </summary>
public static class CassetteConfigurationExtensions
{
/// <summary>
/// Extensions methods for configuring Cassette.
/// Adds Cassette and related services to the <see cref="IServiceCollection"/>.
/// </summary>
public static class CassetteConfigurationExtensions
public static IServiceCollection AddCassette(this IServiceCollection services, Action<CassetteOptions> configureCassette)
{
/// <summary>
/// Adds Cassette and related services to the <see cref="IServiceCollection"/>.
/// </summary>
public static IServiceCollection AddCassette(this IServiceCollection services, Action<CassetteOptions> configureCassette)
{
if (services is null)
{
throw new ArgumentNullException(nameof(services));
}
if (configureCassette is null)
{
throw new ArgumentNullException(nameof(configureCassette));
}
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(configureCassette);

services.AddLogging();
services.AddOptions();
services.AddLogging();
services.AddOptions();

services.ConfigureAll(configureCassette);
services.TryAddTransient<ReplayingHandler>();
services.ConfigureAll(configureCassette);
services.TryAddTransient<ReplayingHandler>();

return services;
}
return services;
}

/// <summary>
/// Adds the Cassette message handler to a dedicated <see cref="System.Net.Http.HttpClient"/>,
/// only if <see cref="AddCassette(IServiceCollection, Action{CassetteOptions})"/> has been previously called.
/// Otherwise does not do anything.
/// </summary>
public static IHttpClientBuilder AddReplayingHttpMessageHandler(this IHttpClientBuilder builder)
/// <summary>
/// Adds the Cassette message handler to a dedicated <see cref="System.Net.Http.HttpClient"/>,
/// only if <see cref="AddCassette(IServiceCollection, Action{CassetteOptions})"/> has been previously called.
/// Otherwise does not do anything.
/// </summary>
public static IHttpClientBuilder AddReplayingHttpMessageHandler(this IHttpClientBuilder builder)
{
if (builder.Services.IsCassetteRegistered())
{
if (builder.Services.IsCassetteRegistered())
{
builder.AddHttpMessageHandler<ReplayingHandler>();
}
return builder;
builder.AddHttpMessageHandler<ReplayingHandler>();
}

private static bool IsCassetteRegistered(this IServiceCollection services) =>
services.Any(x => x.ServiceType == typeof(ReplayingHandler));
return builder;
}

private static bool IsCassetteRegistered(this IServiceCollection services) =>
services.Any(x => x.ServiceType == typeof(ReplayingHandler));
}
Loading

0 comments on commit 5caf814

Please sign in to comment.