Skip to content

Commit c8b9f65

Browse files
committed
Merge branch 'dev' into v109
# Conflicts: # src/RestSharp/Response/RestResponse.cs
2 parents d2c33ab + 2621b17 commit c8b9f65

File tree

13 files changed

+215
-61
lines changed

13 files changed

+215
-61
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) .NET Foundation and Contributors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
//
15+
16+
namespace RestSharp.Extensions;
17+
18+
public static class HttpResponseExtensions {
19+
internal static Exception? MaybeException(this HttpResponseMessage httpResponse)
20+
=> httpResponse.IsSuccessStatusCode
21+
? null
22+
#if NETSTANDARD
23+
: new HttpRequestException($"Request failed with status code {httpResponse.StatusCode}");
24+
#else
25+
: new HttpRequestException($"Request failed with status code {httpResponse.StatusCode}", null, httpResponse.StatusCode);
26+
#endif
27+
}

src/RestSharp/KnownHeaders.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public static class KnownHeaders {
3030
public const string ContentLocation = "Content-Location";
3131
public const string ContentRange = "Content-Range";
3232
public const string ContentType = "Content-Type";
33+
public const string Cookie = "Cookie";
3334
public const string LastModified = "Last-Modified";
3435
public const string ContentMD5 = "Content-MD5";
3536
public const string Host = "Host";

src/RestSharp/Request/RequestHeaders.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414
//
1515

1616
// ReSharper disable InvertIf
17-
namespace RestSharp;
17+
18+
using System.Net;
19+
20+
namespace RestSharp;
1821

1922
class RequestHeaders {
2023
public ParametersCollection Parameters { get; } = new();
@@ -33,4 +36,13 @@ public RequestHeaders AddAcceptHeader(string[] acceptedContentTypes) {
3336

3437
return this;
3538
}
39+
40+
// Add Cookie header from the cookie container
41+
public RequestHeaders AddCookieHeaders(CookieContainer cookieContainer, Uri uri) {
42+
var cookies = cookieContainer.GetCookieHeader(uri);
43+
if (cookies.Length > 0) {
44+
Parameters.AddParameter(new HeaderParameter(KnownHeaders.Cookie, cookies));
45+
}
46+
return this;
47+
}
3648
}

src/RestSharp/Request/RestRequest.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
using System.Net;
1516
using RestSharp.Extensions;
1617
// ReSharper disable UnusedAutoPropertyAccessor.Global
1718

@@ -29,6 +30,11 @@ public class RestRequest {
2930
/// </summary>
3031
public RestRequest() => Method = Method.Get;
3132

33+
/// <summary>
34+
/// Constructor for a rest request to a relative resource URL and optional method
35+
/// </summary>
36+
/// <param name="resource">Resource to use</param>
37+
/// <param name="method">Method to use (defaults to Method.Get></param>
3238
public RestRequest(string? resource, Method method = Method.Get) : this() {
3339
Resource = resource ?? "";
3440
Method = method;
@@ -58,6 +64,11 @@ static IEnumerable<KeyValuePair<string, string>> ParseQuery(string query)
5864
);
5965
}
6066

67+
/// <summary>
68+
/// Constructor for a rest request to a specific resource Uri and optional method
69+
/// </summary>
70+
/// <param name="resource">Resource Uri to use</param>
71+
/// <param name="method">Method to use (defaults to Method.Get></param>
6172
public RestRequest(Uri resource, Method method = Method.Get)
6273
: this(resource.IsAbsoluteUri ? resource.AbsoluteUri : resource.OriginalString, method) { }
6374

@@ -83,6 +94,11 @@ public RestRequest(Uri resource, Method method = Method.Get)
8394
/// </summary>
8495
public ParametersCollection Parameters { get; } = new();
8596

97+
/// <summary>
98+
/// Optional cookie container to use for the request. If not set, cookies are not passed.
99+
/// </summary>
100+
public CookieContainer? CookieContainer { get; set; }
101+
86102
/// <summary>
87103
/// Container of all the files to be uploaded with the request.
88104
/// </summary>

src/RestSharp/Request/RestRequestExtensions.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
using System.Net;
1516
using System.Text.RegularExpressions;
1617
using RestSharp.Extensions;
1718
using RestSharp.Serializers;
@@ -445,6 +446,21 @@ public static RestRequest AddObject<T>(this RestRequest request, T obj, params s
445446
return request;
446447
}
447448

449+
/// <summary>
450+
/// Adds cookie to the <seealso cref="HttpClient"/> cookie container.
451+
/// </summary>
452+
/// <param name="request">RestRequest to add the cookies to</param>
453+
/// <param name="name">Cookie name</param>
454+
/// <param name="value">Cookie value</param>
455+
/// <param name="path">Cookie path</param>
456+
/// <param name="domain">Cookie domain, must not be an empty string</param>
457+
/// <returns></returns>
458+
public static RestRequest AddCookie(this RestRequest request, string name, string value, string path, string domain) {
459+
request.CookieContainer ??= new CookieContainer();
460+
request.CookieContainer.Add(new Cookie(name, value, path, domain));
461+
return request;
462+
}
463+
448464
static void CheckAndThrowsForInvalidHost(string name, string value) {
449465
static bool InvalidHost(string host) => Uri.CheckHostName(PortSplitRegex.Split(host)[0]) == UriHostNameType.Unknown;
450466

src/RestSharp/Response/RestResponse.cs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ internal static async Task<RestResponse> FromHttpResponse(
6464
HttpResponseMessage httpResponse,
6565
RestRequest request,
6666
Encoding encoding,
67-
CookieCollection cookieCollection,
67+
CookieCollection? cookieCollection,
6868
CalculateResponseStatus calculateResponseStatus,
6969
CancellationToken cancellationToken
7070
) {
@@ -89,7 +89,7 @@ async Task<RestResponse> GetDefaultResponse() {
8989
ContentLength = httpResponse.Content.Headers.ContentLength,
9090
ContentType = httpResponse.Content.Headers.ContentType?.MediaType,
9191
ResponseStatus = calculateResponseStatus(httpResponse),
92-
ErrorException = MaybeException(),
92+
ErrorException = httpResponse.MaybeException(),
9393
ResponseUri = httpResponse.RequestMessage!.RequestUri,
9494
Server = httpResponse.Headers.Server.ToString(),
9595
StatusCode = httpResponse.StatusCode,
@@ -102,15 +102,6 @@ async Task<RestResponse> GetDefaultResponse() {
102102
RootElement = request.RootElement
103103
};
104104

105-
Exception? MaybeException()
106-
=> httpResponse.IsSuccessStatusCode
107-
? null
108-
#if NETSTANDARD || NETFRAMEWORK
109-
: new HttpRequestException($"Request failed with status code {httpResponse.StatusCode}");
110-
#else
111-
: new HttpRequestException($"Request failed with status code {httpResponse.StatusCode}", null, httpResponse.StatusCode);
112-
#endif
113-
114105
Task<Stream?> ReadResponse() => httpResponse.ReadResponse(cancellationToken);
115106

116107
async Task<Stream?> ReadAndConvertResponse() {

src/RestSharp/RestClient.Async.cs

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
using System.Net;
1516
using RestSharp.Extensions;
1617

1718
namespace RestSharp;
@@ -32,7 +33,7 @@ public async Task<RestResponse> ExecuteAsync(RestRequest request, CancellationTo
3233
internalResponse.ResponseMessage!,
3334
request,
3435
Options.Encoding,
35-
CookieContainer.GetCookies(internalResponse.Url),
36+
request.CookieContainer!.GetCookies(internalResponse.Url),
3637
CalculateResponseStatus,
3738
cancellationToken
3839
)
@@ -50,8 +51,7 @@ async Task<InternalResponse> ExecuteInternal(RestRequest request, CancellationTo
5051

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

53-
if (Authenticator != null)
54-
await Authenticator.Authenticate(this, request).ConfigureAwait(false);
54+
if (Authenticator != null) await Authenticator.Authenticate(this, request).ConfigureAwait(false);
5555

5656
var httpMethod = AsHttpMethod(request.Method);
5757
var url = BuildUri(request);
@@ -61,22 +61,31 @@ async Task<InternalResponse> ExecuteInternal(RestRequest request, CancellationTo
6161

6262
using var timeoutCts = new CancellationTokenSource(request.Timeout > 0 ? request.Timeout : int.MaxValue);
6363
using var cts = CancellationTokenSource.CreateLinkedTokenSource(timeoutCts.Token, cancellationToken);
64-
var ct = cts.Token;
64+
65+
var ct = cts.Token;
6566

6667
try {
68+
// Make sure we have a cookie container if not provided in the request
69+
var cookieContainer = request.CookieContainer ??= new CookieContainer();
6770
var headers = new RequestHeaders()
6871
.AddHeaders(request.Parameters)
6972
.AddHeaders(DefaultParameters)
70-
.AddAcceptHeader(AcceptedContentTypes);
73+
.AddAcceptHeader(AcceptedContentTypes)
74+
.AddCookieHeaders(cookieContainer, url);
7175
message.AddHeaders(headers);
7276

73-
if (request.OnBeforeRequest != null)
74-
await request.OnBeforeRequest(message).ConfigureAwait(false);
77+
if (request.OnBeforeRequest != null) await request.OnBeforeRequest(message).ConfigureAwait(false);
7578

7679
var responseMessage = await HttpClient.SendAsync(message, request.CompletionOption, ct).ConfigureAwait(false);
7780

78-
if (request.OnAfterRequest != null)
79-
await request.OnAfterRequest(responseMessage).ConfigureAwait(false);
81+
// Parse all the cookies from the response and update the cookie jar with cookies
82+
if (responseMessage.Headers.TryGetValues("Set-Cookie", out var cookiesHeader)) {
83+
foreach (var header in cookiesHeader) {
84+
cookieContainer.SetCookies(url, header);
85+
}
86+
}
87+
88+
if (request.OnAfterRequest != null) await request.OnAfterRequest(responseMessage).ConfigureAwait(false);
8089

8190
return new InternalResponse(responseMessage, url, null, timeoutCts.Token);
8291
}
@@ -99,8 +108,10 @@ record InternalResponse(HttpResponseMessage? ResponseMessage, Uri Url, Exception
99108
request.CompletionOption = HttpCompletionOption.ResponseHeadersRead;
100109
var response = await ExecuteInternal(request, cancellationToken).ConfigureAwait(false);
101110

102-
if (response.Exception != null) {
103-
return Options.ThrowOnAnyError ? throw response.Exception : null;
111+
var exception = response.Exception ?? response.ResponseMessage?.MaybeException();
112+
113+
if (exception != null) {
114+
return Options.ThrowOnAnyError ? throw exception : null;
104115
}
105116

106117
if (response.ResponseMessage == null) return null;
@@ -141,7 +152,7 @@ static HttpMethod AsHttpMethod(Method method)
141152
#if NETSTANDARD || NETFRAMEWORK
142153
Method.Patch => new HttpMethod("PATCH"),
143154
#else
144-
Method.Patch => HttpMethod.Patch,
155+
Method.Patch => HttpMethod.Patch,
145156
#endif
146157
Method.Merge => new HttpMethod("MERGE"),
147158
Method.Copy => new HttpMethod("COPY"),
@@ -157,11 +168,11 @@ public static RestResponse ThrowIfError(this RestResponse response) {
157168

158169
return response;
159170
}
160-
171+
161172
public static RestResponse<T> ThrowIfError<T>(this RestResponse<T> response) {
162173
var exception = response.GetException();
163174
if (exception != null) throw exception;
164175

165176
return response;
166177
}
167-
}
178+
}

src/RestSharp/RestClient.cs

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ namespace RestSharp;
2727
/// Client to translate RestRequests into Http requests and process response result
2828
/// </summary>
2929
public partial class RestClient : IDisposable {
30-
public CookieContainer CookieContainer { get; }
31-
3230
/// <summary>
3331
/// Content types that will be sent in the Accept header. The list is populated from the known serializers.
3432
/// If you need to send something else by default, set this property to a different value.
@@ -51,7 +49,6 @@ public RestClient(RestClientOptions options, Action<HttpRequestHeaders>? configu
5149
UseDefaultSerializers();
5250

5351
Options = options;
54-
CookieContainer = Options.CookieContainer ?? new CookieContainer();
5552
_disposeHttpClient = true;
5653

5754
var handler = new HttpClientHandler();
@@ -71,23 +68,27 @@ public RestClient() : this(new RestClientOptions()) { }
7168

7269
/// <inheritdoc />
7370
/// <summary>
74-
/// Sets the BaseUrl property for requests made by this client instance
71+
/// Creates an instance of RestClient using a specific BaseUrl for requests made by this client instance
7572
/// </summary>
76-
/// <param name="baseUrl"></param>
73+
/// <param name="baseUrl">Base URI for the new client</param>
7774
public RestClient(Uri baseUrl) : this(new RestClientOptions { BaseUrl = baseUrl }) { }
7875

7976
/// <inheritdoc />
8077
/// <summary>
81-
/// Sets the BaseUrl property for requests made by this client instance
78+
/// Creates an instance of RestClient using a specific BaseUrl for requests made by this client instance
8279
/// </summary>
83-
/// <param name="baseUrl"></param>
80+
/// <param name="baseUrl">Base URI for this new client as a string</param>
8481
public RestClient(string baseUrl) : this(new Uri(Ensure.NotEmptyString(baseUrl, nameof(baseUrl)))) { }
8582

83+
/// <summary>
84+
/// Creates an instance of RestClient using a shared HttpClient and does not allocate one internally.
85+
/// </summary>
86+
/// <param name="httpClient">HttpClient to use</param>
87+
/// <param name="disposeHttpClient">True to dispose of the client, false to assume the caller does (defaults to false)</param>
8688
public RestClient(HttpClient httpClient, bool disposeHttpClient = false) {
8789
UseDefaultSerializers();
8890

8991
HttpClient = httpClient;
90-
CookieContainer = new CookieContainer();
9192
Options = new RestClientOptions();
9293
_disposeHttpClient = disposeHttpClient;
9394

@@ -96,15 +97,16 @@ public RestClient(HttpClient httpClient, bool disposeHttpClient = false) {
9697
}
9798
}
9899

100+
/// <summary>
101+
/// Creates an instance of RestClient using a shared HttpClient and specific RestClientOptions and does not allocate one internally.
102+
/// </summary>
103+
/// <param name="httpClient">HttpClient to use</param>
104+
/// <param name="options">RestClient options to use</param>
105+
/// <param name="disposeHttpClient">True to dispose of the client, false to assume the caller does (defaults to false)</param>
99106
public RestClient(HttpClient httpClient, RestClientOptions options, bool disposeHttpClient = false) {
100-
if (options.CookieContainer != null) {
101-
throw new ArgumentException("Custom cookie container cannot be added to the HttpClient instance", nameof(options.CookieContainer));
102-
}
103-
104107
UseDefaultSerializers();
105108

106109
HttpClient = httpClient;
107-
CookieContainer = new CookieContainer();
108110
Options = options;
109111
_disposeHttpClient = disposeHttpClient;
110112

@@ -134,9 +136,9 @@ void ConfigureHttpClient(HttpClient httpClient) {
134136
}
135137

136138
void ConfigureHttpMessageHandler(HttpClientHandler handler) {
139+
handler.UseCookies = false;
137140
handler.Credentials = Options.Credentials;
138141
handler.UseDefaultCredentials = Options.UseDefaultCredentials;
139-
handler.CookieContainer = CookieContainer;
140142
handler.AutomaticDecompression = Options.AutomaticDecompression;
141143
handler.PreAuthenticate = Options.PreAuthenticate;
142144
handler.AllowAutoRedirect = Options.FollowRedirects;

src/RestSharp/RestClientExtensions.Config.cs

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
// limitations under the License.
1414
//
1515

16-
using System.Net;
1716
using System.Text;
1817
using RestSharp.Authenticators;
1918
using RestSharp.Extensions;
@@ -43,23 +42,6 @@ public static partial class RestClientExtensions {
4342
public static RestClient UseQueryEncoder(this RestClient client, Func<string, Encoding, string> queryEncoder)
4443
=> client.With(x => x.EncodeQuery = queryEncoder);
4544

46-
/// <summary>
47-
/// Adds cookie to the <seealso cref="HttpClient"/> cookie container.
48-
/// </summary>
49-
/// <param name="client"></param>
50-
/// <param name="name">Cookie name</param>
51-
/// <param name="value">Cookie value</param>
52-
/// <param name="path">Cookie path</param>
53-
/// <param name="domain">Cookie domain, must not be an empty string</param>
54-
/// <returns></returns>
55-
public static RestClient AddCookie(this RestClient client, string name, string value, string path, string domain) {
56-
lock (client.CookieContainer) {
57-
client.CookieContainer.Add(new Cookie(name, value, path, domain));
58-
}
59-
60-
return client;
61-
}
62-
6345
public static RestClient UseAuthenticator(this RestClient client, IAuthenticator authenticator)
6446
=> client.With(x => x.Authenticator = authenticator);
6547
}

src/RestSharp/RestClientOptions.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ public RestClientOptions(string baseUrl) : this(new Uri(Ensure.NotEmptyString(ba
7474
public CacheControlHeaderValue? CachePolicy { get; set; }
7575
public bool FollowRedirects { get; set; } = true;
7676
public bool? Expect100Continue { get; set; } = null;
77-
public CookieContainer? CookieContainer { get; set; }
7877
public string? UserAgent { get; set; } = DefaultUserAgent;
7978

8079
/// <summary>

0 commit comments

Comments
 (0)