Skip to content

Commit 2a03bde

Browse files
committed
[dotnet] Add transformation for network responses via CDP
1 parent e49e621 commit 2a03bde

File tree

6 files changed

+144
-33
lines changed

6 files changed

+144
-33
lines changed

dotnet/src/webdriver/DevTools/v85/V85Network.cs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -289,22 +289,25 @@ private void OnFetchRequestPaused(object sender, Fetch.RequestPausedEventArgs e)
289289
wrappedResponse.ResponseData.StatusCode = e.ResponseStatusCode.Value;
290290
}
291291

292-
foreach (var header in e.ResponseHeaders)
292+
if (e.ResponseHeaders != null)
293293
{
294-
if (header.Name.ToLowerInvariant() == "set-cookie")
294+
foreach (var header in e.ResponseHeaders)
295295
{
296-
wrappedResponse.ResponseData.CookieHeaders.Add(header.Value);
297-
}
298-
else
299-
{
300-
if (wrappedResponse.ResponseData.Headers.ContainsKey(header.Name))
296+
if (header.Name.ToLowerInvariant() == "set-cookie")
301297
{
302-
string currentHeaderValue = wrappedResponse.ResponseData.Headers[header.Name];
303-
wrappedResponse.ResponseData.Headers[header.Name] = currentHeaderValue + ", " + header.Value;
298+
wrappedResponse.ResponseData.CookieHeaders.Add(header.Value);
304299
}
305300
else
306301
{
307-
wrappedResponse.ResponseData.Headers.Add(header.Name, header.Value);
302+
if (wrappedResponse.ResponseData.Headers.ContainsKey(header.Name))
303+
{
304+
string currentHeaderValue = wrappedResponse.ResponseData.Headers[header.Name];
305+
wrappedResponse.ResponseData.Headers[header.Name] = currentHeaderValue + ", " + header.Value;
306+
}
307+
else
308+
{
309+
wrappedResponse.ResponseData.Headers.Add(header.Name, header.Value);
310+
}
308311
}
309312
}
310313
}

dotnet/src/webdriver/DevTools/v92/V92Network.cs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -290,22 +290,25 @@ private void OnFetchRequestPaused(object sender, Fetch.RequestPausedEventArgs e)
290290
wrappedResponse.ResponseData.StatusCode = e.ResponseStatusCode.Value;
291291
}
292292

293-
foreach (var header in e.ResponseHeaders)
293+
if (e.ResponseHeaders != null)
294294
{
295-
if (header.Name.ToLowerInvariant() == "set-cookie")
295+
foreach (var header in e.ResponseHeaders)
296296
{
297-
wrappedResponse.ResponseData.CookieHeaders.Add(header.Value);
298-
}
299-
else
300-
{
301-
if (wrappedResponse.ResponseData.Headers.ContainsKey(header.Name))
297+
if (header.Name.ToLowerInvariant() == "set-cookie")
302298
{
303-
string currentHeaderValue = wrappedResponse.ResponseData.Headers[header.Name];
304-
wrappedResponse.ResponseData.Headers[header.Name] = currentHeaderValue + ", " + header.Value;
299+
wrappedResponse.ResponseData.CookieHeaders.Add(header.Value);
305300
}
306301
else
307302
{
308-
wrappedResponse.ResponseData.Headers.Add(header.Name, header.Value);
303+
if (wrappedResponse.ResponseData.Headers.ContainsKey(header.Name))
304+
{
305+
string currentHeaderValue = wrappedResponse.ResponseData.Headers[header.Name];
306+
wrappedResponse.ResponseData.Headers[header.Name] = currentHeaderValue + ", " + header.Value;
307+
}
308+
else
309+
{
310+
wrappedResponse.ResponseData.Headers.Add(header.Name, header.Value);
311+
}
309312
}
310313
}
311314
}

dotnet/src/webdriver/DevTools/v93/V93Network.cs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -289,22 +289,25 @@ private void OnFetchRequestPaused(object sender, Fetch.RequestPausedEventArgs e)
289289
wrappedResponse.ResponseData.StatusCode = e.ResponseStatusCode.Value;
290290
}
291291

292-
foreach (var header in e.ResponseHeaders)
292+
if (e.ResponseHeaders != null)
293293
{
294-
if (header.Name.ToLowerInvariant() == "set-cookie")
294+
foreach (var header in e.ResponseHeaders)
295295
{
296-
wrappedResponse.ResponseData.CookieHeaders.Add(header.Value);
297-
}
298-
else
299-
{
300-
if (wrappedResponse.ResponseData.Headers.ContainsKey(header.Name))
296+
if (header.Name.ToLowerInvariant() == "set-cookie")
301297
{
302-
string currentHeaderValue = wrappedResponse.ResponseData.Headers[header.Name];
303-
wrappedResponse.ResponseData.Headers[header.Name] = currentHeaderValue + ", " + header.Value;
298+
wrappedResponse.ResponseData.CookieHeaders.Add(header.Value);
304299
}
305300
else
306301
{
307-
wrappedResponse.ResponseData.Headers.Add(header.Name, header.Value);
302+
if (wrappedResponse.ResponseData.Headers.ContainsKey(header.Name))
303+
{
304+
string currentHeaderValue = wrappedResponse.ResponseData.Headers[header.Name];
305+
wrappedResponse.ResponseData.Headers[header.Name] = currentHeaderValue + ", " + header.Value;
306+
}
307+
else
308+
{
309+
wrappedResponse.ResponseData.Headers.Add(header.Name, header.Value);
310+
}
308311
}
309312
}
310313
}

dotnet/src/webdriver/INetwork.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,18 @@ public interface INetwork
6363
/// </summary>
6464
void ClearAuthenticationHandlers();
6565

