Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fully implement ClientWebSocketOptions for .NET Core #15994

Closed
SidharthNabar opened this issue Dec 28, 2015 · 34 comments
Closed

Fully implement ClientWebSocketOptions for .NET Core #15994

SidharthNabar opened this issue Dec 28, 2015 · 34 comments
Labels
area-System.Net enhancement Product code improvement that does NOT require public API changes/additions os-windows
Milestone

Comments

@SidharthNabar
Copy link

Currently, .NET Core has a placeholder implementation for ClientWebSocketOptions. Filing this issue to track the complete implementation.

@CIPop
Copy link
Member

CIPop commented Dec 13, 2016

@karelz, @davidsh this is tracking ClientWebSocketOptions work using WinHTTP on Windows.

This type enables scenarios such as HTTP client cert, headers, cookies, credentials, proxy and WS sub-protocol specification.

@stephentoub I remember there is similar work required for the ManagedWebSocket implementation (proxy, etc). We may need to create an issue and review it for netstandard2.0.

@stephentoub
Copy link
Member

I remember there is similar work required for the ManagedWebSocket implementation (proxy, etc).

The pieces not currently handled by ManagedWebSocket are listed here:
https://github.com/dotnet/corefx/blob/master/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs#L76-L80
the two main ones being proxy and credentials. Proxy should be pretty straightforward, I just never did it. Credentials will be harder.

@CIPop
Copy link
Member

CIPop commented Dec 13, 2016

I've opened dotnet/corefx#14480 to track the item. I'll submit a PR to change the code to point to the issue. (The TODO is missing the issue number.)

@karelz
Copy link
Member

karelz commented Feb 2, 2017

We should consider it for 2.0 -- we should also find out if it is reasonable for us to reuse the ManagedWebSocket also on Windows.

@karelz
Copy link
Member

karelz commented Apr 3, 2017

We discussed this last week and decided to push it to Future.

We plan to replace WebSockets with ManagedWebSockets (which lack some other implementation that is currently supported).
It is too late to make it happen in 2.0 (we have higher priorities to finish and this is costly ~4w). We have it planned for 2.1 UWP (incl. beefing up ManagedWebSockets implementation) - at that moment it should be usable also for .NET Core.

@nemakam
Copy link

nemakam commented Sep 1, 2017

Just raising another request for this feature.

@SidharthNabar, If I get it right, there is currently no way to get websocket client running behind a proxy server in net core?

@davidsh
Copy link
Contributor

davidsh commented Sep 1, 2017

@nemakam

@SidharthNabar, If I get it right, there is currently no way to get websocket client running behind a proxy server in net core?

@SidharthNabar is no longer working on the .NET Core project.

In terms of client websocket ability to navigate thru a proxy, I believe that it can thru the default system proxy. However, it will not be able to go thru a custom IWebProxy since it is not honoring the .Proxy property.

@nemakam
Copy link

nemakam commented Sep 1, 2017

@davidsh I tried running a basic console app with this functionality and changed my system proxy to point to fiddler (127.0.0.1:8888) to capture all traffic flowing through the proxy. When I run the same setup on net46 target framework, I can see the traffic moving through the proxy (which are captured in fiddler). But when I change the target framework to netcoreapp2.0, the data is no longer captured in fiddler, which makes me believe the data isn't passing through the proxy. The entire connection + send + receive still works, but it just doesn't go through the proxy.

My code roughly looks like this

ClientWebSocket cws = new ClientWebSocket();
cws.Options.AddSubProtocol("amqp");
Task task = cws.ConnectAsync(this.settings.Uri, CancellationToken.None).WithTimeout(timeout, () => "timeout");

While debugging, I can see that in netcoreapp2.0, cws._innerWebSocket._webSocket is an object of type WinHttpWebSocket.
I'm running on Win10, VS 2017 15.3.3, and 2.0.0 dotnet core SDK version.

@nemakam
Copy link

nemakam commented Sep 2, 2017

For a repro, try this:

public static async Task WsProxyTest()
{
	try
	{
		var uri = new Uri("wss://echo.websocket.org");
		ClientWebSocket cws = new ClientWebSocket();
		cws.Options.AddSubProtocol("amqp");
		await cws.ConnectAsync(uri, CancellationToken.None);
		await cws.SendAsync(new ArraySegment<byte>(new[] { (byte)65, (byte)66, (byte)67 }), WebSocketMessageType.Binary, true, CancellationToken.None);
		var buffer = new ArraySegment<byte>(new byte[1000]);
		await cws.ReceiveAsync(buffer, CancellationToken.None);
		await cws.CloseAsync(WebSocketCloseStatus.NormalClosure, "close", CancellationToken.None);
	}
	catch (Exception e)
	{
		Console.WriteLine(e);
	}
}

These are the two target frameworks:
<TargetFrameworks>netcoreapp2.0</TargetFrameworks>
<TargetFrameworks>net46</TargetFrameworks>

