Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 63da6ec

Browse files
committed
Merge pull request #2511 from pgavlin/SNWebSocketsClient
Initial commit of System.Net.WebSockets.Client.
2 parents c664c18 + 2f2fae2 commit 63da6ec

30 files changed

+6862
-15
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System;
5+
using System.Runtime.InteropServices;
6+
7+
using Microsoft.Win32.SafeHandles;
8+
9+
internal partial class Interop
10+
{
11+
internal partial class mincore
12+
{
13+
[DllImport(Libraries.LibraryLoader, ExactSpelling = true, SetLastError = true)]
14+
public static extern unsafe bool FreeLibrary([In] IntPtr hModule);
15+
}
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System;
5+
using System.Runtime.InteropServices;
6+
7+
using Microsoft.Win32.SafeHandles;
8+
9+
internal partial class Interop
10+
{
11+
internal partial class mincore
12+
{
13+
[DllImport(Libraries.LibraryLoader, CharSet = CharSet.Ansi, BestFitMapping = false)]
14+
public static extern IntPtr GetProcAddress(SafeLibraryHandle hModule, string lpProcName);
15+
}
16+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System.Runtime.CompilerServices;
5+
6+
internal partial class Interop
7+
{
8+
internal partial class mincore
9+
{
10+
// Implementation of HRESULT_FROM_WIN32 macro
11+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
12+
internal static int HRESULT_FROM_WIN32(int errorCode)
13+
{
14+
if ((errorCode & 0x80000000) == 0x80000000)
15+
{
16+
return errorCode;
17+
}
18+
19+
return (errorCode & 0x0000FFFF) | unchecked((int)0x80070000);
20+
}
21+
}
22+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System;
5+
using System.Runtime.InteropServices;
6+
7+
using Microsoft.Win32.SafeHandles;
8+
9+
internal partial class Interop
10+
{
11+
internal partial class mincore
12+
{
13+
[DllImport(Libraries.LibraryLoader, ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
14+
public static extern SafeLibraryHandle LoadLibraryExW([In] string lpwLibFileName, [In] IntPtr hFile, [In] uint dwFlags);
15+
}
16+
}

src/Common/src/Interop/Windows/winhttp/Interop.SafeWinHttpHandle.cs

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,43 +4,81 @@
44
using System;
55
using System.Runtime.InteropServices;
66

7+
using Microsoft.Win32.SafeHandles;
8+
79
internal partial class Interop
810
{
911
internal partial class WinHttp
1012
{
11-
// TODO: Move SafeHandleZeroOrMinusOneIsInvalid class to Common
12-
internal abstract class SafeHandleZeroOrMinusOneIsInvalid : SafeHandle
13+
// Issue 2501: Fold SafeWinHttpHandleWithCallback and SafeWinHttpHandle into a single type.
14+
//
15+
// SafeWinHttpHandleWithCallback is a better pattern; SafeWinHttpHandle is probably a better
16+
// name. The implementation for the former should probably be folded into the latter.
17+
18+
internal class SafeWinHttpHandle : SafeHandleZeroOrMinusOneIsInvalid
1319
{
14-
protected SafeHandleZeroOrMinusOneIsInvalid(bool ownsHandle)
15-
: base(IntPtr.Zero, ownsHandle)
20+
public SafeWinHttpHandle() : base(true)
1621
{
1722
}
1823

19-
public override bool IsInvalid
24+
public static void DisposeAndClearHandle(ref SafeWinHttpHandle winHttpHandler)
2025
{
21-
get { return this.handle == IntPtr.Zero || this.handle == (IntPtr)(-1); }
26+
if (winHttpHandler != null)
27+
{
28+
winHttpHandler.Dispose();
29+
winHttpHandler = null;
30+
}
31+
}
32+
33+
// Important: WinHttp API calls should not happen while another WinHttp call for the same handle did not
34+
// return. During finalization that was not initiated by the Dispose pattern we don't expect any other WinHttp
35+
// calls in progress.
36+
protected override bool ReleaseHandle()
37+
{
38+
// TODO(Issue 2500): Add logging so we know when the handle gets closed.
39+
return Interop.WinHttp.WinHttpCloseHandle(handle);
2240
}
2341
}
2442

25-
internal class SafeWinHttpHandle : SafeHandleZeroOrMinusOneIsInvalid
43+
internal sealed class SafeWinHttpHandleWithCallback : SafeWinHttpHandle
2644
{
27-
public SafeWinHttpHandle() : base(true)
45+
// Add a reference to this object so that the AppDomain doesn't get unloaded before the last WinHttp callback.
46+
public void AttachCallback()
47+
{
48+
bool success = false;
49+
DangerousAddRef(ref success);
50+
if (!success)
51+
{
52+
throw new InvalidOperationException("Could not increase SafeHandle reference.");
53+
}
54+
}
55+
56+
public void DetachCallback()
2857
{
58+
DangerousRelease();
2959
}
3060

31-
public static void DisposeAndClearHandle(ref SafeWinHttpHandle handle)
61+
protected override void Dispose(bool disposing)
3262
{
33-
if (handle != null)
63+
if (disposing)
3464
{
35-
handle.Dispose();
36-
handle = null;
65+
// We need to initiate the asynchronous process of closing the WinHttp handle:
66+
// 1. Ensure that all other WinHttp function calls for this handle have returned.
67+
// 2. Call WinHttpCloseHandle.
68+
// 3. Wait for WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING in the callback.
69+
// On this event, call DetachCallback.
70+
//
71+
// WinHttp guarantees that no other calls to the callback are made for this handle after
72+
// WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING has been received.
73+
// Holding the reference to the SafeHandle object ensures that the appdomain where the SafeHandle
74+
// lives until it is guaranteed that no further callback calls are made from the native (WinHttp) side.
75+
Interop.WinHttp.WinHttpCloseHandle(handle);
3776
}
3877
}
3978

4079
protected override bool ReleaseHandle()
4180
{
42-
// TODO: Add logging so we know when the handle gets closed.
43-
return Interop.WinHttp.WinHttpCloseHandle(this.handle);
81+
return base.ReleaseHandle();
4482
}
4583
}
4684
}

src/Common/src/Interop/Windows/winhttp/Interop.winhttp.cs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ public static extern SafeWinHttpHandle WinHttpConnect(
2828
ushort serverPort,
2929
uint reserved);
3030

31+
// NOTE: except for the return type, this refers to the same function as WinHttpConnect.
32+
[DllImport(Interop.Libraries.WinHttp, EntryPoint = "WinHttpConnect", CharSet = CharSet.Unicode, SetLastError = true)]
33+
public static extern SafeWinHttpHandleWithCallback WinHttpConnectWithCallback(
34+
SafeWinHttpHandle sessionHandle,
35+
string serverName,
36+
ushort serverPort,
37+
uint reserved);
38+
3139
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
3240
public static extern SafeWinHttpHandle WinHttpOpenRequest(
3341
SafeWinHttpHandle connectHandle,
@@ -38,6 +46,17 @@ public static extern SafeWinHttpHandle WinHttpOpenRequest(
3846
string acceptTypes,
3947
uint flags);
4048

49+
// NOTE: except for the return type, this refers to the same function as WinOpenRequest.
50+
[DllImport(Interop.Libraries.WinHttp, EntryPoint = "WinHttpOpenRequest", CharSet = CharSet.Unicode, SetLastError = true)]
51+
public static extern SafeWinHttpHandleWithCallback WinHttpOpenRequestWithCallback(
52+
SafeWinHttpHandle connectHandle,
53+
string verb,
54+
string objectName,
55+
string version,
56+
string referrer,
57+
string acceptTypes,
58+
uint flags);
59+
4160
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
4261
[return: MarshalAs(UnmanagedType.Bool)]
4362
public static extern bool WinHttpAddRequestHeaders(
@@ -206,5 +225,61 @@ public static extern IntPtr WinHttpSetStatusCallback(
206225
WINHTTP_STATUS_CALLBACK callback,
207226
uint notificationFlags,
208227
IntPtr reserved);
228+
229+
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
230+
public static extern SafeWinHttpHandleWithCallback WinHttpWebSocketCompleteUpgrade(
231+
SafeWinHttpHandle requestHandle,
232+
IntPtr context);
233+
234+
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = false)]
235+
public static extern uint WinHttpWebSocketSend(
236+
SafeWinHttpHandle webSocketHandle,
237+
WINHTTP_WEB_SOCKET_BUFFER_TYPE bufferType,
238+
IntPtr buffer,
239+
uint bufferLength);
240+
241+
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = false)]
242+
public static extern uint WinHttpWebSocketReceive(
243+
SafeWinHttpHandle webSocketHandle,
244+
IntPtr buffer,
245+
uint bufferLength,
246+
out uint bytesRead,
247+
out WINHTTP_WEB_SOCKET_BUFFER_TYPE bufferType);
248+
249+
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = false)]
250+
public static extern uint WinHttpWebSocketShutdown(
251+
SafeWinHttpHandle webSocketHandle,
252+
ushort status,
253+
byte[] reason,
254+
uint reasonLength);
255+
256+
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = false)]
257+
public static extern uint WinHttpWebSocketShutdown(
258+
SafeWinHttpHandle webSocketHandle,
259+
ushort status,
260+
IntPtr reason,
261+
uint reasonLength);
262+
263+
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = false)]
264+
public static extern uint WinHttpWebSocketClose(
265+
SafeWinHttpHandle webSocketHandle,
266+
ushort status,
267+
byte[] reason,
268+
uint reasonLength);
269+
270+
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = false)]
271+
public static extern uint WinHttpWebSocketClose(
272+
SafeWinHttpHandle webSocketHandle,
273+
ushort status,
274+
IntPtr reason,
275+
uint reasonLength);
276+
277+
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = false)]
278+
public static extern uint WinHttpWebSocketQueryCloseStatus(
279+
SafeWinHttpHandle webSocketHandle,
280+
out ushort status,
281+
byte[] reason,
282+
uint reasonLength,
283+
out uint reasonLengthConsumed);
209284
}
210285
}

