diff --git a/.build/build.ps1 b/.build/build.ps1
index 3c3874fd7..238d8de6d 100644
--- a/.build/build.ps1
+++ b/.build/build.ps1
@@ -29,7 +29,7 @@ if($Branch -eq "beta" ) { $Version = "$Version-beta" }
$NuGet = Join-Path $RepoRoot ".nuget\nuget.exe"
-$MSBuild = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\msbuild.exe"
+$MSBuild = "${env:ProgramFiles}\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\msbuild.exe"
$MSBuild -replace ' ', '` '
FormatTaskName (("-"*25) + "[{0}]" + ("-"*25))
diff --git a/README.md b/README.md
index 0081ef80a..0b4ab7e95 100644
--- a/README.md
+++ b/README.md
@@ -51,15 +51,15 @@ The owner of this project, [justcoding121](https://github.com/justcoding121), is
### Development environment
#### Windows
-* Visual Studio Code as IDE for .NET Core
-* Visual Studio 2019 as IDE for .NET Framework/.NET Core
+* Visual Studio Code as IDE for .NET
+* Visual Studio 2022 as IDE for .NET Framework/.NET
#### Mac OS
-* Visual Studio Code as IDE for .NET Core
-* Visual Studio 2019 as IDE for Mono
+* Visual Studio Code as IDE for .NET
+* Visual Studio 2022 as IDE for Mono
#### Linux
-* Visual Studio Code as IDE for .NET Core
+* Visual Studio Code as IDE for .NET
* Mono develop as IDE for Mono
### Usage
@@ -93,7 +93,7 @@ var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true)
};
// Fired when a CONNECT request is received
-explicitEndPoint.BeforeTunnelConnect += OnBeforeTunnelConnect;
+explicitEndPoint.BeforeTunnelConnectRequest += OnBeforeTunnelConnectRequest;
// An explicit endpoint is where the client knows about the existence of a proxy
// So client sends request in a proxy friendly manner
@@ -127,7 +127,7 @@ proxyServer.SetAsSystemHttpsProxy(explicitEndPoint);
Console.Read();
// Unsubscribe & Quit
-explicitEndPoint.BeforeTunnelConnect -= OnBeforeTunnelConnect;
+explicitEndPoint.BeforeTunnelConnectRequest -= OnBeforeTunnelConnectRequest;
proxyServer.BeforeRequest -= OnRequest;
proxyServer.BeforeResponse -= OnResponse;
proxyServer.ServerCertificateValidationCallback -= OnCertificateValidation;
@@ -158,7 +158,7 @@ public async Task OnRequest(object sender, SessionEventArgs e)
Console.WriteLine(e.HttpClient.Request.Url);
// read request headers
- var requestHeaders = e.HttpClient.Request.RequestHeaders;
+ var requestHeaders = e.HttpClient.Request.Headers;
var method = e.HttpClient.Request.Method.ToUpper();
if ((method == "POST" || method == "PUT" || method == "PATCH"))
@@ -200,12 +200,12 @@ public async Task OnRequest(object sender, SessionEventArgs e)
public async Task OnResponse(object sender, SessionEventArgs e)
{
// read response headers
- var responseHeaders = e.HttpClient.Response.ResponseHeaders;
+ var responseHeaders = e.HttpClient.Response.Headers;
//if (!e.ProxySession.Request.Host.Equals("medeczane.sgk.gov.tr")) return;
if (e.HttpClient.Request.Method == "GET" || e.HttpClient.Request.Method == "POST")
{
- if (e.HttpClient.Response.ResponseStatusCode == "200")
+ if (e.HttpClient.Response.StatusCode == 200)
{
if (e.HttpClient.Response.ContentType != null && e.HttpClient.Response.ContentType.Trim().ToLower().Contains("text/html"))
{
diff --git a/appveyor.yml b/appveyor.yml
index 71cbcb40a..b90f85ec1 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -8,7 +8,7 @@
# version format
version: 3.1.{build}
-image: Visual Studio 2019
+image: Visual Studio 2022
shallow_clone: false
diff --git a/examples/Titanium.Web.Proxy.Examples.Basic/Properties/AssemblyInfo.cs b/examples/Titanium.Web.Proxy.Examples.Basic/Properties/AssemblyInfo.cs
deleted file mode 100644
index 8490a716c..000000000
--- a/examples/Titanium.Web.Proxy.Examples.Basic/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using System.Reflection;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("Demo")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("Demo")]
-[assembly: AssemblyCopyright("Copyright © Titanium 2015-2019")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("9a2c6980-90d1-4082-ad60-b2428f3d6197")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/examples/Titanium.Web.Proxy.Examples.Basic/Titanium.Web.Proxy.Examples.Basic.Mono.csproj b/examples/Titanium.Web.Proxy.Examples.Basic/Titanium.Web.Proxy.Examples.Basic.Mono.csproj
deleted file mode 100644
index 80a278309..000000000
--- a/examples/Titanium.Web.Proxy.Examples.Basic/Titanium.Web.Proxy.Examples.Basic.Mono.csproj
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
- Exe
- net461
- false
- 7.1
- AnyCPU;x64
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/examples/Titanium.Web.Proxy.Examples.Basic/Titanium.Web.Proxy.Examples.Basic.NetCore.csproj b/examples/Titanium.Web.Proxy.Examples.Basic/Titanium.Web.Proxy.Examples.Basic.NetCore.csproj
deleted file mode 100644
index 7c33920f3..000000000
--- a/examples/Titanium.Web.Proxy.Examples.Basic/Titanium.Web.Proxy.Examples.Basic.NetCore.csproj
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
- Exe
- net50
- false
- 7.1
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/examples/Titanium.Web.Proxy.Examples.Basic/Titanium.Web.Proxy.Examples.Basic.csproj b/examples/Titanium.Web.Proxy.Examples.Basic/Titanium.Web.Proxy.Examples.Basic.csproj
index b92fd5a6e..a76dca8cd 100644
--- a/examples/Titanium.Web.Proxy.Examples.Basic/Titanium.Web.Proxy.Examples.Basic.csproj
+++ b/examples/Titanium.Web.Proxy.Examples.Basic/Titanium.Web.Proxy.Examples.Basic.csproj
@@ -2,9 +2,7 @@
Exe
- net461;net50
- false
- 7.1
+ net48;net60
diff --git a/examples/Titanium.Web.Proxy.Examples.WindowsService/App.config b/examples/Titanium.Web.Proxy.Examples.WindowsService/App.config
index ae789a6c9..7be895aa1 100644
--- a/examples/Titanium.Web.Proxy.Examples.WindowsService/App.config
+++ b/examples/Titanium.Web.Proxy.Examples.WindowsService/App.config
@@ -1,26 +1,26 @@

-
-
+
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
diff --git a/examples/Titanium.Web.Proxy.Examples.WindowsService/Properties/Settings.Designer.cs b/examples/Titanium.Web.Proxy.Examples.WindowsService/Properties/Settings.Designer.cs
index 681c91c1d..ab7241999 100644
--- a/examples/Titanium.Web.Proxy.Examples.WindowsService/Properties/Settings.Designer.cs
+++ b/examples/Titanium.Web.Proxy.Examples.WindowsService/Properties/Settings.Designer.cs
@@ -13,7 +13,7 @@ namespace WindowsServiceExample.Properties
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.3.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.0.3.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
{
diff --git a/examples/Titanium.Web.Proxy.Examples.WindowsService/Titanium.Web.Proxy.Examples.WindowsService.csproj b/examples/Titanium.Web.Proxy.Examples.WindowsService/Titanium.Web.Proxy.Examples.WindowsService.csproj
index 0e9afb2a4..c24478490 100644
--- a/examples/Titanium.Web.Proxy.Examples.WindowsService/Titanium.Web.Proxy.Examples.WindowsService.csproj
+++ b/examples/Titanium.Web.Proxy.Examples.WindowsService/Titanium.Web.Proxy.Examples.WindowsService.csproj
@@ -8,7 +8,7 @@
win
WindowsServiceExample
WindowsServiceExample
- v4.7.2
+ v4.8
512
true
true
@@ -29,6 +29,7 @@
1.0.0.%2a
false
true
+
AnyCPU
@@ -121,5 +122,8 @@
+
+
+
\ No newline at end of file
diff --git a/examples/Titanium.Web.Proxy.Examples.Wpf/Properties/AssemblyInfo.cs b/examples/Titanium.Web.Proxy.Examples.Wpf/Properties/AssemblyInfo.cs
deleted file mode 100644
index cca79748f..000000000
--- a/examples/Titanium.Web.Proxy.Examples.Wpf/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-using System.Reflection;
-using System.Runtime.InteropServices;
-using System.Windows;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("Demo WPF")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("Demo WPF")]
-[assembly: AssemblyCopyright("Copyright © Titanium 2015-2019")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-//In order to begin building localizable applications, set
-//CultureYouAreCodingWith in your .csproj file
-//inside a . For example, if you are using US english
-//in your source files, set the to en-US. Then uncomment
-//the NeutralResourceLanguage attribute below. Update the "en-US" in
-//the line below to match the UICulture setting in the project file.
-
-//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
-
-
-[assembly: ThemeInfo(
- ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
- //(used if a resource is not found in the page,
- // or application resource dictionaries)
- ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
- //(used if a resource is not found in the page,
- // app, or any theme specific resource dictionaries)
-)]
-
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/examples/Titanium.Web.Proxy.Examples.Wpf/Titanium.Web.Proxy.Examples.Wpf.NetCore.csproj b/examples/Titanium.Web.Proxy.Examples.Wpf/Titanium.Web.Proxy.Examples.Wpf.NetCore.csproj
deleted file mode 100644
index fb17f5421..000000000
--- a/examples/Titanium.Web.Proxy.Examples.Wpf/Titanium.Web.Proxy.Examples.Wpf.NetCore.csproj
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
- WinExe
- net50
- true
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/examples/Titanium.Web.Proxy.Examples.Wpf/Titanium.Web.Proxy.Examples.Wpf.csproj b/examples/Titanium.Web.Proxy.Examples.Wpf/Titanium.Web.Proxy.Examples.Wpf.csproj
index caa5c1810..b8dcee626 100644
--- a/examples/Titanium.Web.Proxy.Examples.Wpf/Titanium.Web.Proxy.Examples.Wpf.csproj
+++ b/examples/Titanium.Web.Proxy.Examples.Wpf/Titanium.Web.Proxy.Examples.Wpf.csproj
@@ -1,17 +1,11 @@
-
+
WinExe
- net461;net50-windows
+ net48;net60-windows
true
-
-
-
-
-
-
diff --git a/src/Titanium.Web.Proxy/Certificates/Cache/DefaultCertificateDiskCache.cs b/src/Titanium.Web.Proxy/Certificates/Cache/DefaultCertificateDiskCache.cs
index 910df634e..6f4740179 100644
--- a/src/Titanium.Web.Proxy/Certificates/Cache/DefaultCertificateDiskCache.cs
+++ b/src/Titanium.Web.Proxy/Certificates/Cache/DefaultCertificateDiskCache.cs
@@ -129,6 +129,14 @@ private string getRootCertificateDirectory()
assemblyLocation = Assembly.GetEntryAssembly().Location;
}
+#if NETSTANDARD2_1
+ // single-file app returns string.Empty location
+ if (assemblyLocation == string.Empty)
+ {
+ assemblyLocation = AppContext.BaseDirectory;
+ }
+#endif
+
string path = Path.GetDirectoryName(assemblyLocation);
rootCertificatePath = path ?? throw new NullReferenceException();
diff --git a/src/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs b/src/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
index 4e6630b10..a36a36503 100644
--- a/src/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
+++ b/src/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
@@ -85,6 +85,11 @@ private async Task readRequestBodyAsync(CancellationToken cancellationToken)
// If not already read (not cached yet)
if (!request.IsBodyRead)
{
+ if (request.IsBodyReceived)
+ {
+ throw new Exception("Request body was already received.");
+ }
+
if (request.HttpVersion == HttpHeader.Version20)
{
// do not send to the remote endpoint
@@ -103,6 +108,7 @@ private async Task readRequestBodyAsync(CancellationToken cancellationToken)
// Now set the flag to true
// So that next time we can deliver body from cache
request.IsBodyRead = true;
+ request.IsBodyReceived = true;
}
else
{
@@ -115,6 +121,7 @@ private async Task readRequestBodyAsync(CancellationToken cancellationToken)
// Now set the flag to true
// So that next time we can deliver body from cache
request.IsBodyRead = true;
+ request.IsBodyReceived = true;
}
}
}
@@ -160,6 +167,11 @@ private async Task readResponseBodyAsync(CancellationToken cancellationToken)
// If not already read (not cached yet)
if (!response.IsBodyRead)
{
+ if (response.IsBodyReceived)
+ {
+ throw new Exception("Response body was already received.");
+ }
+
if (response.HttpVersion == HttpHeader.Version20)
{
// do not send to the remote endpoint
@@ -178,6 +190,7 @@ private async Task readResponseBodyAsync(CancellationToken cancellationToken)
// Now set the flag to true
// So that next time we can deliver body from cache
response.IsBodyRead = true;
+ response.IsBodyReceived = true;
}
else
{
@@ -190,6 +203,7 @@ private async Task readResponseBodyAsync(CancellationToken cancellationToken)
// Now set the flag to true
// So that next time we can deliver body from cache
response.IsBodyRead = true;
+ response.IsBodyReceived = true;
}
}
}
@@ -221,7 +235,7 @@ private async Task readBodyAsync(bool isRequest, CancellationToken cance
internal async Task SyphonOutBodyAsync(bool isRequest, CancellationToken cancellationToken)
{
var requestResponse = isRequest ? (RequestResponseBase)HttpClient.Request : HttpClient.Response;
- if (requestResponse.IsBodyRead || !requestResponse.OriginalHasBody)
+ if (requestResponse.IsBodyReceived || !requestResponse.OriginalHasBody)
{
return;
}
@@ -229,6 +243,7 @@ internal async Task SyphonOutBodyAsync(bool isRequest, CancellationToken cancell
var reader = isRequest ? (HttpStream)ClientStream : HttpClient.Connection.Stream;
await reader.CopyBodyAsync(requestResponse, true, NullWriter.Instance, TransformationMode.None, isRequest, this, cancellationToken);
+ requestResponse.IsBodyReceived = true;
}
///
@@ -272,11 +287,15 @@ internal async Task CopyRequestBodyAsync(IHttpStreamWriter writer, Transformatio
{
await reader.CopyBodyAsync(request, false, writer, transformation, true, this, cancellationToken);
}
+
+ request.IsBodyReceived = true;
}
private async Task copyResponseBodyAsync(IHttpStreamWriter writer, TransformationMode transformation, CancellationToken cancellationToken)
{
- await HttpClient.Connection.Stream.CopyBodyAsync(HttpClient.Response, false, writer, transformation, false, this, cancellationToken);
+ var response = HttpClient.Response;
+ await HttpClient.Connection.Stream.CopyBodyAsync(response, false, writer, transformation, false, this, cancellationToken);
+ response.IsBodyReceived = true;
}
///
diff --git a/src/Titanium.Web.Proxy/Http/RequestResponseBase.cs b/src/Titanium.Web.Proxy/Http/RequestResponseBase.cs
index 825cc8db8..8fb1b6eaa 100644
--- a/src/Titanium.Web.Proxy/Http/RequestResponseBase.cs
+++ b/src/Titanium.Web.Proxy/Http/RequestResponseBase.cs
@@ -211,6 +211,8 @@ internal set
internal bool BodyAvailable => BodyInternal != null;
+ internal bool IsBodyReceived { get; set; }
+
internal bool IsBodySent { get; set; }
internal abstract void EnsureBodyAvailable(bool throwWhenNotReadYet = true);
diff --git a/src/Titanium.Web.Proxy/Http2/Http2Helper.cs b/src/Titanium.Web.Proxy/Http2/Http2Helper.cs
index d84341874..125e5185a 100644
--- a/src/Titanium.Web.Proxy/Http2/Http2Helper.cs
+++ b/src/Titanium.Web.Proxy/Http2/Http2Helper.cs
@@ -394,6 +394,7 @@ private static async Task copyHttp2FrameAsync(Stream input, Stream output,
}
rr.IsBodyRead = true;
+ rr.IsBodyReceived = true;
var tcs = rr.ReadHttp2BodyTaskCompletionSource;
rr.ReadHttp2BodyTaskCompletionSource = null;
diff --git a/src/Titanium.Web.Proxy/Network/TcpConnection/TcpConnectionFactory.cs b/src/Titanium.Web.Proxy/Network/TcpConnection/TcpConnectionFactory.cs
index 3194682fc..23e86a54e 100644
--- a/src/Titanium.Web.Proxy/Network/TcpConnection/TcpConnectionFactory.cs
+++ b/src/Titanium.Web.Proxy/Network/TcpConnection/TcpConnectionFactory.cs
@@ -538,6 +538,8 @@ internal Task GetServerConnection(ProxyServer proxyServer,
connectRequest.Headers.AddHeader(HttpHeader.GetProxyAuthorizationHeader(externalProxy.UserName, externalProxy.Password));
}
+ await proxyServer.onBeforeUpStreamConnectRequest(connectRequest);
+
await stream.WriteRequestAsync(connectRequest, cancellationToken);
var httpStatus = await stream.ReadResponseStatus(cancellationToken);
diff --git a/src/Titanium.Web.Proxy/ProxyServer.cs b/src/Titanium.Web.Proxy/ProxyServer.cs
index bf4f625bf..ca0220af6 100644
--- a/src/Titanium.Web.Proxy/ProxyServer.cs
+++ b/src/Titanium.Web.Proxy/ProxyServer.cs
@@ -11,6 +11,7 @@
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Helpers.WinHttp;
+using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.Network.Tcp;
@@ -387,6 +388,11 @@ public ExceptionHandler ExceptionFunc
///
public event AsyncEventHandler? OnServerConnectionCreate;
+ ///
+ /// Intercept connect request sent to upstream proxy.
+ ///
+ public event AsyncEventHandler? BeforeUpStreamConnectRequest;
+
///
/// Customize the minimum ThreadPool size (increase it on a server)
///
diff --git a/src/Titanium.Web.Proxy/RequestHandler.cs b/src/Titanium.Web.Proxy/RequestHandler.cs
index 005c2daa7..ba6b502d8 100644
--- a/src/Titanium.Web.Proxy/RequestHandler.cs
+++ b/src/Titanium.Web.Proxy/RequestHandler.cs
@@ -406,6 +406,19 @@ private async Task onBeforeRequest(SessionEventArgs args)
}
}
+ ///
+ /// Invoke before request handler if it is set.
+ ///
+ /// The COONECT request.
+ ///
+ internal async Task onBeforeUpStreamConnectRequest(ConnectRequest request)
+ {
+ if (BeforeUpStreamConnectRequest != null)
+ {
+ await BeforeUpStreamConnectRequest.InvokeAsync(this, request, ExceptionFunc);
+ }
+ }
+
#if DEBUG
internal bool ShouldCallBeforeRequestBodyWrite()
{
diff --git a/src/Titanium.Web.Proxy/ResponseHandler.cs b/src/Titanium.Web.Proxy/ResponseHandler.cs
index 0d07c9d3b..b294244ba 100644
--- a/src/Titanium.Web.Proxy/ResponseHandler.cs
+++ b/src/Titanium.Web.Proxy/ResponseHandler.cs
@@ -120,6 +120,8 @@ await handleHttpSessionRequest(args, null, args.ClientConnection.NegotiatedAppli
await serverStream.CopyBodyAsync(response, false, clientStream, TransformationMode.None,
false, args, cancellationToken);
}
+
+ response.IsBodyReceived = true;
}
args.TimeLine["Response Sent"] = DateTime.UtcNow;
diff --git a/tests/Titanium.Web.Proxy.IntegrationTests/Titanium.Web.Proxy.IntegrationTests.csproj b/tests/Titanium.Web.Proxy.IntegrationTests/Titanium.Web.Proxy.IntegrationTests.csproj
index 0b45452c3..79af3429b 100644
--- a/tests/Titanium.Web.Proxy.IntegrationTests/Titanium.Web.Proxy.IntegrationTests.csproj
+++ b/tests/Titanium.Web.Proxy.IntegrationTests/Titanium.Web.Proxy.IntegrationTests.csproj
@@ -1,7 +1,7 @@

- net50
+ net60
StrongNameKey.snk
true