Skip to content

Commit 3103680

Browse files
committed
Merged
2 parents 5152a2c + 87926ad commit 3103680

File tree

8 files changed

+207
-3
lines changed

8 files changed

+207
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
- Updated to latest version
44
- Dispose WebSocket on unload
5+
- Added `about:` handler
56

67
# 0.3.1
78

src/AngleSharp.Io.Tests/AngleSharp.Io.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
<Compile Include="DisposableStream.cs" />
7676
<Compile Include="Helper.cs" />
7777
<Compile Include="Network\CookieHandlingTests.cs" />
78+
<Compile Include="Network\AboutTests.cs" />
7879
<Compile Include="Network\FileRequesterTests.cs" />
7980
<Compile Include="Network\FtpRequesterTests.cs" />
8081
<Compile Include="Network\HttpClientRequesterTests.cs" />
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
namespace AngleSharp.Io.Tests.Network
2+
{
3+
using AngleSharp.Network;
4+
using AngleSharp.Network.Default;
5+
using Io.Network;
6+
using NUnit.Framework;
7+
using System.Collections.Generic;
8+
using System.Threading;
9+
using System.Threading.Tasks;
10+
11+
[TestFixture]
12+
public class AboutTests
13+
{
14+
[Test]
15+
public async Task InvokeAboutSettingsLowercaseShouldWorkFine()
16+
{
17+
var about = new AboutRequester();
18+
var requests = new List<IRequest>();
19+
var req = new Request
20+
{
21+
Address = Url.Create("about://settings"),
22+
Method = HttpMethod.Get
23+
};
24+
25+
about.SetRoute("settings", (request, cancel) =>
26+
{
27+
requests.Add(request);
28+
return Task.FromResult(default(IResponse));
29+
});
30+
31+
var response = await about.RequestAsync(req, CancellationToken.None);
32+
Assert.IsNull(response);
33+
Assert.AreEqual(1, requests.Count);
34+
}
35+
36+
[Test]
37+
public async Task InvokeAboutSettingsUppercaseShouldWorkFine()
38+
{
39+
var about = new AboutRequester();
40+
var requests = new List<IRequest>();
41+
var req = new Request
42+
{
43+
Address = Url.Create("about://Settings"),
44+
Method = HttpMethod.Get
45+
};
46+
47+
about.SetRoute("settings", (request, cancel) =>
48+
{
49+
requests.Add(request);
50+
return Task.FromResult(default(IResponse));
51+
});
52+
53+
var response = await about.RequestAsync(req, CancellationToken.None);
54+
Assert.IsNull(response);
55+
Assert.AreEqual(1, requests.Count);
56+
}
57+
58+
[Test]
59+
public async Task InvokeAboutUnsetUrlShouldNotFire()
60+
{
61+
var about = new AboutRequester();
62+
var requests = new List<IRequest>();
63+
var req = new Request
64+
{
65+
Address = Url.Create("about://bookmarks"),
66+
Method = HttpMethod.Get
67+
};
68+
69+
about.SetRoute("settings", (request, cancel) =>
70+
{
71+
requests.Add(request);
72+
return Task.FromResult(default(IResponse));
73+
});
74+
75+
var response = await about.RequestAsync(req, CancellationToken.None);
76+
Assert.IsNull(response);
77+
Assert.AreEqual(0, requests.Count);
78+
}
79+
80+
[Test]
81+
public async Task InvokeAboutUrlWithQueryShouldFire()
82+
{
83+
var about = new AboutRequester();
84+
var requests = new List<IRequest>();
85+
var req = new Request
86+
{
87+
Address = Url.Create("about://cache?device=memory"),
88+
Method = HttpMethod.Get
89+
};
90+
91+
about.SetRoute("cache", (request, cancel) =>
92+
{
93+
requests.Add(request);
94+
return Task.FromResult(default(IResponse));
95+
});
96+
97+
var response = await about.RequestAsync(req, CancellationToken.None);
98+
Assert.IsNull(response);
99+
Assert.AreEqual(1, requests.Count);
100+
Assert.AreEqual(req.Address.Data, requests[0].Address.Data);
101+
}
102+
}
103+
}

src/AngleSharp.Io/AngleSharp.Io.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
<Compile Include="Extensions\GeneralExtensions.cs" />
5353
<Compile Include="Dom\WebSocket.cs" />
5454
<Compile Include="Interfaces\IStorage.cs" />
55+
<Compile Include="Network\AboutRequester.cs" />
5556
<Compile Include="Network\FileRequester.cs" />
5657
<Compile Include="Network\FtpRequester.cs" />
5758
<Compile Include="Network\HttpClientRequester.cs" />

