Skip to content

Commit 794348e

Browse files
Merge client- and request-level cookies in the header. Also, dispose the request if not downloading data. (#2056)
1 parent e147e1c commit 794348e

File tree

4 files changed

+78
-22
lines changed

4 files changed

+78
-22
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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+
using System.Net;
17+
18+
namespace RestSharp.Extensions;
19+
20+
static class CookieContainerExtensions {
21+
public static void AddCookies(this CookieContainer cookieContainer, Uri uri, IEnumerable<string> cookiesHeader) {
22+
foreach (var header in cookiesHeader) {
23+
try {
24+
cookieContainer.SetCookies(uri, header);
25+
}
26+
catch (CookieException) {
27+
// Do not fail request if we cannot parse a cookie
28+
}
29+
}
30+
}
31+
}

src/RestSharp/Request/RequestHeaders.cs

+14-3
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,24 @@ public RequestHeaders AddAcceptHeader(string[] acceptedContentTypes) {
3939
}
4040

4141
// Add Cookie header from the cookie container
42-
public RequestHeaders AddCookieHeaders(CookieContainer cookieContainer, Uri uri) {
42+
public RequestHeaders AddCookieHeaders(Uri uri, CookieContainer? cookieContainer) {
43+
if (cookieContainer == null) return this;
44+
4345
var cookies = cookieContainer.GetCookieHeader(uri);
4446

45-
if (cookies.Length > 0) {
46-
Parameters.AddParameter(new HeaderParameter(KnownHeaders.Cookie, cookies));
47+
if (string.IsNullOrWhiteSpace(cookies)) return this;
48+
49+
var newCookies = SplitHeader(cookies);
50+
var existing = Parameters.GetParameters<HeaderParameter>().FirstOrDefault(x => x.Name == KnownHeaders.Cookie);
51+
52+
if (existing?.Value != null) {
53+
newCookies = newCookies.Union(SplitHeader(existing.Value.ToString()!));
4754
}
4855

56+
Parameters.AddParameter(new HeaderParameter(KnownHeaders.Cookie, string.Join("; ", newCookies)));
57+
4958
return this;
59+
60+
IEnumerable<string> SplitHeader(string header) => header.Split(';').Select(x => x.Trim());
5061
}
5162
}

src/RestSharp/RestClient.Async.cs

+12-16
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ namespace RestSharp;
2121
public partial class RestClient {
2222
/// <inheritdoc />
2323
public async Task<RestResponse> ExecuteAsync(RestRequest request, CancellationToken cancellationToken = default) {
24-
var internalResponse = await ExecuteRequestAsync(request, cancellationToken).ConfigureAwait(false);
24+
using var internalResponse = await ExecuteRequestAsync(request, cancellationToken).ConfigureAwait(false);
2525

2626
var response = internalResponse.Exception == null
2727
? await RestResponse.FromHttpResponse(
@@ -85,7 +85,8 @@ async Task<HttpResponse> ExecuteRequestAsync(RestRequest request, CancellationTo
8585

8686
var httpMethod = AsHttpMethod(request.Method);
8787
var url = this.BuildUri(request);
88-
var message = new HttpRequestMessage(httpMethod, url) { Content = requestContent.BuildContent() };
88+
89+
using var message = new HttpRequestMessage(httpMethod, url) { Content = requestContent.BuildContent() };
8990
message.Headers.Host = Options.BaseHost;
9091
message.Headers.CacheControl = request.CachePolicy ?? Options.CachePolicy;
9192

@@ -102,11 +103,8 @@ async Task<HttpResponse> ExecuteRequestAsync(RestRequest request, CancellationTo
102103
.AddHeaders(request.Parameters)
103104
.AddHeaders(DefaultParameters)
104105
.AddAcceptHeader(AcceptedContentTypes)
105-
.AddCookieHeaders(cookieContainer, url);
106-
107-
if (Options.CookieContainer != null) {
108-
headers.AddCookieHeaders(Options.CookieContainer, url);
109-
}
106+
.AddCookieHeaders(url, cookieContainer)
107+
.AddCookieHeaders(url, Options.CookieContainer);
110108

111109
message.AddHeaders(headers);
112110

@@ -116,14 +114,10 @@ async Task<HttpResponse> ExecuteRequestAsync(RestRequest request, CancellationTo
116114

117115
// Parse all the cookies from the response and update the cookie jar with cookies
118116
if (responseMessage.Headers.TryGetValues(KnownHeaders.SetCookie, out var cookiesHeader)) {
119-
foreach (var header in cookiesHeader) {
120-
try {
121-
cookieContainer.SetCookies(url, header);
122-
}
123-
catch (CookieException) {
124-
// Do not fail request if we cannot parse a cookie
125-
}
126-
}
117+
// ReSharper disable once PossibleMultipleEnumeration
118+
cookieContainer.AddCookies(url, cookiesHeader);
119+
// ReSharper disable once PossibleMultipleEnumeration
120+
Options.CookieContainer?.AddCookies(url, cookiesHeader);
127121
}
128122

129123
if (request.OnAfterRequest != null) await request.OnAfterRequest(responseMessage).ConfigureAwait(false);
@@ -141,7 +135,9 @@ record HttpResponse(
141135
CookieContainer? CookieContainer,
142136
Exception? Exception,
143137
CancellationToken TimeoutToken
144-
);
138+
) : IDisposable {
139+
public void Dispose() => ResponseMessage?.Dispose();
140+
}
145141

146142
static HttpMethod AsHttpMethod(Method method)
147143
=> method switch {

test/RestSharp.Tests.Integrated/CookieTests.cs

+21-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ public class CookieTests {
99
readonly string _host;
1010

1111
public CookieTests(TestServerFixture fixture) {
12-
_client = new RestClient(fixture.Server.Url);
12+
var options = new RestClientOptions(fixture.Server.Url) {
13+
CookieContainer = new CookieContainer()
14+
};
15+
_client = new RestClient(options);
1316
_host = _client.Options.BaseUrl!.Host;
1417
}
1518

@@ -24,6 +27,21 @@ public async Task Can_Perform_GET_Async_With_Request_Cookies() {
2427
response.Content.Should().Be("[\"cookie=value\",\"cookie2=value2\"]");
2528
}
2629

30+
[Fact]
31+
public async Task Can_Perform_GET_Async_With_Request_And_Client_Cookies() {
32+
_client.Options.CookieContainer!.Add(new Cookie("clientCookie", "clientCookieValue", null, _host));
33+
34+
var request = new RestRequest("get-cookies") {
35+
CookieContainer = new CookieContainer()
36+
};
37+
request.CookieContainer.Add(new Cookie("cookie", "value", null, _host));
38+
request.CookieContainer.Add(new Cookie("cookie2", "value2", null, _host));
39+
var response = await _client.ExecuteAsync<string[]>(request);
40+
41+
var expected = new[] { "cookie=value", "cookie2=value2", "clientCookie=clientCookieValue" };
42+
response.Data.Should().BeEquivalentTo(expected);
43+
}
44+
2745
[Fact]
2846
public async Task Can_Perform_GET_Async_With_Response_Cookies() {
2947
var request = new RestRequest("set-cookies");
@@ -37,7 +55,7 @@ public async Task Can_Perform_GET_Async_With_Response_Cookies() {
3755
FindCookie("cookie5").Should().BeNull("Cookie 5 should vanish as the request is not SSL");
3856
AssertCookie("cookie6", "value6", x => x == DateTime.MinValue, true);
3957

40-
Cookie? FindCookie(string name) =>response!.Cookies!.FirstOrDefault(p => p.Name == name);
58+
Cookie? FindCookie(string name) => response.Cookies!.FirstOrDefault(p => p.Name == name);
4159

4260
void AssertCookie(string name, string value, Func<DateTime, bool> checkExpiration, bool httpOnly = false) {
4361
var c = FindCookie(name)!;
@@ -62,7 +80,7 @@ public async Task GET_Async_With_Response_Cookies_Should_Not_Fail_With_Cookie_Wi
6280
.SingleOrDefault(h => h.Name == KnownHeaders.SetCookie && ((string)h.Value!).StartsWith("cookie_empty_domain"));
6381
emptyDomainCookieHeader.Should().NotBeNull();
6482
((string)emptyDomainCookieHeader!.Value!).Should().Contain("domain=;");
65-
83+
6684
Cookie? FindCookie(string name) => response!.Cookies!.FirstOrDefault(p => p.Name == name);
6785
}
6886
}

0 commit comments

Comments
 (0)