Skip to content

Commit ccf6e01

Browse files
kblokMeir017
authored andcommitted
Implement Request.RedirectChain API (#387)
* Implement Request.RedirectChain API * Self-review
1 parent f366438 commit ccf6e01

File tree

4 files changed

+110
-16
lines changed

4 files changed

+110
-16
lines changed

lib/PuppeteerSharp.Tests/NetworkTests/NetworkEventTests.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ public async Task ShouldSupportRedirects()
204204
Page.RequestFailed += (sender, e) => events.Add($"FAIL {e.Request.Url}");
205205
Server.SetRedirect("/foo.html", "/empty.html");
206206
const string FOO_URL = TestConstants.ServerUrl + "/foo.html";
207-
await Page.GoToAsync(FOO_URL);
207+
var response = await Page.GoToAsync(FOO_URL);
208208
Assert.Equal(new[] {
209209
$"GET {FOO_URL}",
210210
$"302 {FOO_URL}",
@@ -213,6 +213,11 @@ public async Task ShouldSupportRedirects()
213213
$"200 {TestConstants.EmptyPage}",
214214
$"DONE {TestConstants.EmptyPage}"
215215
}, events);
216+
217+
// Check redirect chain
218+
var redirectChain = response.Request.RedirectChain;
219+
Assert.Single(redirectChain);
220+
Assert.Contains("/foo.html", redirectChain[0].Url);
216221
}
217222
}
218-
}
223+
}

lib/PuppeteerSharp.Tests/PageTests/SetRequestInterceptionTests.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,18 @@ public async Task ShouldWorkWithRedirects()
190190
Assert.Contains("empty.html", response.Url);
191191
Assert.Equal(5, requests.Count);
192192
Assert.Equal(ResourceType.Document, requests[2].ResourceType);
193+
194+
// Check redirect chain
195+
var redirectChain = response.Request.RedirectChain;
196+
Assert.Equal(4, redirectChain.Length);
197+
Assert.Contains("/non-existing-page.html", redirectChain[0].Url);
198+
Assert.Contains("/non-existing-page-3.html", redirectChain[2].Url);
199+
200+
for (var i = 0; i < redirectChain.Length; ++i)
201+
{
202+
var request = redirectChain[i];
203+
Assert.Equal(request, request.RedirectChain.ElementAt(i));
204+
}
193205
}
194206

195207
[Fact]
@@ -380,4 +392,4 @@ public async Task ShouldThrowIfInterceptionIsNotEnabled()
380392
Assert.Contains("Request Interception is not enabled", exception.Message);
381393
}
382394
}
383-
}
395+
}

lib/PuppeteerSharp/NetworkManager.cs

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,14 @@ private async Task OnRequestInterceptedAsync(RequestInterceptedResponse e)
247247
var request = _interceptionIdToRequest[e.InterceptionId];
248248

249249
HandleRequestRedirect(request, e.ResponseStatusCode, e.ResponseHeaders, false, false);
250-
HandleRequestStart(request.RequestId, e.InterceptionId, e.RedirectUrl, e.ResourceType, e.Request, e.FrameId);
250+
HandleRequestStart(
251+
request.RequestId,
252+
e.InterceptionId,
253+
e.RedirectUrl,
254+
e.ResourceType,
255+
e.Request,
256+
e.FrameId,
257+
request.RedirectChainList);
251258
return;
252259
}
253260

@@ -256,12 +263,26 @@ private async Task OnRequestInterceptedAsync(RequestInterceptedResponse e)
256263
if (requestId != null)
257264
{
258265
_requestHashToRequestIds.Delete(requestHash, requestId);
259-
HandleRequestStart(requestId, e.InterceptionId, e.Request.Url, e.ResourceType, e.Request, e.FrameId);
266+
HandleRequestStart(
267+
requestId,
268+
e.InterceptionId,
269+
e.Request.Url,
270+
e.ResourceType,
271+
e.Request,
272+
e.FrameId,
273+
new List<Request>());
260274
}
261275
else
262276
{
263277
_requestHashToInterceptionIds.Add(requestHash, e.InterceptionId);
264-
HandleRequestStart(null, e.InterceptionId, e.Request.Url, e.ResourceType, e.Request, e.FrameId);
278+
HandleRequestStart(
279+
null,
280+
e.InterceptionId,
281+
e.Request.Url,
282+
e.ResourceType,
283+
e.Request,
284+
e.FrameId,
285+
new List<Request>());
265286
}
266287
}
267288

@@ -279,7 +300,8 @@ private void HandleRequestStart(
279300
string url,
280301
ResourceType resourceType,
281302
Payload requestPayload,
282-
string frameId)
303+
string frameId,
304+
List<Request> redirectChain)
283305
{
284306
Frame frame = null;
285307

@@ -296,7 +318,8 @@ private void HandleRequestStart(
296318
url,
297319
resourceType,
298320
requestPayload,
299-
frame);
321+
frame,
322+
redirectChain);
300323

