Skip to content

Commit b302d1e

Browse files
committed
provide RemoteCertificateValidationCallback to let user ignore ssl err when using WebSocket
1 parent 81eca9c commit b302d1e

File tree

11 files changed

+226
-11
lines changed

11 files changed

+226
-11
lines changed

azure-devops/templates/socket.io-tpl.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ steps:
1010
pm2 start v4/v4-ws-token-mp.js
1111
pm2 start v4/v4-http-mp.js
1212
pm2 start v4/v4-http-token-mp.js
13+
pm2 start v4/v4-ws-ssl-err.js
1314
pm2 start v2/v2-ws.js
1415
pm2 start v2/v2-ws-token.js
1516
pm2 start v2/v2-http.js

src/SocketIO.Client/SocketIO.Client.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>netstandard2;net462</TargetFrameworks>
4+
<TargetFrameworks>netstandard2;net462;net6.0</TargetFrameworks>
55
<Authors>doghappy</Authors>
66
<Description>socket.io-client implemention for .NET</Description>
77
<PackageProjectUrl>https://github.com/doghappy/socket.io-client-csharp</PackageProjectUrl>

src/SocketIO.Client/SocketIOClient.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,12 @@ private void Initialize()
168168
Serializer = new SystemTextJsonSerializer();
169169

170170
HttpClient = new DefaultHttpClient();
171-
ClientWebSocketProvider = () => new DefaultClientWebSocket();
171+
ClientWebSocketProvider = () =>
172+
{
173+
var ws = new DefaultClientWebSocket();
174+
ws.RemoteCertificateValidationCallback = Options.RemoteCertificateValidationCallback;
175+
return ws;
176+
};
172177
_expectedExceptions = new List<Type>
173178
{
174179
typeof(TimeoutException),

src/SocketIO.Client/SocketIOOptions.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Net;
4+
using System.Net.Security;
45
using SocketIO.Client.Transport;
56
using SocketIO.Core;
67

@@ -38,6 +39,7 @@ public SocketIOOptions()
3839
public int ReconnectionAttempts { get; set; }
3940

4041
double _randomizationFactor;
42+
4143
public double RandomizationFactor
4244
{
4345
get => _randomizationFactor;
@@ -49,7 +51,8 @@ public double RandomizationFactor
4951
}
5052
else
5153
{
52-
throw new ArgumentException($"{nameof(RandomizationFactor)} should be greater than or equal to 0.0, and less than 1.0.");
54+
throw new ArgumentException(
55+
$"{nameof(RandomizationFactor)} should be greater than or equal to 0.0, and less than 1.0.");
5356
}
5457
}
5558
}
@@ -64,5 +67,6 @@ public double RandomizationFactor
6467

6568
public object Auth { get; set; }
6669
public IWebProxy Proxy { get; set; }
70+
public RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; set; }
6771
}
68-
}
72+
}

