diff --git a/dotnet/src/webdriver/DevTools/v85/V85Network.cs b/dotnet/src/webdriver/DevTools/v85/V85Network.cs
index d0bdcba4339f3..a5d4f9534fcb9 100644
--- a/dotnet/src/webdriver/DevTools/v85/V85Network.cs
+++ b/dotnet/src/webdriver/DevTools/v85/V85Network.cs
@@ -289,22 +289,25 @@ private void OnFetchRequestPaused(object sender, Fetch.RequestPausedEventArgs e)
wrappedResponse.ResponseData.StatusCode = e.ResponseStatusCode.Value;
}
- foreach (var header in e.ResponseHeaders)
+ if (e.ResponseHeaders != null)
{
- if (header.Name.ToLowerInvariant() == "set-cookie")
+ foreach (var header in e.ResponseHeaders)
{
- wrappedResponse.ResponseData.CookieHeaders.Add(header.Value);
- }
- else
- {
- if (wrappedResponse.ResponseData.Headers.ContainsKey(header.Name))
+ if (header.Name.ToLowerInvariant() == "set-cookie")
{
- string currentHeaderValue = wrappedResponse.ResponseData.Headers[header.Name];
- wrappedResponse.ResponseData.Headers[header.Name] = currentHeaderValue + ", " + header.Value;
+ wrappedResponse.ResponseData.CookieHeaders.Add(header.Value);
}
else
{
- wrappedResponse.ResponseData.Headers.Add(header.Name, header.Value);
+ if (wrappedResponse.ResponseData.Headers.ContainsKey(header.Name))
+ {
+ string currentHeaderValue = wrappedResponse.ResponseData.Headers[header.Name];
+ wrappedResponse.ResponseData.Headers[header.Name] = currentHeaderValue + ", " + header.Value;
+ }
+ else
+ {
+ wrappedResponse.ResponseData.Headers.Add(header.Name, header.Value);
+ }
}
}
}
diff --git a/dotnet/src/webdriver/DevTools/v92/V92Network.cs b/dotnet/src/webdriver/DevTools/v92/V92Network.cs
index 2b1816a59badb..0c47ad6ebfd7d 100644
--- a/dotnet/src/webdriver/DevTools/v92/V92Network.cs
+++ b/dotnet/src/webdriver/DevTools/v92/V92Network.cs
@@ -290,22 +290,25 @@ private void OnFetchRequestPaused(object sender, Fetch.RequestPausedEventArgs e)
wrappedResponse.ResponseData.StatusCode = e.ResponseStatusCode.Value;
}
- foreach (var header in e.ResponseHeaders)
+ if (e.ResponseHeaders != null)
{
- if (header.Name.ToLowerInvariant() == "set-cookie")
+ foreach (var header in e.ResponseHeaders)
{
- wrappedResponse.ResponseData.CookieHeaders.Add(header.Value);
- }
- else
- {
- if (wrappedResponse.ResponseData.Headers.ContainsKey(header.Name))
+ if (header.Name.ToLowerInvariant() == "set-cookie")
{
- string currentHeaderValue = wrappedResponse.ResponseData.Headers[header.Name];
- wrappedResponse.ResponseData.Headers[header.Name] = currentHeaderValue + ", " + header.Value;
+ wrappedResponse.ResponseData.CookieHeaders.Add(header.Value);
}
else
{
- wrappedResponse.ResponseData.Headers.Add(header.Name, header.Value);
+ if (wrappedResponse.ResponseData.Headers.ContainsKey(header.Name))
+ {
+ string currentHeaderValue = wrappedResponse.ResponseData.Headers[header.Name];
+ wrappedResponse.ResponseData.Headers[header.Name] = currentHeaderValue + ", " + header.Value;
+ }
+ else
+ {
+ wrappedResponse.ResponseData.Headers.Add(header.Name, header.Value);
+ }
}
}
}
diff --git a/dotnet/src/webdriver/DevTools/v93/V93Network.cs b/dotnet/src/webdriver/DevTools/v93/V93Network.cs
index 0c23e875ffa3e..29e42058ac718 100644
--- a/dotnet/src/webdriver/DevTools/v93/V93Network.cs
+++ b/dotnet/src/webdriver/DevTools/v93/V93Network.cs
@@ -289,22 +289,25 @@ private void OnFetchRequestPaused(object sender, Fetch.RequestPausedEventArgs e)
wrappedResponse.ResponseData.StatusCode = e.ResponseStatusCode.Value;
}
- foreach (var header in e.ResponseHeaders)
+ if (e.ResponseHeaders != null)
{
- if (header.Name.ToLowerInvariant() == "set-cookie")
+ foreach (var header in e.ResponseHeaders)
{
- wrappedResponse.ResponseData.CookieHeaders.Add(header.Value);
- }
- else
- {
- if (wrappedResponse.ResponseData.Headers.ContainsKey(header.Name))
+ if (header.Name.ToLowerInvariant() == "set-cookie")
{
- string currentHeaderValue = wrappedResponse.ResponseData.Headers[header.Name];
- wrappedResponse.ResponseData.Headers[header.Name] = currentHeaderValue + ", " + header.Value;
+ wrappedResponse.ResponseData.CookieHeaders.Add(header.Value);
}
else
{
- wrappedResponse.ResponseData.Headers.Add(header.Name, header.Value);
+ if (wrappedResponse.ResponseData.Headers.ContainsKey(header.Name))
+ {
+ string currentHeaderValue = wrappedResponse.ResponseData.Headers[header.Name];
+ wrappedResponse.ResponseData.Headers[header.Name] = currentHeaderValue + ", " + header.Value;
+ }
+ else
+ {
+ wrappedResponse.ResponseData.Headers.Add(header.Name, header.Value);
+ }
}
}
}
diff --git a/dotnet/src/webdriver/INetwork.cs b/dotnet/src/webdriver/INetwork.cs
index a9ca439fafa7f..d0bf5aa292add 100644
--- a/dotnet/src/webdriver/INetwork.cs
+++ b/dotnet/src/webdriver/INetwork.cs
@@ -63,6 +63,18 @@ public interface INetwork
///
void ClearAuthenticationHandlers();
+ ///
+ /// Adds a to examine received network responses,
+ /// and optionally modify the response.
+ ///
+ /// The to add.
+ void AddResponseHandler(NetworkResponseHandler handler);
+
+ ///
+ /// Clears all added instances.
+ ///
+ void ClearResponseHandlers();
+
///
/// Asynchronously starts monitoring for network traffic.
///
diff --git a/dotnet/src/webdriver/NetworkManager.cs b/dotnet/src/webdriver/NetworkManager.cs
index d715429dde30a..7dd3df2b898e8 100644
--- a/dotnet/src/webdriver/NetworkManager.cs
+++ b/dotnet/src/webdriver/NetworkManager.cs
@@ -30,10 +30,11 @@ public class NetworkManager : INetwork
{
private Lazy session;
private List requestHandlers = new List();
+ private List responseHandlers = new List();
private List authenticationHandlers = new List();
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
/// The instance on which the network should be monitored.
public NetworkManager(IWebDriver driver)
@@ -161,6 +162,34 @@ public void ClearAuthenticationHandlers()
this.authenticationHandlers.Clear();
}
+ ///
+ /// Adds a to examine received network responses,
+ /// and optionally modify the response.
+ ///
+ /// The to add.
+ public void AddResponseHandler(NetworkResponseHandler handler)
+ {
+ if (handler == null)
+ {
+ throw new ArgumentNullException("handler", "Request handler cannot be null");
+ }
+
+ if (handler.ResponseMatcher == null)
+ {
+ throw new ArgumentException("Matcher for response cannot be null", "handler");
+ }
+
+ this.responseHandlers.Add(handler);
+ }
+
+ ///
+ /// Clears all added instances.
+ ///
+ public void ClearResponseHandlers()
+ {
+ this.responseHandlers.Clear();
+ }
+
private async void OnAuthRequired(object sender, AuthRequiredEventArgs e)
{
string requestId = e.RequestId;
@@ -213,12 +242,32 @@ private async void OnRequestPaused(object sender, RequestPausedEventArgs e)
private async void OnResponsePaused(object sender, ResponsePausedEventArgs e)
{
- await this.session.Value.Domains.Network.AddResponseBody(e.ResponseData);
- await this.session.Value.Domains.Network.ContinueResponseWithoutModification(e.ResponseData);
+ if (e.ResponseData.Headers.Count > 0)
+ {
+ // If no headers are present, the body cannot be retrieved.
+ await this.session.Value.Domains.Network.AddResponseBody(e.ResponseData);
+ }
+
if (this.NetworkResponseReceived != null)
{
this.NetworkResponseReceived(this, new NetworkResponseReceivedEventArgs(e.ResponseData));
}
+
+ foreach (var handler in this.responseHandlers)
+ {
+ if (handler.ResponseMatcher.Invoke(e.ResponseData))
+ {
+ // NOTE: We create a dummy HttpRequestData object here, because the ContinueRequestWithResponse
+ // method demands one; however, the only property used by that method is the RequestId property.
+ // It might be better to refactor that method signature to simply pass the request ID, or
+ // alternatively, just pass the response data, which should also contain the request ID anyway.
+ HttpRequestData requestData = new HttpRequestData() { RequestId = e.ResponseData.RequestId };
+ await this.session.Value.Domains.Network.ContinueRequestWithResponse(requestData, handler.ResponseTransformer(e.ResponseData));
+ return;
+ }
+ }
+
+ await this.session.Value.Domains.Network.ContinueResponseWithoutModification(e.ResponseData);
}
}
}
diff --git a/dotnet/src/webdriver/NetworkResponseHandler.cs b/dotnet/src/webdriver/NetworkResponseHandler.cs
new file mode 100644
index 0000000000000..bce62c7e0fe4c
--- /dev/null
+++ b/dotnet/src/webdriver/NetworkResponseHandler.cs
@@ -0,0 +1,41 @@
+//
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+using System;
+
+namespace OpenQA.Selenium
+{
+ ///
+ /// Allows a user to handle a returned network, potentially modifying it before processing by the browser.
+ ///
+ public class NetworkResponseHandler
+ {
+ ///
+ /// Gets or sets a function that evaluates returned response data in an object,
+ /// and returns a value indicating whether the data matches the specified criteria.
+ ///
+ public Func ResponseMatcher { get; set; }
+
+ ///
+ /// Gets or sets a function that accepts an object describing a network
+ /// response received by the browser, and returns a modified object to used
+ /// as the actual network response.
+ ///
+ public Func ResponseTransformer { get; set; }
+ }
+}