src/AngleSharp.Io/IoConfigurationExtensions.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,14 @@ public static IConfiguration WithRequesters(this IConfiguration configuration, A
3434
public static IConfiguration WithRequesters(this IConfiguration configuration, HttpMessageHandler httpMessageHandler, Action<ConfigurationExtensions.LoaderSetup> setup = null)
3535
{
3636
var httpClient = new HttpClient(httpMessageHandler);
37-
var requesters = new IRequester[] { new HttpClientRequester(httpClient), new DataRequester(), new FtpRequester(), new FileRequester() };
37+
var requesters = new IRequester[]
38+
{
39+
new HttpClientRequester(httpClient),
40+
new DataRequester(),
41+
new FtpRequester(),
42+
new FileRequester(),
43+
new AboutRequester()
44+
};
3845
return configuration.WithDefaultLoader(setup, requesters);
3946
}
4047
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
namespace AngleSharp.Io.Network
2+
{
3+
using AngleSharp.Network;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
9+
/// <summary>
10+
/// Requester to perform about:// requests.
11+
/// </summary>
12+
public class AboutRequester : IRequester
13+
{
14+
private readonly Dictionary<String, Func<IRequest, CancellationToken, Task<IResponse>>> _routes;
15+
16+
/// <summary>
17+
/// Creates a new about requester.
18+
/// </summary>
19+
public AboutRequester()
20+
{
21+
_routes = new Dictionary<String, Func<IRequest, CancellationToken, Task<IResponse>>>(StringComparer.InvariantCultureIgnoreCase);
22+
}
23+
24+
/// <summary>
25+
/// Sets the route for the given address.
26+
/// </summary>
27+
/// <param name="address">The address of the route.</param>
28+
/// <param name="route">The route to use.</param>
29+
public void SetRoute(String address, Func<IRequest, CancellationToken, Task<IResponse>> route)
30+
{
31+
_routes[address] = route;
32+
}
33+
34+
/// <summary>
35+
/// Gets the route for the given address, if any.
36+
/// </summary>
37+
/// <param name="address">The address of the route to obtain.</param>
38+
/// <returns>The route, if any.</returns>
39+
public Func<IRequest, CancellationToken, Task<IResponse>> GetRoute(String address)
40+
{
41+
var route = default(Func<IRequest, CancellationToken, Task<IResponse>>);
42+
_routes.TryGetValue(address, out route);
43+
return route;
44+
}
45+
46+
/// <summary>
47+
/// Performs an asynchronous request that can be cancelled.
48+
/// </summary>
49+
/// <param name="request">The options to consider.</param>
50+
/// <param name="cancel">The token for cancelling the task.</param>
51+
/// <returns>The task that will eventually give the response data.</returns>
52+
public Task<IResponse> RequestAsync(IRequest request, CancellationToken cancel)
53+
{
54+
var address = GetAddress(request.Address.Data);
55+
var route = GetRoute(address);
56+
57+
if (route != null)
58+
{
59+
return route.Invoke(request, cancel);
60+
}
61+
62+
return Task.FromResult(default(IResponse));
63+
}
64+
65+
/// <summary>
66+
/// Checks if the given protocol is supported.
67+
/// </summary>
68+
/// <param name="protocol">The protocol to check for, e.g. file.</param>
69+
/// <returns>True if the protocol is supported, otherwise false.</returns>
70+
public Boolean SupportsProtocol(String protocol)
71+
{
72+
return protocol.Equals("about", StringComparison.OrdinalIgnoreCase);
73+
}
74+
75+
private static String GetAddress(String data)
76+
{
77+
var skip = 0;
78+
79+
while (data.Length > skip && data[skip] == '/' && skip++ < 2) ;
80+
81+
var query = data.IndexOf('?');
82+
83+
if (query >= 0)
84+
{
85+
data = data.Remove(query);
86+
}
87+
88+
return data.Remove(0, skip);
89+
}
90+
}
91+
}

src/AngleSharp.Io/Network/FileRequester.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public async Task<IResponse> RequestAsync(IRequest request, CancellationToken ca
4545
/// <returns>True if the protocol is supported, otherwise false.</returns>
4646
public Boolean SupportsProtocol(String protocol)
4747
{
48-
return !String.IsNullOrEmpty(protocol) && protocol.Equals(ProtocolNames.File);
48+
return protocol.Equals(ProtocolNames.File, StringComparison.OrdinalIgnoreCase);
4949
}
5050
}
5151
}

src/AngleSharp.Io/Network/FtpRequester.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public async Task<IResponse> RequestAsync(IRequest request, CancellationToken ca
4848
/// <returns>True if the protocol is supported, otherwise false.</returns>
4949
public Boolean SupportsProtocol(String protocol)
5050
{
51-
return !String.IsNullOrEmpty(protocol) && protocol.Equals(ProtocolNames.Ftp);
51+
return protocol.Equals(ProtocolNames.Ftp, StringComparison.OrdinalIgnoreCase);
5252
}
5353
}
5454
}

0 commit comments

Comments
 (0)