66+
/// <summary>
67+
/// Adds a <see cref="NetworkResponseHandler"/> to examine received network responses,
68+
/// and optionally modify the response.
69+
/// </summary>
70+
/// <param name="handler">The <see cref="NetworkResponseHandler"/> to add.</param>
71+
void AddResponseHandler(NetworkResponseHandler handler);
72+
73+
/// <summary>
74+
/// Clears all added <see cref="NetworkResponseHandler"/> instances.
75+
/// </summary>
76+
void ClearResponseHandlers();
77+
6678
/// <summary>
6779
/// Asynchronously starts monitoring for network traffic.
6880
/// </summary>

dotnet/src/webdriver/NetworkManager.cs

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,11 @@ public class NetworkManager : INetwork
3030
{
3131
private Lazy<DevToolsSession> session;
3232
private List<NetworkRequestHandler> requestHandlers = new List<NetworkRequestHandler>();
33+
private List<NetworkResponseHandler> responseHandlers = new List<NetworkResponseHandler>();
3334
private List<NetworkAuthenticationHandler> authenticationHandlers = new List<NetworkAuthenticationHandler>();
3435

3536
/// <summary>
36-
/// Initializes a new instance of the <see cref="RemoteNetwork"/> class.
37+
/// Initializes a new instance of the <see cref="NetworkManager"/> class.
3738
/// </summary>
3839
/// <param name="driver">The <see cref="IWebDriver"/> instance on which the network should be monitored.</param>
3940
public NetworkManager(IWebDriver driver)
@@ -161,6 +162,34 @@ public void ClearAuthenticationHandlers()
161162
this.authenticationHandlers.Clear();
162163
}
163164

165+
/// <summary>
166+
/// Adds a <see cref="NetworkResponseHandler"/> to examine received network responses,
167+
/// and optionally modify the response.
168+
/// </summary>
169+
/// <param name="handler">The <see cref="NetworkResponseHandler"/> to add.</param>
170+
public void AddResponseHandler(NetworkResponseHandler handler)
171+
{
172+
if (handler == null)
173+
{
174+
throw new ArgumentNullException("handler", "Request handler cannot be null");
175+
}
176+
177+
if (handler.ResponseMatcher == null)
178+
{
179+
throw new ArgumentException("Matcher for response cannot be null", "handler");
180+
}
181+
182+
this.responseHandlers.Add(handler);
183+
}
184+
185+
/// <summary>
186+
/// Clears all added <see cref="NetworkResponseHandler"/> instances.
187+
/// </summary>
188+
public void ClearResponseHandlers()
189+
{
190+
this.responseHandlers.Clear();
191+
}
192+
164193
private async void OnAuthRequired(object sender, AuthRequiredEventArgs e)
165194
{
166195
string requestId = e.RequestId;
@@ -213,12 +242,32 @@ private async void OnRequestPaused(object sender, RequestPausedEventArgs e)
213242

214243
private async void OnResponsePaused(object sender, ResponsePausedEventArgs e)
215244
{
216-
await this.session.Value.Domains.Network.AddResponseBody(e.ResponseData);
217-
await this.session.Value.Domains.Network.ContinueResponseWithoutModification(e.ResponseData);
245+
if (e.ResponseData.Headers.Count > 0)
246+
{
247+
// If no headers are present, the body cannot be retrieved.
248+
await this.session.Value.Domains.Network.AddResponseBody(e.ResponseData);
249+
}
250+
218251
if (this.NetworkResponseReceived != null)
219252
{
220253
this.NetworkResponseReceived(this, new NetworkResponseReceivedEventArgs(e.ResponseData));
221254
}
255+
256+
foreach (var handler in this.responseHandlers)
257+
{
258+
if (handler.ResponseMatcher.Invoke(e.ResponseData))
259+
{
260+
// NOTE: We create a dummy HttpRequestData object here, because the ContinueRequestWithResponse
261+
// method demands one; however, the only property used by that method is the RequestId property.
262+
// It might be better to refactor that method signature to simply pass the request ID, or
263+
// alternatively, just pass the response data, which should also contain the request ID anyway.
264+
HttpRequestData requestData = new HttpRequestData() { RequestId = e.ResponseData.RequestId };
265+
await this.session.Value.Domains.Network.ContinueRequestWithResponse(requestData, handler.ResponseTransformer(e.ResponseData));
266+
return;
267+
}
268+
}
269+
270+
await this.session.Value.Domains.Network.ContinueResponseWithoutModification(e.ResponseData);
222271
}
223272
}
224273
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// <copyright file="NetworkResponsetHandler.cs" company="WebDriver Committers">
2+
// Licensed to the Software Freedom Conservancy (SFC) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The SFC licenses this file
6+
// to you under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
// </copyright>
18+
19+
using System;
20+
21+
namespace OpenQA.Selenium
22+
{
23+
/// <summary>
24+
/// Allows a user to handle a returned network, potentially modifying it before processing by the browser.
25+
/// </summary>
26+
public class NetworkResponseHandler
27+
{
28+
/// <summary>
29+
/// Gets or sets a function that evaluates returned response data in an <see cref="HttpResponseData"/> object,
30+
/// and returns a value indicating whether the data matches the specified criteria.
31+
/// </summary>
32+
public Func<HttpResponseData, bool> ResponseMatcher { get; set; }
33+
34+
/// <summary>
35+
/// Gets or sets a function that accepts an <see cref="HttpResponseData"/> object describing a network
36+
/// response received by the browser, and returns a modified <see cref="HttpResponseData"/> object to used
37+
/// as the actual network response.
38+
/// </summary>
39+
public Func<HttpResponseData, HttpResponseData> ResponseTransformer { get; set; }
40+
}
41+
}

0 commit comments

Comments
 (0)