Skip to content

Commit fc28c02

Browse files
authored
[dotnet] [bidi] Support network collectors and get response body (#16192)
1 parent 29af98d commit fc28c02

File tree

11 files changed

+296
-1
lines changed

11 files changed

+296
-1
lines changed

dotnet/src/webdriver/BiDi/Communication/Broker.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ internal Broker(BiDi bidi, Uri url)
7575
new BrowserUserContextConverter(bidi),
7676
new BrowserClientWindowConverter(),
7777
new NavigationConverter(),
78+
new CollectorConverter(_bidi),
7879
new InterceptConverter(_bidi),
7980
new RequestConverter(),
8081
new ChannelConverter(),

dotnet/src/webdriver/BiDi/Communication/Json/BiDiJsonSerializerContext.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,18 @@ namespace OpenQA.Selenium.BiDi.Communication.Json;
118118
[JsonSerializable(typeof(BrowsingContext.UserPromptOpenedEventArgs))]
119119
[JsonSerializable(typeof(BrowsingContext.UserPromptClosedEventArgs))]
120120

121+
[JsonSerializable(typeof(Network.AddDataCollectorCommand))]
122+
[JsonSerializable(typeof(Network.AddDataCollectorResult))]
121123
[JsonSerializable(typeof(Network.AddInterceptCommand))]
122124
[JsonSerializable(typeof(Network.AddInterceptResult))]
123125
[JsonSerializable(typeof(Network.ContinueRequestCommand))]
124126
[JsonSerializable(typeof(Network.ContinueResponseCommand))]
125127
[JsonSerializable(typeof(Network.ContinueWithAuthCommand))]
126128
[JsonSerializable(typeof(Network.FailRequestCommand))]
129+
[JsonSerializable(typeof(Network.GetDataCommand))]
130+
[JsonSerializable(typeof(Network.GetDataResult))]
127131
[JsonSerializable(typeof(Network.ProvideResponseCommand))]
132+
[JsonSerializable(typeof(Network.RemoveDataCollectorCommand))]
128133
[JsonSerializable(typeof(Network.RemoveInterceptCommand))]
129134
[JsonSerializable(typeof(Network.SetCacheBehaviorCommand))]
130135

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// <copyright file="CollectorConverter.cs" company="Selenium 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
7+
// "License"); you may not use this file except in compliance
8+
// with the License. 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,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
// </copyright>
19+
20+
using OpenQA.Selenium.BiDi.Network;
21+
using System;
22+
using System.Text.Json;
23+
using System.Text.Json.Serialization;
24+
25+
namespace OpenQA.Selenium.BiDi.Communication.Json.Converters;
26+
27+
internal class CollectorConverter : JsonConverter<Collector>
28+
{
29+
private readonly BiDi _bidi;
30+
31+
public CollectorConverter(BiDi bidi)
32+
{
33+
_bidi = bidi;
34+
}
35+
36+
public override Collector? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
37+
{
38+
var id = reader.GetString();
39+
40+
return new Collector(_bidi, id!);
41+
}
42+
43+
public override void Write(Utf8JsonWriter writer, Collector value, JsonSerializerOptions options)
44+
{
45+
writer.WriteStringValue(value.Id);
46+
}
47+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// <copyright file="AddDataCollectorCommand.cs" company="Selenium 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
7+
// "License"); you may not use this file except in compliance
8+
// with the License. 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,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
// </copyright>
19+
20+
using System.Collections.Generic;
21+
using OpenQA.Selenium.BiDi.Communication;
22+
23+
namespace OpenQA.Selenium.BiDi.Network;
24+
25+
internal sealed class AddDataCollectorCommand(AddDataCollectorParameters @params)
26+
: Command<AddDataCollectorParameters, AddDataCollectorResult>(@params, "network.addDataCollector");
27+
28+
internal sealed record AddDataCollectorParameters(IEnumerable<DataType> DataTypes, int MaxEncodedDataSize, CollectorType? CollectorType, IEnumerable<BrowsingContext.BrowsingContext>? Contexts, IEnumerable<Browser.UserContext>? UserContexts) : Parameters;
29+
30+
public class AddDataCollectorOptions : CommandOptions
31+
{
32+
public CollectorType? CollectorType { get; set; }
33+
34+
public IEnumerable<BrowsingContext.BrowsingContext>? Contexts { get; set; }
35+
36+
public IEnumerable<Browser.UserContext>? UserContexts { get; set; }
37+
}
38+
39+
public sealed record AddDataCollectorResult(Collector Collector) : EmptyResult;
40+
41+
public enum DataType
42+
{
43+
Response
44+
}
45+
46+
public enum CollectorType
47+
{
48+
Blob
49+
}

dotnet/src/webdriver/BiDi/Network/BytesValue.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@ public abstract record BytesValue
2929
{
3030
public static implicit operator BytesValue(string value) => new StringBytesValue(value);
3131
public static implicit operator BytesValue(byte[] value) => new Base64BytesValue(value);
32+
33+
public static explicit operator string(BytesValue value)
34+
{
35+
if (value is StringBytesValue stringBytesValue)
36+
{
37+
return stringBytesValue.Value;
38+
}
39+
40+
throw new InvalidCastException($"Cannot cast '{value.GetType()}' to '{typeof(string)}'.");
41+
}
3242
}
3343

3444
public sealed record StringBytesValue(string Value) : BytesValue;
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// <copyright file="Collector.cs" company="Selenium 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
7+
// "License"); you may not use this file except in compliance
8+
// with the License. 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,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
// </copyright>
19+
20+
using System;
21+
using System.Threading.Tasks;
22+
23+
namespace OpenQA.Selenium.BiDi.Network;
24+
25+
public sealed class Collector : IAsyncDisposable
26+
{
27+
private readonly BiDi _bidi;
28+
29+
internal Collector(BiDi bidi, string id)
30+
{
31+
_bidi = bidi;
32+
Id = id;
33+
}
34+
35+
internal string Id { get; }
36+
37+
public async Task RemoveAsync()
38+
{
39+
await _bidi.Network.RemoveDataCollectorAsync(this).ConfigureAwait(false);
40+
}
41+
42+
public async ValueTask DisposeAsync()
43+
{
44+
await RemoveAsync();
45+
}
46+
47+
public override bool Equals(object? obj)
48+
{
49+
if (obj is Collector collectortObj) return collectortObj.Id == Id;
50+
51+
return false;
52+
}
53+
54+
public override int GetHashCode()
55+
{
56+
return Id.GetHashCode();
57+
}
58+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// <copyright file="GetDataCommand.cs" company="Selenium 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
7+
// "License"); you may not use this file except in compliance
8+
// with the License. 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,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
// </copyright>
19+
20+
using OpenQA.Selenium.BiDi.Communication;
21+
22+
namespace OpenQA.Selenium.BiDi.Network;
23+
24+
internal sealed class GetDataCommand(GetDataParameters @params)
25+
: Command<GetDataParameters, GetDataResult>(@params, "network.getData");
26+
27+
internal sealed record GetDataParameters(DataType DataType, Request Request, Collector? Collector, bool? Disown) : Parameters;
28+
29+
public sealed class GetDataOptions : CommandOptions
30+
{
31+
public Collector? Collector { get; set; }
32+
33+
public bool? Disown { get; set; }
34+
}
35+
36+
public sealed record GetDataResult(BytesValue Bytes) : EmptyResult;

dotnet/src/webdriver/BiDi/Network/NetworkModule.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@ namespace OpenQA.Selenium.BiDi.Network;
2626

2727
public sealed partial class NetworkModule(Broker broker) : Module(broker)
2828
{
29+
public async Task<Collector> AddDataCollectorAsync(IEnumerable<DataType> DataTypes, int MaxEncodedDataSize, AddDataCollectorOptions? options = null)
30+
{
31+
var @params = new AddDataCollectorParameters(DataTypes, MaxEncodedDataSize, options?.CollectorType, options?.Contexts, options?.UserContexts);
32+
33+
var result = await Broker.ExecuteCommandAsync<AddDataCollectorCommand, AddDataCollectorResult>(new AddDataCollectorCommand(@params), options).ConfigureAwait(false);
34+
35+
return result.Collector;
36+
}
37+
2938
public async Task<Intercept> AddInterceptAsync(IEnumerable<InterceptPhase> phases, AddInterceptOptions? options = null)
3039
{
3140
var @params = new AddInterceptParameters(phases, options?.Contexts, options?.UrlPatterns);
@@ -35,6 +44,13 @@ public async Task<Intercept> AddInterceptAsync(IEnumerable<InterceptPhase> phase
3544
return result.Intercept;
3645
}
3746

47+
public async Task<EmptyResult> RemoveDataCollectorAsync(Collector collector, RemoveDataCollectorOptions? options = null)
48+
{
49+
var @params = new RemoveDataCollectorParameters(collector);
50+
51+
return await Broker.ExecuteCommandAsync<RemoveDataCollectorCommand, EmptyResult>(new RemoveDataCollectorCommand(@params), options).ConfigureAwait(false);
52+
}
53+
3854
public async Task<EmptyResult> RemoveInterceptAsync(Intercept intercept, RemoveInterceptOptions? options = null)
3955
{
4056
var @params = new RemoveInterceptParameters(intercept);
@@ -70,6 +86,15 @@ public async Task<EmptyResult> FailRequestAsync(Request request, FailRequestOpti
7086
return await Broker.ExecuteCommandAsync<FailRequestCommand, EmptyResult>(new FailRequestCommand(@params), options).ConfigureAwait(false);
7187
}
7288

89+
public async Task<BytesValue> GetDataAsync(DataType dataType, Request request, GetDataOptions? options = null)
90+
{
91+
var @params = new GetDataParameters(dataType, request, options?.Collector, options?.Disown);
92+
93+
var result = await Broker.ExecuteCommandAsync<GetDataCommand, GetDataResult>(new GetDataCommand(@params), options).ConfigureAwait(false);
94+
95+
return result.Bytes;
96+
}
97+
7398
public async Task<EmptyResult> ProvideResponseAsync(Request request, ProvideResponseOptions? options = null)
7499
{
75100
var @params = new ProvideResponseParameters(request, options?.Body, options?.Cookies, options?.Headers, options?.ReasonPhrase, options?.StatusCode);
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// <copyright file="RemoveDataCollectorCommand.cs" company="Selenium 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
7+
// "License"); you may not use this file except in compliance
8+
// with the License. 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,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
// </copyright>
19+
20+
using OpenQA.Selenium.BiDi.Communication;
21+
22+
namespace OpenQA.Selenium.BiDi.Network;
23+
24+
internal sealed class RemoveDataCollectorCommand(RemoveDataCollectorParameters @params)
25+
: Command<RemoveDataCollectorParameters, EmptyResult>(@params, "network.removeDataCollector");
26+
27+
internal sealed record RemoveDataCollectorParameters(Collector Collector) : Parameters;
28+
29+
public class RemoveDataCollectorOptions : CommandOptions;

dotnet/test/common/BiDi/Network/NetworkTest.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,23 @@
1919

2020
using NUnit.Framework;
2121
using OpenQA.Selenium.BiDi.BrowsingContext;
22+
using System;
2223
using System.Threading.Tasks;
2324

2425
namespace OpenQA.Selenium.BiDi.Network;
2526

2627
class NetworkTest : BiDiTestFixture
2728
{
29+
[Test]
30+
public async Task CanAddDataCollector()
31+
{
32+
// Firefox doesn't like int.MaxValue as max encoded data size
33+
// invalid argument: Expected "maxEncodedDataSize" to be less than the max total data size available (200000000), got 2147483647
34+
await using var collector = await bidi.Network.AddDataCollectorAsync([DataType.Response], 200000000);
35+
36+
Assert.That(collector, Is.Not.Null);
37+
}
38+
2839
[Test]
2940
public async Task CanAddIntercept()
3041
{
@@ -210,6 +221,30 @@ public async Task CanFailRequest()
210221
Assert.That(action, Throws.TypeOf<BiDiException>().With.Message.Contain("net::ERR_FAILED").Or.Message.Contain("NS_ERROR_ABORT"));
211222
}
212223

224+
[Test]
225+
public async Task CanGetData()
226+
{
227+
// Firefox doesn't like int.MaxValue as max encoded data size
228+
// invalid argument: Expected "maxEncodedDataSize" to be less than the max total data size available (200000000), got 2147483647
229+
await using var collector = await bidi.Network.AddDataCollectorAsync([DataType.Response], 200000000);
230+
231+
TaskCompletionSource<string> responseBodyCompletionSource = new();
232+
233+
await using var _ = await bidi.Network.OnResponseCompletedAsync(async e =>
234+
{
235+
if (e.Response.Url.Contains("simpleTest.html"))
236+
{
237+
responseBodyCompletionSource.SetResult((string)await bidi.Network.GetDataAsync(DataType.Response, e.Request.Request));
238+
}
239+
});
240+
241+
await context.NavigateAsync(UrlBuilder.WhereIs("simpleTest.html"), new() { Wait = ReadinessState.Complete });
242+
243+
var responseBody = await responseBodyCompletionSource.Task.WaitAsync(TimeSpan.FromSeconds(5));
244+
245+
Assert.That(responseBody, Contains.Substring("Hello WebDriver"));
246+
}
247+
213248
[Test]
214249
public void CanSetCacheBehavior()
215250
{

0 commit comments

Comments
 (0)