src/SocketIO.Client/Transport/WebSockets/DefaultClientWebSocket.cs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Net;
3+
using System.Net.Security;
34
using System.Net.WebSockets;
45
using System.Threading;
56
using System.Threading.Tasks;
@@ -33,9 +34,11 @@ private void AllowHeaders()
3334
.GetType()
3435
.GetProperty("RequestHeaders", BindingFlags.NonPublic | BindingFlags.Instance);
3536
var headers = property.GetValue(_ws.Options);
36-
var hinfoField = headers.GetType().GetField("HInfo", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
37+
var hinfoField =
38+
headers.GetType().GetField("HInfo", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
3739
var hinfo = hinfoField.GetValue(null);
38-
var hhtField = hinfo.GetType().GetField("HeaderHashTable", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
40+
var hhtField =
41+
hinfo.GetType().GetField("HeaderHashTable", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
3942
var hashTable = hhtField.GetValue(null) as System.Collections.Hashtable;
4043

4144
foreach (string key in hashTable.Keys)
@@ -67,30 +70,43 @@ private void AllowHeaders()
6770

6871
public WebSocketState State => (WebSocketState)_ws.State;
6972

73+
public RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; set; }
74+
7075
public async Task ConnectAsync(Uri uri, CancellationToken cancellationToken)
7176
{
77+
#if NET6_0_OR_GREATER
78+
_ws.Options.RemoteCertificateValidationCallback = RemoteCertificateValidationCallback;
79+
#endif
80+
#if NET461_OR_GREATER
81+
ServicePointManager.ServerCertificateValidationCallback = RemoteCertificateValidationCallback;
82+
#endif
7283
await _ws.ConnectAsync(uri, cancellationToken).ConfigureAwait(false);
7384
}
7485

7586
public async Task DisconnectAsync(CancellationToken cancellationToken)
7687
{
77-
await _ws.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, cancellationToken).ConfigureAwait(false);
88+
await _ws.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, cancellationToken)
89+
.ConfigureAwait(false);
7890
}
7991

80-
public async Task SendAsync(byte[] bytes, TransportMessageType type, bool endOfMessage, CancellationToken cancellationToken)
92+
public async Task SendAsync(byte[] bytes, TransportMessageType type, bool endOfMessage,
93+
CancellationToken cancellationToken)
8194
{
8295
var msgType = WebSocketMessageType.Text;
8396
if (type == TransportMessageType.Binary)
8497
{
8598
msgType = WebSocketMessageType.Binary;
8699
}
87-
await _ws.SendAsync(new ArraySegment<byte>(bytes), msgType, endOfMessage, cancellationToken).ConfigureAwait(false);
100+
101+
await _ws.SendAsync(new ArraySegment<byte>(bytes), msgType, endOfMessage, cancellationToken)
102+
.ConfigureAwait(false);
88103
}
89104

90105
public async Task<WebSocketReceiveResult> ReceiveAsync(int bufferSize, CancellationToken cancellationToken)
91106
{
92107
var buffer = new byte[bufferSize];
93-
var result = await _ws.ReceiveAsync(new ArraySegment<byte>(buffer), cancellationToken).ConfigureAwait(false);
108+
var result = await _ws.ReceiveAsync(new ArraySegment<byte>(buffer), cancellationToken)
109+
.ConfigureAwait(false);
94110
return new WebSocketReceiveResult
95111
{
96112
Count = result.Count,

tests/SocketIO.Client.IntegrationTests.Net472/V4WebSocketTests.cs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Microsoft.VisualStudio.TestTools.UnitTesting;
1+
using System;
2+
using Microsoft.VisualStudio.TestTools.UnitTesting;
23
using System.Collections.Generic;
34
using System.Threading.Tasks;
45
using FluentAssertions;
@@ -37,5 +38,24 @@ await io.EmitAsync("get_header",
3738
actual.Should().Be(value);
3839
};
3940
}
41+
42+
[TestMethod]
43+
public async Task Should_ignore_SSL_error()
44+
{
45+
var io = new SocketIOClient("https://localhost:11404", new SocketIOOptions
46+
{
47+
EIO = EngineIO.V4,
48+
AutoUpgrade = false,
49+
Reconnection = false,
50+
Transport = TransportProtocol.WebSocket,
51+
ConnectionTimeout = TimeSpan.FromSeconds(2),
52+
RemoteCertificateValidationCallback = (sender, cert, chain, errs) => true
53+
});
54+
var connected = false;
55+
io.OnConnected += (s, e) => connected = true;
56+
await io.ConnectAsync();
57+
58+
connected.Should().BeTrue();
59+
}
4060
}
4161
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using FluentAssertions;
4+
using Microsoft.VisualStudio.TestTools.UnitTesting;
5+
using SocketIO.Client.Transport;
6+
using SocketIO.Core;
7+
8+
namespace SocketIO.Client.IntegrationTests;
9+
10+
[TestClass]
11+
public class V4WebSocketSslTests
12+
{
13+
[TestMethod]
14+
public async Task Should_ignore_SSL_error()
15+
{
16+
var io = new SocketIOClient("https://localhost:11404", new SocketIOOptions
17+
{
18+
EIO = EngineIO.V4,
19+
AutoUpgrade = false,
20+
Reconnection = false,
21+
Transport = TransportProtocol.WebSocket,
22+
ConnectionTimeout = TimeSpan.FromSeconds(2),
23+
RemoteCertificateValidationCallback = (_, _, _, _) => true
24+
});
25+
var connected = false;
26+
io.OnConnected += (_, _) => connected = true;
27+
await io.ConnectAsync();
28+
29+
connected.Should().BeTrue();
30+
}
31+
}

tests/socket.io/v4/cert/cert.crt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDETCCAfkCFHvkxrj6IG2A/aLKJSCEPRcNXz0CMA0GCSqGSIb3DQEBCwUAMEUx
3+
CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl
4+
cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjQwMzMxMTUxODE0WhcNMjUwMzMxMTUx
5+
ODE0WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UE
6+
CgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOC
7+
AQ8AMIIBCgKCAQEAzLYnBjWrkZE53X8F4VMIzO/uQyneipAWzPJpGErLLsgqM7Tq
8+
moUCjW1d7SEQNaSonYyAEIitHFujNJbZK2pN3gqaFI0nncXRYdX2wjqXY6zZR/ty
9+
p4FFyqcQPv5okpE3e37mAHCkzWnLgm5/BXNaqZCs5sGDiMEwHrn8mqLKGnHW+fL0
10+
al1LHh5SMGUadXa2UDrK6JJWO0oY03RZq8OnO1iktOsh49QkaDuq729m7c8ic3lj
11+
FwbNJTnlhWC6oTq+L017j00GoxA1FnFeuFpLVySC+/EmKX3d+jEMLJi3Jmz/tRWk
12+
5pFRwfjJ5bUb/3XxJ3gdufQWxWFNw/4koE63HQIDAQABMA0GCSqGSIb3DQEBCwUA
13+
A4IBAQBDiC6OBwvwS/t+9+rC5YQ3OxjsTeTiarBOvRfheg6pibVrJBa/w/e+arQq
14+
+RZrBIM1aqWKzQwloPYugflet6ZJQpRebwb7TGNKVRSUcws5PMhbXzDrO2X6rqMS
15+
/g1tKZtLVL79LklmIvxFvcI0N27BXvShXLff8XxMJz+dZk9212NnZjuPtlvCmk4G
16+
1n/bkl5YnDWnHLBEImfOtNEH5fOjO5LkOrz8O80AkiFwbbzDJPS2o3Yhs89zULo3
17+
Sa0ji/NM0OFN9fZZ0irNxGcBnW4KwpRqou+coWAzqpDFjLx49u9ODM5XCI23QpIJ
18+
ZSzcPSvmAIrHxpsH3oD+GpnGdUk8
19+
-----END CERTIFICATE-----
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDMticGNauRkTnd
3+
fwXhUwjM7+5DKd6KkBbM8mkYSssuyCoztOqahQKNbV3tIRA1pKidjIAQiK0cW6M0
4+
ltkrak3eCpoUjSedxdFh1fbCOpdjrNlH+3KngUXKpxA+/miSkTd7fuYAcKTNacuC
5+
bn8Fc1qpkKzmwYOIwTAeufyaosoacdb58vRqXUseHlIwZRp1drZQOsroklY7ShjT
6+
dFmrw6c7WKS06yHj1CRoO6rvb2btzyJzeWMXBs0lOeWFYLqhOr4vTXuPTQajEDUW
7+
cV64WktXJIL78SYpfd36MQwsmLcmbP+1FaTmkVHB+MnltRv/dfEneB259BbFYU3D
8+
/iSgTrcdAgMBAAECggEATraVj8owQ8M7JEN0z9ydLCvvigy94LqhwcFODsSnlr/p
9+
lkMw2CRfxGCytny5nl7HZPkCvxjGs3o66Xw9WffApCmgcFmMS5qmNX/Pp4Re9Lkg
10+
PRDe17CZ1N/jG824CO9kjYxQRQgLHl7ZHTh+h+qAiGW0TfBHstxRs+bgzdbdbkf6
11+
WU5hbFQQpyy6wiwEEx9XDhm3cm/mF660ZD2f6ZZCJiicV4TNb2+EwZAb8miWr86c
12+
IfqjqVVCO+MeOz/Y+ri2a67vpkZ+0ysQv6VytwIEMZW1Qq3vLQKg7cIM7dxR2YPi
13+
nDeisIvzC/WDSd1p42M2Yv3Rts0hrr03BLZuAxZ1owKBgQDwFJVOXaP5IjE9PXbQ
14+
8ePlacD9zOR0ObOyk8eBSFSf3cpVdd3QKx7uioXFNPmkJauP4kWU5lMqtzGj4hdw
15+
uVylb/GZ1Lp8uXkeQzVktbFI9O4EDsvIeC83aRxu9ZXtRMBHS8SHBufXlc4IcMJZ
16+
lsywKqGON3KakK0ACGOIs7HzPwKBgQDaSSzMDVlLCwibrsrE31mjxmGGpOr1Qa1b
17+
m5kddPN35uBp/lKrbXNx0sAbZIpcuenXBbJ4fBTvoZHsiGEqOXJpGcJ2mHuO5hoJ
18+
5AJ/r+iEDRzM5GhE4+ATjYpULugYB+KXhqDCMpfR77xqwbUFYOdeLBSEj/PlLe82
19+
cbfzbEeqowKBgQCkIh5VXjWNTLAHIy9I+CaLIDreCSciwpQ1AU1C+LVKOnJq7NMB
20+
z4ktIi0EPwxxCYP6MYLKopC3QllApoDKAx/wxtCRD9uTC6ZfZyloucMDktfqlEcD
21+
vg7hvg2/Wkzu0rL1yzoH6lO0kukx4g0s/Kjhw7OBrCzAuSpdPF74BYoiNwKBgQCt
22+
W5AIPlG8F3curRK8Z+V4/ARYOoGfZhmXt2tSyZ7SirmPdDuTick1jHqlRqPcIIpm
23+
ClBC/8hgx6BsiaMhNZ53ec3HAjKeun/TexHA9qNivEczMfLdQ1yiKrbBRL9u1lRO
24+
oszpbeTFBfBNmKl7LAqT784buXepe2GPi6Db4hLIoQKBgFw7Bnu79raA6jjr+DPP
25+
bZh5kv9EaUhH2qdMXfrckRBb1dCbRYZAClWKl0ZXKTrVn7swFn5ngUbdbHqTxe8U
26+
NteUxtJg+oRYPu6BAnG4gXja/eGJaUeNsA2nUNdJu9pQX4Gl6yHRSXewWdvAwqfx
27+
O3koFVqHLXN2BUhY5t2gsQ7d
28+
-----END PRIVATE KEY-----

tests/socket.io/v4/common.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
'use strict';
2+
3+
function registerEvents(io) {
4+
io.on('connection', socket => {
5+
socket.on('1:emit', data => {
6+
socket.emit('1:emit', data);
7+
});
8+
socket.on('2:emit', (d1, d2) => {
9+
socket.emit('2:emit', d1, d2);
10+
});
11+
socket.on('1:ack', (data, cb) => {
12+
cb(data);
13+
});
14+
socket.on('get_auth', cb => {
15+
cb(socket.handshake.auth);
16+
});
17+
socket.on('get_header', (key, cb) => {
18+
cb(socket.handshake.headers[key]);
19+
});
20+
socket.on('disconnect', close => {
21+
socket.disconnect(close);
22+
});
23+
socket.on('client will be sending data to server', () => {
24+
socket.emit('client sending data to server', (arg) => {
25+
socket.emit("server received data", arg);
26+
});
27+
});
28+
});
29+
30+
const nsp = io.of("/nsp");
31+
nsp.on("connection", socket => {
32+
socket.on('1:emit', data => {
33+
socket.emit('1:emit', data);
34+
});
35+
socket.on('2:emit', (d1, d2) => {
36+
socket.emit('2:emit', d1, d2);
37+
});
38+
socket.on('1:ack', (data, cb) => {
39+
cb(data);
40+
});
41+
socket.on('get_auth', cb => {
42+
cb(socket.handshake.auth);
43+
});
44+
socket.on('get_header', (key, cb) => {
45+
cb(socket.handshake.headers[key]);
46+
});
47+
socket.on('disconnect', close => {
48+
socket.disconnect(close);
49+
});
50+
socket.on('client will be sending data to server', () => {
51+
socket.emit('client sending data to server', (arg) => {
52+
socket.emit("server received data", arg);
53+
});
54+
});
55+
});
56+
}
57+
58+
//TODO: need to refactor and reuse this function
59+
60+
module.exports = registerEvents

0 commit comments

Comments
 (0)