Skip to content

Commit 117feaa

Browse files
committed
WIP
1 parent d4b6f55 commit 117feaa

File tree

4 files changed

+118
-11
lines changed

4 files changed

+118
-11
lines changed

src/RestSharp/Request/RestRequestExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ public static RestRequest AddBody(this RestRequest request, object obj, ContentT
338338
DataFormat.Json => request.AddJsonBody(obj, contentType),
339339
DataFormat.Xml => request.AddXmlBody(obj, contentType),
340340
DataFormat.Binary => request.AddParameter(new BodyParameter("", obj, ContentType.Binary)),
341-
_ => request.AddParameter(new BodyParameter("", obj.ToString(), ContentType.Plain))
341+
_ => request.AddParameter(new BodyParameter("", obj.ToString()!, ContentType.Plain))
342342
};
343343
}
344344

src/RestSharp/RestClient.Async.cs

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,16 +86,11 @@ async Task<HttpResponse> ExecuteRequestAsync(RestRequest request, CancellationTo
8686
throw new ObjectDisposedException(nameof(RestClient));
8787
}
8888

89-
using var requestContent = new RequestContent(this, request);
90-
9189
var authenticator = request.Authenticator ?? Options.Authenticator;
9290
if (authenticator != null) await authenticator.Authenticate(this, request).ConfigureAwait(false);
9391

9492
var httpMethod = AsHttpMethod(request.Method);
9593
var url = this.BuildUri(request);
96-
var message = new HttpRequestMessage(httpMethod, url) { Content = requestContent.BuildContent() };
97-
message.Headers.Host = Options.BaseHost;
98-
message.Headers.CacheControl = Options.CachePolicy;
9994

10095
using var timeoutCts = new CancellationTokenSource(request.Timeout > 0 ? request.Timeout : int.MaxValue);
10196
using var cts = CancellationTokenSource.CreateLinkedTokenSource(timeoutCts.Token, cancellationToken);
@@ -116,11 +111,50 @@ async Task<HttpResponse> ExecuteRequestAsync(RestRequest request, CancellationTo
116111
headers.AddCookieHeaders(Options.CookieContainer, url);
117112
}
118113

119-
message.AddHeaders(headers);
114+
HttpResponseMessage? responseMessage;
115+
116+
while (true) {
117+
using var requestContent = new RequestContent(this, request);
118+
using var message = PrepareRequestMessage(httpMethod, url, requestContent, headers);
120119

121-
if (request.OnBeforeRequest != null) await request.OnBeforeRequest(message).ConfigureAwait(false);
120+
if (request.OnBeforeRequest != null) await request.OnBeforeRequest(message).ConfigureAwait(false);
121+
122+
responseMessage = await HttpClient.SendAsync(message, request.CompletionOption, ct).ConfigureAwait(false);
123+
124+
if (request.OnAfterRequest != null) await request.OnAfterRequest(responseMessage).ConfigureAwait(false);
125+
126+
if (!IsRedirect(responseMessage)) {
127+
// || !Options.FollowRedirects) {
128+
break;
129+
}
130+
131+
var location = responseMessage.Headers.Location;
132+
133+
if (location == null) {
134+
break;
135+
}
136+
137+
if (!location.IsAbsoluteUri) {
138+
location = new Uri(url, location);
139+
}
140+
141+
if (responseMessage.StatusCode == HttpStatusCode.RedirectMethod) {
142+
httpMethod = HttpMethod.Get;
143+
}
122144

123-
var responseMessage = await HttpClient.SendAsync(message, request.CompletionOption, ct).ConfigureAwait(false);
145+
url = location;
146+
147+
if (responseMessage.Headers.TryGetValues(KnownHeaders.SetCookie, out var ch)) {
148+
foreach (var header in ch) {
149+
try {
150+
cookieContainer.SetCookies(url, header);
151+
}
152+
catch (CookieException) {
153+
// Do not fail request if we cannot parse a cookie
154+
}
155+
}
156+
}
157+
}
124158

125159
// Parse all the cookies from the response and update the cookie jar with cookies
126160
if (responseMessage.Headers.TryGetValues(KnownHeaders.SetCookie, out var cookiesHeader)) {
@@ -134,15 +168,34 @@ async Task<HttpResponse> ExecuteRequestAsync(RestRequest request, CancellationTo
134168
}
135169
}
136170

