Skip to content
Merged

v109 #1963

Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Added Authenticator to RestRequest
Made Options immutable to make the client thread-safe
  • Loading branch information
alexeyzimarev committed Feb 16, 2023
commit 7e0a1aff813db0eeec9b938695086e1f730b9ee7
35 changes: 35 additions & 0 deletions RestSharp.sln
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RestSharp.Serializers.CsvHe
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RestSharp.Tests.Serializers.Csv", "test\RestSharp.Tests.Serializers.Csv\RestSharp.Tests.Serializers.Csv.csproj", "{E6D94FFD-7811-40BE-ABC4-6D6AB41F0060}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SourceGen", "SourceGen", "{55B8F371-B2BA-4DEE-AB98-5BAB8A21B1C2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceGenerator", "gen\SourceGenerator\SourceGenerator.csproj", "{FE778406-ADCF-45A1-B775-A054B55BFC50}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug.Appveyor|Any CPU = Debug.Appveyor|Any CPU
Expand Down Expand Up @@ -444,6 +448,36 @@ Global
{E6D94FFD-7811-40BE-ABC4-6D6AB41F0060}.Release|x64.Build.0 = Release|Any CPU
{E6D94FFD-7811-40BE-ABC4-6D6AB41F0060}.Release|x86.ActiveCfg = Release|Any CPU
{E6D94FFD-7811-40BE-ABC4-6D6AB41F0060}.Release|x86.Build.0 = Release|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Debug.Appveyor|Any CPU.ActiveCfg = Debug|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Debug.Appveyor|Any CPU.Build.0 = Debug|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Debug.Appveyor|ARM.ActiveCfg = Debug|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Debug.Appveyor|ARM.Build.0 = Debug|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Debug.Appveyor|Mixed Platforms.ActiveCfg = Debug|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Debug.Appveyor|Mixed Platforms.Build.0 = Debug|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Debug.Appveyor|x64.ActiveCfg = Debug|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Debug.Appveyor|x64.Build.0 = Debug|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Debug.Appveyor|x86.ActiveCfg = Debug|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Debug.Appveyor|x86.Build.0 = Debug|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Debug|ARM.ActiveCfg = Debug|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Debug|ARM.Build.0 = Debug|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Debug|x64.ActiveCfg = Debug|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Debug|x64.Build.0 = Debug|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Debug|x86.ActiveCfg = Debug|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Debug|x86.Build.0 = Debug|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Release|Any CPU.Build.0 = Release|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Release|ARM.ActiveCfg = Release|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Release|ARM.Build.0 = Release|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Release|x64.ActiveCfg = Release|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Release|x64.Build.0 = Release|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Release|x86.ActiveCfg = Release|Any CPU
{FE778406-ADCF-45A1-B775-A054B55BFC50}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -461,6 +495,7 @@ Global
{5A8A5BBE-28DA-4C89-B393-BE39A96E8DC0} = {9051DDA0-E563-45D5-9504-085EBAACF469}
{2150E333-8FDC-42A3-9474-1A3956D46DE8} = {8C7B43EB-2F93-483C-B433-E28F9386AD67}
{E6D94FFD-7811-40BE-ABC4-6D6AB41F0060} = {9051DDA0-E563-45D5-9504-085EBAACF469}
{FE778406-ADCF-45A1-B775-A054B55BFC50} = {55B8F371-B2BA-4DEE-AB98-5BAB8A21B1C2}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {77FF357B-03FA-4FA5-A68F-BFBE5800FEBA}
Expand Down
92 changes: 92 additions & 0 deletions gen/SourceGenerator/ImmutableGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright (c) .NET Foundation and Contributors
//
// 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.
//

using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;

namespace SourceGenerator;

[Generator]
public class ImmutableGenerator : ISourceGenerator {
public void Initialize(GeneratorInitializationContext context) { }

public void Execute(GeneratorExecutionContext context) {
var compilation = context.Compilation;

var mutableClasses = compilation.SyntaxTrees
.Select(tree => compilation.GetSemanticModel(tree))
.SelectMany(model => model.SyntaxTree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>())
.Where(syntax => syntax.AttributeLists.Any(list => list.Attributes.Any(attr => attr.Name.ToString() == "GenerateImmutable")));

foreach (var mutableClass in mutableClasses) {
var immutableClass = GenerateImmutableClass(mutableClass, compilation);
context.AddSource($"ReadOnly{mutableClass.Identifier.Text}.cs", SourceText.From(immutableClass, Encoding.UTF8));
}
}

static string GenerateImmutableClass(TypeDeclarationSyntax mutableClass, Compilation compilation) {
var containingNamespace = compilation.GetSemanticModel(mutableClass.SyntaxTree).GetDeclaredSymbol(mutableClass)!.ContainingNamespace;

var namespaceName = containingNamespace.ToDisplayString();

var className = mutableClass.Identifier.Text;

var usings = mutableClass.SyntaxTree.GetCompilationUnitRoot().Usings.Select(u => u.ToString());

var properties = GetDefinitions(SyntaxKind.SetKeyword)
.Select(prop => $" public {prop.Type} {prop.Identifier.Text} {{ get; }}")
.ToArray();

var props = GetDefinitions(SyntaxKind.SetKeyword).ToArray();

const string argName = "inner";
var mutableProperties = props
.Select(prop => $" {prop.Identifier.Text} = {argName}.{prop.Identifier.Text};");

var constructor = $@" public ReadOnly{className}({className} {argName}) {{
{string.Join("\n", mutableProperties)}
}}";

const string template = @"{Usings}

namespace {Namespace};

public class ReadOnly{ClassName} {
{Constructor}

{Properties}
}";

var code = template
.Replace("{Usings}", string.Join("\n", usings))
.Replace("{Namespace}", namespaceName)
.Replace("{ClassName}", className)
.Replace("{Constructor}", constructor)
.Replace("{Properties}", string.Join("\n", properties));

return code;

IEnumerable<PropertyDeclarationSyntax> GetDefinitions(SyntaxKind kind)
=> mutableClass.Members
.OfType<PropertyDeclarationSyntax>()
.Where(
prop =>
prop.AccessorList!.Accessors.Any(accessor => accessor.Keyword.IsKind(kind))
);
}
}
22 changes: 22 additions & 0 deletions gen/SourceGenerator/SourceGenerator.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>11</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
<IncludeBuildOutput>false</IncludeBuildOutput>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="All" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" PrivateAssets="All" />
<!-- <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.4.0" PrivateAssets="All" />-->
<!-- <PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.4.0" PrivateAssets="All"/>-->
</ItemGroup>