When I run this code under net46, I can see the data being captured in Fiddler (since fiddler listens on 127.0.0.1:8888) and system proxy is changed to the same endpoint. But the same code under netcoreapp2.0 doesn't go through the proxy endpoint.

@stephentoub
Copy link
Member

The managed ClientWebSocket implementation as of dotnet/corefx#24288 now provides support for all of ClientWebSocketOptions, in that it passes all of the relevant options along to the ManagedHandler implementation.

@CIPop
Copy link
Member

CIPop commented Oct 4, 2017

@stephentoub is the app-compat table still correct given this new commit?

@stephentoub
Copy link
Member

is the app-compat table still correct given this new commit?

The table only goes up through 2.0, so yes. It'll need to be updated once we have a 2.1.

@qmfrederik
Copy link
Contributor

Is this supposed to work in the latest nightly builds?

I tried using the ClientWebSocket with the latest nightly build (the debugger tells me I'm on veresion 4.06.25929.2 of System.Net.WebSockets.Client.dll, with timestamp 11/29/2017 2:23 PM which was loaded from C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.1.0-preview1-25929-02\System.Net.WebSockets.Client.dll). This is on Windows 10.

Specifying client certificates and proxy servers via Options doesn't seem to work on my side.

My code is something along these lines (trying to connect to Kubernetes):

ClientWebSocket webSocket = new ClientWebSocket();
webSocket.Options.Proxy = new WebProxy("127.0.0.1:8888");
await webSocket.ConnectAsync(wssUrl, CancellationToken.None).ConfigureAwait(false);

I'm fairly sure the client certificates don't work as I get an exception telling me I need to specify one; the WebProxy also doesn't seem to work as my proxy servers (Fiddler/Burp) don't see any incoming request. The URL represents an external server, so loopback exceptions don't apply, either.

@davidsh
Copy link
Contributor

davidsh commented Dec 1, 2017

Is this supposed to work in the latest nightly builds?

ClientWebSocketOptions is not fully implemented yet. So, it is expected that the code above doesn't work.

@stephentoub
Copy link
Member

(If you want to give it a try on Linux, though, I'd expect/hope with the nightly it should work there.)

@qmfrederik
Copy link
Contributor

Sure, I can give Linux a try. Is there any way to force the client to use the managed implementation on Windows?

@stephentoub
Copy link
Member

Is there any way to force the client to use the managed implementation on Windows?

If you overwrite the System.Net.WebSockets.Client.dll you have with the Unix one from the package, that should "just work", though you'll want to make a backup of the original just in case :)

@qmfrederik
Copy link
Contributor

@stephentoub I gave it a try on Linux; right now I'm getting SSL certification errors: "The remote certificate is invalid according to the validation procedure".

I have a X509Certificate2 which represents certificate of the CA issuing the SSL certificates for the server, but I haven't been able to figure out yet how to let the ClientWebSocket know to either trust that CA or let me do the validation myself.

PS: Copying the dll from Linux to Windows doesn't "just work" as the file is marked as targetting Linux so the runtime rejects it on Windows. I was able to patch the architecture in the PE header of the assembly but apparently that wasn't enough, so I decided to abandon that experiment for now ;-)

@stephentoub
Copy link
Member

Copying the dll from Linux to Windows doesn't "just work" as the file is marked as targetting Linux so the runtime rejects it on Windows. I was able to patch the architecture in the PE header of the assembly

I'm not aware of that. @weshaggard, do you know what @qmfrederik is referring to here? We should not be doing anything special to say that the assembly is somehow Linux-specific... it's not.

I have a X509Certificate2 which represents certificate of the CA issuing the SSL certificates for the server, but I haven't been able to figure out yet how to let the ClientWebSocket know to either trust that CA or let me do the validation myself.

ClientWebSocket here is just using SslStream, which on Windows should be using standard validation mechanisms. @bartonjs, can you help?

@qmfrederik
Copy link
Contributor

@stephentoub @weshaggard For example, the WinPE header of contains System.Net.WebSockets.Client.dll which I got from dotnet-sdk-latest-linux-x64.tar.gz contains the value 0xFD1D in the architecture field (offset 0x84); for other assemblies that appears to be 0x8664.

@qmfrederik
Copy link
Contributor

@stephentoub @bartonjs Regarding the SSL validation (I'm trying on Linux ATM), it looks like dotnet/corefx#12038 is related.

@weshaggard
Copy link
Member

For IL only assemblies we shouldn't be marking them as OS or architecture specific (I think the default is prefer32bit) so I would expect them to work just fine between linux and windows. In fact we build all our IL assemblies on windows. However if you are taking the binaries out of the runtime zips then those are native ngen'ed binaries and will not work on the other platform as you point out.

@qmfrederik you should be able to download the nuget package for this library and take the unix asset which is IL only and try and run it.

@qmfrederik
Copy link
Contributor

@weshaggard OK - that makes sense. Where should I find the NuGet package? I tried https://dotnet.myget.org/feed/dotnet-core/package/nuget/System.Net.WebSockets.Client but it looks that hasn't been updated since January.

@qmfrederik
Copy link
Contributor

@stephentoub Adding my CA to the list of trusted CA's on Linux got me this gem:

Unhandled Exception: System.Net.WebSockets.WebSocketException: The WebSocket client request requested '' protocol(s), but server is only accepting '' protocol(s).
   at System.Net.WebSockets.WebSocketHandle.<ConnectAsyncCore>d__24.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Net.WebSockets.ClientWebSocket.<ConnectAsyncCore>d__16.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at CloudRunner.Program.<Main>d__0.MoveNext() in /home/fcarlier/git/CloudRunner/CloudRunner/Program.cs:line 111
--- End of stack trace from previous location where exception was thrown ---
   at CloudRunner.Program.<Main>(String[] args)

@davidsh
Copy link
Contributor

davidsh commented Dec 1, 2017

@weshaggard OK - that makes sense. Where should I find the NuGet package?

Go to NuGet.org:
https://www.nuget.org/packages/System.Net.WebSockets.Client/4.3.2

@stephentoub
Copy link
Member

Adding my CA to the list of trusted CA's on Linux got me this gem

Hmm, that error is coming from here:
https://github.com/dotnet/corefx/blob/baf2128a7cdf4a7638be1e86a6fc18651f3945d3/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs#L183
It's possible the validation condition there is wrong.

@qmfrederik
Copy link
Contributor

@davidsh Hmm, looks like that package is built off https://github.com/dotnet/corefx/commits/1503bfaac164addc2821b41193d2c54512d49e37 which doesn't seem to include dotnet/corefx#24288 though

@davidsh
Copy link
Contributor

davidsh commented Dec 1, 2017

The NuGet.org package is from the official release, 1.1 which was the last release that had a separate System.Net.WebSockets.Client NuGet package.

If you want daily feeds from MYGET.ORG, then your previous link would have been correct. However, we no longer build a separate System.Net.WebSockets.Client NuGet package. The new model starting with .NET Core 2.0 actually is for mega-packages. That is why the last date for that package is last January.

Instead of looking for the System.Net.WebSockets.Client.dll binary in a System.Net.WebSockets.Client NuGet package, you should look for that binary is one of the larger packages, i.e. NetCoreApp packages, etc.

@weshaggard can explain this further.

@weshaggard
Copy link
Member

As of 2.0 we ship this library in the Microsoft.NETCore.App packages which also contain ngen'ed binaries. However we do have our private corefx package https://dotnet.myget.org/F/dotnet-core/api/v2/package/runtime.linux-x64.Microsoft.Private.CoreFx.NETCoreApp/4.5.0-preview1-26001-02 which should contain the IL file for this. Give that a try.

As an aside @davidsh this fact is going to make fixing https://github.com/dotnet/corefx/issues/9503 more complicated

@qmfrederik
Copy link
Contributor

@stephentoub Yes, I think that validation condition needs tweaking. At least in my case, subprotocolEnumerableValues contained one empty string.

Changing the condition to:

if (subprotocolArray.Length != 1 ||
    (subprotocolArray[0] != string.Empty && (subprotocol = options.RequestedSubProtocols.Find(requested => string.Equals(requested, subprotocolArray[0], StringComparison.OrdinalIgnoreCase))) == null))

worked for me.

In other news, I got the managed implementation to work on Windows - thanks all who helped me, greatly appreciated! I'll keep you posted.

@stephentoub
Copy link
Member

In other news, I got the managed implementation to work on Windows - thanks all who helped me, greatly appreciated! I'll keep you posted.

Excellent.

@qmfrederik
Copy link
Contributor

@stephentoub I wanted to come back on this:

I have a X509Certificate2 which represents certificate of the CA issuing the SSL certificates for the server, but I haven't been able to figure out yet how to let the ClientWebSocket know to either trust that CA or let me do the validation myself.

Is there a way for me to override the server certificate validation or pass a list of valid root certificates to ClientWebSocket?

@stephentoub
Copy link
Member

Is there a way for me to override the server certificate validation or pass a list of valid root certificates to ClientWebSocket?

Not currently. Feel free to open an API proposal issue.

@karelz
Copy link
Member

karelz commented Jan 12, 2018

We will be switching Windows WebSockets to managed WebSockets implementation in dotnet/corefx#9503, which already implements all ClientWebSocketOptions (https://github.com/dotnet/corefx/issues/5120#issuecomment-334212032).

Closing as duplicate of dotnet/corefx#9503.

@karelz karelz closed this as completed Jan 12, 2018
@msftgits msftgits transferred this issue from dotnet/corefx Jan 31, 2020
@msftgits msftgits added this to the 2.1.0 milestone Jan 31, 2020
@dotnet dotnet locked as resolved and limited conversation to collaborators Jan 3, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Net enhancement Product code improvement that does NOT require public API changes/additions os-windows
Projects
None yet
Development

No branches or pull requests

10 participants