src/Common/src/Interop/Windows/winhttp/Interop.winhttp_types.cs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,21 @@
33

44
using System;
55
using System.Runtime.InteropServices;
6+
using System.Text;
67

78
internal partial class Interop
89
{
910
internal partial class WinHttp
1011
{
12+
public const uint ERROR_SUCCESS = 0;
1113
public const uint ERROR_FILE_NOT_FOUND = 2;
1214
public const uint ERROR_INVALID_HANDLE = 6;
1315
public const uint ERROR_INVALID_PARAMETER = 87;
1416
public const uint ERROR_INSUFFICIENT_BUFFER = 122;
1517
public const uint ERROR_NOT_FOUND = 1168;
1618
public const uint ERROR_WINHTTP_INVALID_OPTION = 12009;
1719
public const uint ERROR_WINHTTP_LOGIN_FAILURE = 12015;
20+
public const uint ERROR_WINHTTP_OPERATION_CANCELLED = 12017;
1821
public const uint ERROR_WINHTTP_INCORRECT_HANDLE_STATE = 12019;
1922
public const uint ERROR_WINHTTP_CONNECTION_ERROR = 12030;
2023
public const uint ERROR_WINHTTP_RESEND_REQUEST = 12032;
@@ -58,7 +61,7 @@ internal partial class WinHttp
5861

5962
public const uint WINHTTP_FLAG_SECURE = 0x00800000;
6063

61-
public const string WINHTTP_NO_ADDITIONAL_HEADERS = null;
64+
public const StringBuilder WINHTTP_NO_ADDITIONAL_HEADERS = null;
6265

6366
public const uint WINHTTP_QUERY_FLAG_NUMBER = 0x20000000;
6467
public const uint WINHTTP_QUERY_VERSION = 18;
@@ -141,6 +144,28 @@ internal partial class WinHttp
141144
public const uint WINHTTP_OPTION_MAX_RESPONSE_DRAIN_SIZE = 92;
142145
public const uint WINHTTP_OPTION_CONNECTION_INFO = 93;
143146

147+
public const uint WINHTTP_OPTION_ASSURED_NON_BLOCKING_CALLBACKS = 111;
148+
149+
public const uint WINHTTP_OPTION_UPGRADE_TO_WEB_SOCKET = 114;
150+
public const uint WINHTTP_OPTION_WEB_SOCKET_CLOSE_TIMEOUT = 115;
151+
public const uint WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL = 116;
152+
153+
public const uint WINHTTP_OPTION_WEB_SOCKET_RECEIVE_BUFFER_SIZE = 122;
154+
public const uint WINHTTP_OPTION_WEB_SOCKET_SEND_BUFFER_SIZE = 123;
155+
156+
public enum WINHTTP_WEB_SOCKET_BUFFER_TYPE
157+
{
158+
WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE = 0,
159+
WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE = 1,
160+
WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE = 2,
161+
WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE = 3,
162+
WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE = 4
163+
}
164+
165+
public const uint WINHTTP_OPTION_CONTEXT_VALUE = 45;
166+
167+
public const uint WINHTTP_FLAG_ASYNC = 0x10000000;
168+
144169
public const uint WINHTTP_CALLBACK_STATUS_RESOLVING_NAME = 0x00000001;
145170
public const uint WINHTTP_CALLBACK_STATUS_NAME_RESOLVED = 0x00000002;
146171
public const uint WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER = 0x00000004;
@@ -219,5 +244,40 @@ public struct WINHTTP_CONNECTION_INFO
219244
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
220245
public byte[] RemoteAddress; // SOCKADDR_STORAGE
221246
}
247+
248+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
249+
public struct WINHTTP_ASYNC_RESULT
250+
{
251+
public IntPtr dwResult;
252+
public uint dwError;
253+
}
254+
255+
public const uint API_RECEIVE_RESPONSE = 1;
256+
public const uint API_QUERY_DATA_AVAILABLE = 2;
257+
public const uint API_READ_DATA = 3;
258+
public const uint API_WRITE_DATA = 4;
259+
public const uint API_SEND_REQUEST = 5;
260+
261+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
262+
public struct WINHTTP_WEB_SOCKET_ASYNC_RESULT
263+
{
264+
public WINHTTP_ASYNC_RESULT AsyncResult;
265+
public WINHTTP_WEB_SOCKET_OPERATION Operation;
266+
}
267+
268+
public enum WINHTTP_WEB_SOCKET_OPERATION
269+
{
270+
WINHTTP_WEB_SOCKET_SEND_OPERATION = 0,
271+
WINHTTP_WEB_SOCKET_RECEIVE_OPERATION = 1,
272+
WINHTTP_WEB_SOCKET_CLOSE_OPERATION = 2,
273+
WINHTTP_WEB_SOCKET_SHUTDOWN_OPERATION = 3
274+
}
275+
276+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
277+
public struct WINHTTP_WEB_SOCKET_STATUS
278+
{
279+
public uint dwBytesTransferred;
280+
public WINHTTP_WEB_SOCKET_BUFFER_TYPE eBufferType;
281+
}
222282
}
223283
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System;
5+
using System.Runtime.InteropServices;
6+
7+
namespace Microsoft.Win32.SafeHandles
8+
{
9+
// Issue 2499: Replace ad-hoc definitions of SafeHandleZeroOrMinusOneIsInvalid with a single definition
10+
//
11+
// Other definitions of this type should be removed in favor of this definition.
12+
internal abstract class SafeHandleZeroOrMinusOneIsInvalid : SafeHandle
13+
{
14+
protected SafeHandleZeroOrMinusOneIsInvalid(bool ownsHandle)
15+
: base(IntPtr.Zero, ownsHandle)
16+
{
17+
}
18+
19+
public override bool IsInvalid
20+
{
21+
get { return handle == IntPtr.Zero || handle == (IntPtr)(-1); }
22+
}
23+
}
24+
}

0 commit comments

Comments
 (0)