<ItemGroup>
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup>
</Project>
19 changes: 19 additions & 0 deletions src/RestSharp/Extensions/GenerateImmutableAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) .NET Foundation and Contributors
//
// 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.
//

namespace RestSharp.Extensions;

[AttributeUsage(AttributeTargets.Class)]
public class GenerateImmutableAttribute : Attribute { }
48 changes: 48 additions & 0 deletions src/RestSharp/Extensions/ImmutableGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) .NET Foundation and Contributors
//
// 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.
//

using System.Linq.Expressions;
using System.Reflection;

namespace RestSharp.Extensions;

static class ImmutableGenerator {
static readonly MethodInfo SetPropertyMethod =
typeof(ImmutableGenerator).GetMethod(nameof(SetProperty), BindingFlags.Static | BindingFlags.NonPublic)!;

static void SetProperty<T>(T obj, PropertyInfo property, object value) => property.SetValue(obj, (T)value);

public static Func<TMutable, TImmutable> CreateImmutableFunc<TMutable, TImmutable>()
where TMutable : class
where TImmutable : class, new()
{
var mutableType = typeof(TMutable);
var immutableType = typeof(TImmutable);

var parameter = Expression.Parameter(mutableType, "mutable");

var bindings = mutableType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Select(property =>
Expression.Bind(
immutableType.GetProperty(property.Name)!,
Expression.Property(parameter, property)));

var body = Expression.MemberInit(Expression.New(immutableType), bindings);

var lambda = Expression.Lambda<Func<TMutable, TImmutable>>(body, parameter);

return lambda.Compile();
}
}
2 changes: 1 addition & 1 deletion src/RestSharp/IRestClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public interface IRestClient : IDisposable {
/// <summary>
/// Client options that aren't used for configuring HttpClient
/// </summary>
IRestClientOptions Options { get; }
ReadOnlyRestClientOptions Options { get; }

/// <summary>
/// Client-level serializers
Expand Down
83 changes: 3 additions & 80 deletions src/RestSharp/Options/RestClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,89 +21,12 @@
using System.Text;
using RestSharp.Authenticators;
using RestSharp.Extensions;
using SourceGenerator;

namespace RestSharp;

public interface IRestClientOptions {
/// <summary>
/// Explicit Host header value to use in requests independent from the request URI.
/// If null, default host value extracted from URI is used.
/// </summary>
Uri? BaseUrl { get; }

/// <summary>
/// Function to calculate the response status. By default, the status will be Completed if it was successful, or NotFound.
/// </summary>
CalculateResponseStatus CalculateResponseStatus { get; }

/// <summary>
/// Authenticator that will be used to populate request with necessary authentication data
/// </summary>
IAuthenticator? Authenticator { get; set; }

/// <summary>
/// Set to true if you need the Content-Type not to have the charset
/// </summary>
bool DisableCharset { get; }

/// <summary>
/// Sets the cache control header for all the requests made by the client
/// </summary>
CacheControlHeaderValue? CachePolicy { get; }

Encoding Encoding { get; }

/// <summary>
/// Modifies the default behavior of RestSharp to swallow exceptions.
/// When set to <code>true</code>, a <see cref="DeserializationException"/> will be thrown
/// in case RestSharp fails to deserialize the response.
/// </summary>
bool ThrowOnDeserializationError { get; }

/// <summary>
/// Modifies the default behavior of RestSharp to swallow exceptions.
/// When set to <code>true</code>, RestSharp will consider the request as unsuccessful
/// in case it fails to deserialize the response.
/// </summary>
bool FailOnDeserializationError { get; }

/// <summary>
/// Modifies the default behavior of RestSharp to swallow exceptions.
/// When set to <code>true</code>, exceptions will be re-thrown.
/// </summary>
bool ThrowOnAnyError { get; }

/// <summary>
/// Sets the base host header for all the requests made by the client
/// </summary>
string? BaseHost { get; }

/// <summary>
/// By default, RestSharp doesn't allow multiple parameters to have the same name.
/// This properly allows to override the default behavior.
/// </summary>
bool AllowMultipleDefaultParametersWithSameName { get; }

/// <summary>
/// Function used to encode parameters
/// </summary>
Func<string, string> Encode {
get;
[Obsolete("Don't change this options at runtime")]
set;
}

/// <summary>
/// Function used to encode query parameters
/// </summary>
Func<string, Encoding, string> EncodeQuery {
get;
[Obsolete("Don't change this options at runtime")]
set;
}
}

public class RestClientOptions : IRestClientOptions {
[GenerateImmutable]
public class RestClientOptions {
static readonly Version Version = new AssemblyName(typeof(RestClientOptions).Assembly.FullName!).Version!;

static readonly string DefaultUserAgent = $"RestSharp/{Version}";
Expand Down
6 changes: 6 additions & 0 deletions src/RestSharp/Request/RestRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

using System.Net;
using RestSharp.Authenticators;
using RestSharp.Extensions;

// ReSharper disable ReplaceSubstringWithRangeIndexer
Expand Down Expand Up @@ -100,6 +101,11 @@ public RestRequest(Uri resource, Method method = Method.Get)
/// </summary>
public CookieContainer? CookieContainer { get; set; }

/// <summary>
/// Request-level authenticator. It will be used if set, otherwise RestClient.Authenticator will be used.
/// </summary>
public IAuthenticator? Authenticator { get; set; }

/// <summary>
/// Container of all the files to be uploaded with the request.
/// </summary>
Expand Down
5 changes: 3 additions & 2 deletions src/RestSharp/RestClient.Async.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public async Task<RestResponse> ExecuteAsync(RestRequest request, CancellationTo
internalResponse.ResponseMessage!,
request,
Options.Encoding,
request.CookieContainer!.GetCookies(internalResponse.Url),
request.CookieContainer?.GetCookies(internalResponse.Url),
Options.CalculateResponseStatus,
cancellationToken
)
Expand Down Expand Up @@ -87,7 +87,8 @@ async Task<HttpResponse> ExecuteRequestAsync(RestRequest request, CancellationTo

using var requestContent = new RequestContent(this, request);

if (Options.Authenticator != null) await Options.Authenticator.Authenticate(this, request).ConfigureAwait(false);
var authenticator = request.Authenticator ?? Options.Authenticator;
if (authenticator != null) await authenticator.Authenticate(this, request).ConfigureAwait(false);

var httpMethod = AsHttpMethod(request.Method);
var url = BuildUri(request);
Expand Down
Loading