Skip to content

Commit f6be96c

Browse files
committed
Merge branch 'refs/heads/dev' into wrapping-up-interceptors
# Conflicts: # src/Directory.Build.props # src/RestSharp/RestClient.cs
2 parents 95ed004 + 1c86286 commit f6be96c

File tree

9 files changed

+90
-36
lines changed

9 files changed

+90
-36
lines changed

docs/usage.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,10 @@ Now, we need to implement the `GetToken` function in the class:
6060

6161
```csharp
6262
async Task<string> GetToken() {
63-
var options = new RestClientOptions(_baseUrl);
64-
using var client = new RestClient(options) {
63+
var options = new RestClientOptions(_baseUrl){
6564
Authenticator = new HttpBasicAuthenticator(_clientId, _clientSecret),
6665
};
66+
using var client = new RestClient(options);
6767

6868
var request = new RestRequest("oauth2/token")
6969
.AddParameter("grant_type", "client_credentials");
@@ -107,11 +107,11 @@ public class TwitterClient : ITwitterClient, IDisposable {
107107
readonly RestClient _client;
108108

109109
public TwitterClient(string apiKey, string apiKeySecret) {
110-
var options = new RestClientOptions("https://api.twitter.com/2");
111-
112-
_client = new RestClient(options) {
110+
var options = new RestClientOptions("https://api.twitter.com/2"){
113111
Authenticator = new TwitterAuthenticator("https://api.twitter.com", apiKey, apiKeySecret)
114112
};
113+
114+
_client = new RestClient(options);
115115
}
116116

117117
public async Task<TwitterUser> GetUser(string user) {
@@ -594,7 +594,7 @@ One way of doing it is to use `RestClient` constructors that accept an instance
594594

595595
- `BaseAddress` will be used to set the base address of the `HttpClient` instance if base address is not set there already.
596596
- `MaxTimeout`
597-
- `UserAgent` will be set if the `User-Agent` header is not set on the `HttpClient` instance already.
597+
- `UserAgent` will be added to the `RestClient.DefaultParameters` list as a HTTP header. This will be added to each request made by the `RestClient`, and the `HttpClient` instance will not be modified. This is to allow the `HttpClient` instance to be reused for scenarios where different `User-Agent` headers are required.
598598
- `Expect100Continue`
599599

600600
Another option is to use a simple HTTP client factory as described [above](#simple-factory).

src/Directory.Build.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
1616
<GenerateDocumentationFile>true</GenerateDocumentationFile>
1717
<NoWarn>$(NoWarn);1591</NoWarn>
18+
<PackageReadmeFile>README.md</PackageReadmeFile>
1819
</PropertyGroup>
1920

2021
<ItemGroup>

src/RestSharp/Authenticators/OAuth/OAuthTools.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ public static string GetNonce() {
142142
internal static IEnumerable<string> SortParametersExcludingSignature(WebPairCollection parameters)
143143
=> parameters
144144
.Where(x => !x.Name.EqualsIgnoreCase("oauth_signature"))
145-
.Select(x => new WebPair(UrlEncodeStrict(x.Name), UrlEncodeStrict(x.Value)))
145+
.Select(x => new WebPair(UrlEncodeStrict(x.Name), UrlEncodeRelaxed(x.Value)))
146146
.OrderBy(x => x, WebPair.Comparer)
147147
.Select(x => x.GetQueryParameter(false));
148148

src/RestSharp/KnownHeaders.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public static class KnownHeaders {
4242
LastModified
4343
};
4444

45-
static readonly HashSet<string> ContentHeadersHash = new(ContentHeaders.Select(x => x.ToLower()));
45+
static readonly HashSet<string> ContentHeadersHash = new(ContentHeaders, StringComparer.InvariantCultureIgnoreCase);
4646

47-
internal static bool IsContentHeader(string key) => ContentHeadersHash.Contains(key.ToLower());
47+
internal static bool IsContentHeader(string key) => ContentHeadersHash.Contains(key);
4848
}

src/RestSharp/RestClient.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ public RestClient(
7979

8080
ConfigureSerializers(configureSerialization);
8181
Options = new ReadOnlyRestClientOptions(options);
82+
DefaultParameters = new DefaultParameters(Options);
8283

8384
if (useClientFactory) {
8485
_disposeHttpClient = false;
@@ -89,7 +90,6 @@ public RestClient(
8990
HttpClient = GetClient();
9091
}
9192

92-
DefaultParameters = new DefaultParameters(Options);
9393
return;
9494

9595
HttpClient GetClient() {
@@ -98,6 +98,7 @@ HttpClient GetClient() {
9898
var finalHandler = options.ConfigureMessageHandler?.Invoke(handler) ?? handler;
9999
var httpClient = new HttpClient(finalHandler);
100100
ConfigureHttpClient(httpClient, options);
101+
ConfigureDefaultParameters(options);
101102
configureDefaultHeaders?.Invoke(httpClient.DefaultRequestHeaders);
102103
return httpClient;
103104
}
@@ -189,6 +190,7 @@ public RestClient(
189190

190191
if (options != null) {
191192
ConfigureHttpClient(httpClient, options);
193+
ConfigureDefaultParameters(options);
192194
}
193195
}
194196

@@ -226,11 +228,6 @@ public RestClient(
226228
static void ConfigureHttpClient(HttpClient httpClient, RestClientOptions options) {
227229
if (options.MaxTimeout > 0) httpClient.Timeout = TimeSpan.FromMilliseconds(options.MaxTimeout);
228230

229-
if (options.UserAgent != null &&
230-
httpClient.DefaultRequestHeaders.UserAgent.All(x => $"{x.Product?.Name}/{x.Product?.Version}" != options.UserAgent)) {
231-
httpClient.DefaultRequestHeaders.TryAddWithoutValidation(KnownHeaders.UserAgent, options.UserAgent);
232-
}
233-
234231
if (options.Expect100Continue != null) httpClient.DefaultRequestHeaders.ExpectContinue = options.Expect100Continue;
235232
}
236233

@@ -278,6 +275,15 @@ void ConfigureSerializers(ConfigureSerialization? configureSerialization) {
278275
AcceptedContentTypes = Serializers.GetAcceptedContentTypes();
279276
}
280277

278+
void ConfigureDefaultParameters(RestClientOptions options) {
279+
if (options.UserAgent != null) {
280+
if (!options.AllowMultipleDefaultParametersWithSameName
281+
&& DefaultParameters.Any(parameter => parameter.Type == ParameterType.HttpHeader && parameter.Name == KnownHeaders.UserAgent))
282+
DefaultParameters.RemoveParameter(KnownHeaders.UserAgent, ParameterType.HttpHeader);
283+
DefaultParameters.AddParameter(Parameter.CreateParameter(KnownHeaders.UserAgent, options.UserAgent, ParameterType.HttpHeader));
284+
}
285+
}
286+
281287
readonly bool _disposeHttpClient;
282288
bool _disposed;
283289

src/RestSharp/RestSharp.csproj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
44
</PropertyGroup>
@@ -56,4 +56,7 @@
5656
<ItemGroup>
5757
<ProjectReference Include="$(RepoRoot)\gen\SourceGenerator\SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
5858
</ItemGroup>
59+
<ItemGroup>
60+
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
61+
</ItemGroup>
5962
</Project>

src/RestSharp/Serializers/RestSerializers.cs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,24 @@ static async ValueTask OnBeforeDeserialization(RestResponse response, Cancellati
9393
if (string.IsNullOrWhiteSpace(response.Content)) return null;
9494

9595
var contentType = response.ContentType ?? DetectContentType()?.Value;
96-
if (contentType == null) return null;
96+
97+
if (contentType == null) {
98+
Serializers.TryGetValue(response.Request.RequestFormat, out var serializerByRequestFormat);
99+
return serializerByRequestFormat?.GetSerializer().Deserializer;
100+
}
97101

98102
var serializer = Serializers.Values.FirstOrDefault(x => x.SupportsContentType(contentType));
99103

100-
var factory = serializer ??
101-
(Serializers.ContainsKey(response.Request.RequestFormat) ? Serializers[response.Request.RequestFormat] : null);
102-
return factory?.GetSerializer().Deserializer;
104+
if (serializer == null) {
105+
var detectedType = DetectContentType()?.Value;
106+
107+
if (detectedType != null && detectedType != contentType)
108+
{
109+
serializer = Serializers.Values.FirstOrDefault(x => x.SupportsContentType(detectedType));
110+
}
111+
}
112+
113+
return serializer?.GetSerializer().Deserializer;
103114

104115
ContentType? DetectContentType()
105116
=> response.Content![0] switch {

test/RestSharp.Tests.Integrated/OAuth1Tests.cs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,29 @@ public async Task Can_Authenticate_OAuth1_With_Querystring_Parameters() {
5151
actual.Should().BeEquivalentTo(expected);
5252
}
5353

54-
[Fact]
55-
public void Properly_Encodes_Parameter_Names() {
56-
var postData = new WebPairCollection {
57-
{ "name[first]", "Chuck" },
58-
{ "name[last]", "Testa" }
59-
};
54+
[Theory]
55+
[MemberData(nameof(EncodeParametersTestData))]
56+
public void Properly_Encodes_Parameter_Names(IList<(string, string)> parameters, string expected) {
57+
var postData = new WebPairCollection();
58+
postData.AddRange(parameters.Select(x => new WebPair(x.Item1, x.Item2)));
6059
var sortedParams = OAuthTools.SortParametersExcludingSignature(postData);
6160

62-
sortedParams.First().Should().Be("name%5Bfirst%5D=Chuck");
61+
sortedParams.First().Should().Be(expected);
6362
}
6463

64+
public static IEnumerable<object[]> EncodeParametersTestData =>
65+
new List<object[]>
66+
{
67+
new object[] {
68+
new List<(string, string)> { ("name[first]", "Chuck"), ("name[last]", "Testa") },
69+
"name%5Bfirst%5D=Chuck"
70+
},
71+
new object[] {
72+
new List<(string, string)> { ("country", "España") },
73+
"country=Espa%C3%B1a"
74+
}
75+
};
76+
6577
[Fact]
6678
public void Use_RFC_3986_Encoding_For_Auth_Signature_Base() {
6779
// reserved characters for 2396 and 3986

test/RestSharp.Tests/RestClientTests.cs

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -118,20 +118,41 @@ public void Should_use_new_httpClient_instance() {
118118
}
119119

120120
[Fact]
121-
public void ConfigureHttpClient_does_not_duplicate_user_agent_for_same_client() {
121+
public void ConfigureDefaultParameters_sets_user_agent_new_httpClient_instance() {
122+
// arrange
123+
var clientOptions = new RestClientOptions();
124+
125+
// act
126+
var restClient = new RestClient(clientOptions);
127+
128+
//assert
129+
Assert.Single(
130+
restClient.DefaultParameters,
131+
parameter => parameter.Type == ParameterType.HttpHeader &&
132+
parameter.Name == KnownHeaders.UserAgent &&
133+
parameter.Value is string valueAsString &&
134+
valueAsString == clientOptions.UserAgent);
135+
136+
Assert.Empty(restClient.HttpClient.DefaultRequestHeaders.UserAgent);
137+
}
138+
139+
[Fact]
140+
public void ConfigureDefaultParameters_sets_user_agent_given_httpClient_instance() {
122141
// arrange
123142
var httpClient = new HttpClient();
124143
var clientOptions = new RestClientOptions();
125144

126145
// act
127-
var unused = new RestClient(httpClient, clientOptions);
128-
var dummy = new RestClient(httpClient, clientOptions);
146+
var restClient = new RestClient(httpClient, clientOptions);
129147

130-
// assert
131-
Assert.Contains(
132-
httpClient.DefaultRequestHeaders.UserAgent,
133-
agent => $"{agent.Product.Name}/{agent.Product.Version}" == clientOptions.UserAgent
134-
);
135-
Assert.Single(httpClient.DefaultRequestHeaders.UserAgent);
148+
//assert
149+
Assert.Single(
150+
restClient.DefaultParameters,
151+
parameter => parameter.Type == ParameterType.HttpHeader &&
152+
parameter.Name == KnownHeaders.UserAgent &&
153+
parameter.Value is string valueAsString &&
154+
valueAsString == clientOptions.UserAgent);
155+
156+
Assert.Empty(httpClient.DefaultRequestHeaders.UserAgent);
136157
}
137158
}

0 commit comments

Comments
 (0)