137-
if (request.OnAfterRequest != null) await request.OnAfterRequest(responseMessage).ConfigureAwait(false);
138-
139171
return new HttpResponse(responseMessage, url, cookieContainer, null, timeoutCts.Token);
140172
}
141173
catch (Exception ex) {
142174
return new HttpResponse(null, url, null, ex, timeoutCts.Token);
143175
}
144176
}
145177

178+
HttpRequestMessage PrepareRequestMessage(HttpMethod httpMethod, Uri url, RequestContent requestContent, RequestHeaders headers) {
179+
var message = new HttpRequestMessage(httpMethod, url) { Content = requestContent.BuildContent() };
180+
message.Headers.Host = Options.BaseHost;
181+
message.Headers.CacheControl = Options.CachePolicy;
182+
message.AddHeaders(headers);
183+
184+
return message;
185+
}
186+
187+
static bool IsRedirect(HttpResponseMessage responseMessage)
188+
=> responseMessage.StatusCode switch {
189+
HttpStatusCode.MovedPermanently => true,
190+
HttpStatusCode.SeeOther => true,
191+
HttpStatusCode.TemporaryRedirect => true,
192+
HttpStatusCode.Redirect => true,
193+
#if NET
194+
HttpStatusCode.PermanentRedirect => true,
195+
#endif
196+
_ => false
197+
};
198+
146199
record HttpResponse(
147200
HttpResponseMessage? ResponseMessage,
148201
Uri Url,
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using System.Net;
2+
using RestSharp.Tests.Integrated.Server;
3+
4+
namespace RestSharp.Tests.Integrated;
5+
6+
[Collection(nameof(TestServerCollection))]
7+
public class RedirectTests {
8+
readonly RestClient _client;
9+
readonly string _host;
10+
11+
public RedirectTests(TestServerFixture fixture, ITestOutputHelper output) {
12+
var options = new RestClientOptions(fixture.Server.Url) {
13+
FollowRedirects = false
14+
};
15+
_client = new RestClient(options);
16+
_host = _client.Options.BaseUrl!.Host;
17+
}
18+
19+
[Fact]
20+
public async Task Can_Perform_GET_Async_With_Redirect() {
21+
const string val = "Works!";
22+
23+
var request = new RestRequest("redirect");
24+
25+
var response = await _client.ExecuteAsync<Response>(request);
26+
response.StatusCode.Should().Be(HttpStatusCode.OK);
27+
response.Data!.Message.Should().Be(val);
28+
}
29+
30+
[Fact]
31+
public async Task Can_Perform_GET_Async_With_Request_Cookies() {
32+
var request = new RestRequest("get-cookies-redirect") {
33+
CookieContainer = new CookieContainer(),
34+
};
35+
request.CookieContainer.Add(new Cookie("cookie", "value", null, _host));
36+
request.CookieContainer.Add(new Cookie("cookie2", "value2", null, _host));
37+
var response = await _client.ExecuteAsync(request);
38+
response.Content.Should().Be("[\"cookie=value\",\"cookie2=value2\"]");
39+
}
40+
41+
class Response {
42+
public string? Message { get; set; }
43+
}
44+
}

test/RestSharp.Tests.Integrated/Server/TestServer.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Microsoft.Extensions.Logging;
66
using RestSharp.Tests.Integrated.Server.Handlers;
77
using RestSharp.Tests.Shared.Extensions;
8+
89
// ReSharper disable ConvertClosureToMethodGroup
910

1011
namespace RestSharp.Tests.Integrated.Server;
@@ -36,11 +37,20 @@ public HttpServer(ITestOutputHelper? output = null) {
3637
_app.MapGet("headers", HeaderHandlers.HandleHeaders);
3738
_app.MapGet("request-echo", async context => await context.Request.BodyReader.AsStream().CopyToAsync(context.Response.BodyWriter.AsStream()));
3839
_app.MapDelete("delete", () => new TestResponse { Message = "Works!" });
40+
_app.MapGet("redirect", () => Results.Redirect("/success", false, true));
3941

4042
// Cookies
4143
_app.MapGet("get-cookies", CookieHandlers.HandleCookies);
4244
_app.MapGet("set-cookies", CookieHandlers.HandleSetCookies);
4345

46+
_app.MapGet(
47+
"get-cookies-redirect",
48+
(HttpContext ctx) => {
49+
ctx.Response.Cookies.Append("redirectCookie", "value1");
50+
return Results.Redirect("/get-cookies", false, true);
51+
}
52+
);
53+
4454
// PUT
4555
_app.MapPut(
4656
ContentResource,

0 commit comments

Comments
 (0)