301324
if (!string.IsNullOrEmpty(requestId))
302325
{
@@ -331,6 +354,7 @@ private void HandleRequestRedirect(
331354
securityDetails);
332355

333356
request.Response = response;
357+
request.RedirectChainList.Add(request);
334358
if (request.RequestId != null)
335359
{
336360
_requestIdToRequest.Remove(request.RequestId);
@@ -355,6 +379,8 @@ private void HandleRequestRedirect(
355379

356380
private void OnRequestWillBeSent(RequestWillBeSentResponse e)
357381
{
382+
var redirectChain = new List<Request>();
383+
358384
if (_protocolRequestInterceptionEnabled)
359385
{
360386
// All redirects are handled in requestIntercepted.
@@ -388,9 +414,11 @@ private void OnRequestWillBeSent(RequestWillBeSentResponse e)
388414
e.RedirectResponse.FromDiskCache,
389415
e.RedirectResponse.FromServiceWorker,
390416
e.RedirectResponse.SecurityDetails);
417+
418+
redirectChain = request.RedirectChainList;
391419
}
392420

393-
HandleRequestStart(e.RequestId, null, e.Request.Url, e.Type, e.Request, e.FrameId);
421+
HandleRequestStart(e.RequestId, null, e.Request.Url, e.Type, e.Request, e.FrameId, redirectChain);
394422
}
395423

396424
private async Task UpdateProtocolRequestInterceptionAsync()
@@ -420,4 +448,4 @@ await Task.WhenAll(
420448
}
421449
#endregion
422450
}
423-
}
451+
}

lib/PuppeteerSharp/Request.cs

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,16 @@ public class Request : Payload
2929
internal Request()
3030
{
3131
}
32-
internal Request(CDPSession client, string requestId, string interceptionId, bool allowInterception, string url,
33-
ResourceType resourceType, Payload payload, Frame frame)
32+
internal Request(
33+
CDPSession client,
34+
string requestId,
35+
string interceptionId,
36+
bool allowInterception,
37+
string url,
38+
ResourceType resourceType,
39+
Payload payload,
40+
Frame frame,
41+
List<Request> redirectChain)
3442
{
3543
_client = client;
3644
_allowInterception = allowInterception;
@@ -44,6 +52,7 @@ internal Request(CDPSession client, string requestId, string interceptionId, boo
4452
Method = payload.Method;
4553
PostData = payload.PostData;
4654
Frame = frame;
55+
RedirectChainList = redirectChain;
4756

4857
Headers = new Dictionary<string, object>();
4958
foreach (KeyValuePair<string, object> keyValue in payload.Headers)
@@ -87,9 +96,34 @@ internal Request(CDPSession client, string requestId, string interceptionId, boo
8796
/// <value>The frame.</value>
8897
public Frame Frame { get; }
8998

99+
/// <summary>
100+
/// A redirectChain is a chain of requests initiated to fetch a resource.
101+
/// If there are no redirects and the request was successful, the chain will be empty.
102+
/// If a server responds with at least a single redirect, then the chain will contain all the requests that were redirected.
103+
/// redirectChain is shared between all the requests of the same chain.
104+
/// </summary>
105+
/// <example>
106+
/// For example, if the website http://example.com has a single redirect to https://example.com, then the chain will contain one request:
107+
/// <code>
108+
/// var response = await page.GoToAsync("http://example.com");
109+
/// var chain = response.Request.RedirectChain;
110+
/// Console.WriteLine(chain.Length); // 1
111+
/// Console.WriteLine(chain[0].Url); // 'http://example.com'
112+
/// </code>
113+
/// If the website https://google.com has no redirects, then the chain will be empty:
114+
/// <code>
115+
/// var response = await page.GoToAsync("https://google.com");
116+
/// var chain = response.Request.RedirectChain;
117+
/// Console.WriteLine(chain.Length); // 0
118+
/// </code>
119+
/// </example>
120+
/// <value>The redirect chain.</value>
121+
public Request[] RedirectChain => RedirectChainList.ToArray();
122+
90123
internal bool FromMemoryCache { get; set; }
91124
internal Task<bool> CompleteTask => CompleteTaskWrapper.Task;
92125
internal TaskCompletionSource<bool> CompleteTaskWrapper { get; set; }
126+
internal List<Request> RedirectChainList { get; }
93127
#endregion
94128

95129
#region Public Methods
@@ -115,10 +149,25 @@ public async Task ContinueAsync(Payload overrides = null)
115149
try
116150
{
117151
var requestData = new Dictionary<string, object> { ["interceptionId"] = InterceptionId };
118-
if (overrides?.Url != null) requestData["url"] = overrides.Url;
119-
if (overrides?.Method != null) requestData["method"] = overrides.Method;
120-
if (overrides?.PostData != null) requestData["postData"] = overrides.PostData;
121-
if (overrides?.Headers != null) requestData["headers"] = overrides.Headers;
152+
if (overrides?.Url != null)
153+
{
154+
requestData["url"] = overrides.Url;
155+
}
156+
157+
if (overrides?.Method != null)
158+
{
159+
requestData["method"] = overrides.Method;
160+
}
161+
162+
if (overrides?.PostData != null)
163+
{
164+
requestData["postData"] = overrides.PostData;
165+
}
166+
167+
if (overrides?.Headers != null)
168+
{
169+
requestData["headers"] = overrides.Headers;
170+
}
122171

123172
await _client.SendAsync("Network.continueInterceptedRequest", requestData);
124173
}

0 commit comments

Comments
 (0)