diff --git a/eng/testing/tests.wasi.targets b/eng/testing/tests.wasi.targets index 3fb2112131b170..7f981dde513c7f 100644 --- a/eng/testing/tests.wasi.targets +++ b/eng/testing/tests.wasi.targets @@ -49,6 +49,8 @@ <_XHarnessArgs Condition="'$(WasmXHarnessTestsTimeout)' != ''" >$(_XHarnessArgs) "--timeout=$(WasmXHarnessTestsTimeout)" <_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasi --engine-arg=http <_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasi --engine-arg=inherit-network + <_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasi --engine-arg=tcp + <_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasi --engine-arg=udp <_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasi --engine-arg=allow-ip-name-lookup <_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--env --engine-arg=DOTNET_WASI_PRINT_EXIT_CODE=1 <_XHarnessArgs Condition="'$(WasmXHarnessArgsCli)' != ''" >$(_XHarnessArgs) $(WasmXHarnessArgsCli) diff --git a/src/libraries/Common/src/Interop/Wasi/System.Native/Interop.SocketEvent.cs b/src/libraries/Common/src/Interop/Wasi/System.Native/Interop.SocketEvent.cs new file mode 100644 index 00000000000000..2e8742bc4c7708 --- /dev/null +++ b/src/libraries/Common/src/Interop/Wasi/System.Native/Interop.SocketEvent.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Sys + { + [Flags] + internal enum SocketEvents : int + { + None = 0x00, + Read = 0x01, + Write = 0x02, + ReadClose = 0x04, + Close = 0x08, + Error = 0x10 + } + + [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetWasiSocketDescriptor")] + internal static unsafe partial Error GetWasiSocketDescriptor(IntPtr socket, IntPtr* entry); + } +} diff --git a/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Unix.cs b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Unix.cs index cb81412f460adb..41cdd601dc5611 100644 --- a/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Unix.cs +++ b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Unix.cs @@ -14,7 +14,7 @@ private static unsafe bool IsSupported(AddressFamily af) { // Check for AF_UNIX on iOS/tvOS. The OS claims to support this, but returns EPERM on bind. // We should explicitly set the return here to false, to avoid giving a false impression. - if (af == AddressFamily.Unix && (OperatingSystem.IsTvOS() || (OperatingSystem.IsIOS() && !OperatingSystem.IsMacCatalyst()))) + if (af == AddressFamily.Unix && (OperatingSystem.IsTvOS() || OperatingSystem.IsWasi() || (OperatingSystem.IsIOS() && !OperatingSystem.IsMacCatalyst()))) { return false; } diff --git a/src/libraries/Common/src/System/Threading/Tasks/TaskToAsyncResult.cs b/src/libraries/Common/src/System/Threading/Tasks/TaskToAsyncResult.cs index 7c9bd9073e6d3c..8ffe3ad042fbaf 100644 --- a/src/libraries/Common/src/System/Threading/Tasks/TaskToAsyncResult.cs +++ b/src/libraries/Common/src/System/Threading/Tasks/TaskToAsyncResult.cs @@ -32,6 +32,8 @@ static class TaskToAsyncResult public static IAsyncResult Begin(Task task, AsyncCallback? callback, object? state) { #if NET + if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ArgumentNullException.ThrowIfNull(task); #else if (task is null) diff --git a/src/libraries/Common/tests/StreamConformanceTests/System/IO/StreamConformanceTests.cs b/src/libraries/Common/tests/StreamConformanceTests/System/IO/StreamConformanceTests.cs index c36ce7c6feb0a0..61fb0f51936e27 100644 --- a/src/libraries/Common/tests/StreamConformanceTests/System/IO/StreamConformanceTests.cs +++ b/src/libraries/Common/tests/StreamConformanceTests/System/IO/StreamConformanceTests.cs @@ -65,6 +65,31 @@ public abstract class StreamConformanceTests : FileCleanupTestBase protected virtual bool CanSetLength => CanSeek; protected virtual bool CanSetLengthGreaterThanCapacity => CanSetLength; + protected bool SkipOnWasi(ReadWriteMode mode) + { + if (!OperatingSystem.IsWasi()) return false; + switch (mode) + { + case ReadWriteMode.AsyncArray: + case ReadWriteMode.AsyncMemory: + return false; + case ReadWriteMode.SyncAPM: + case ReadWriteMode.AsyncAPM: + case ReadWriteMode.SyncByte: + case ReadWriteMode.SyncSpan: + case ReadWriteMode.SyncArray: + default: + return true; + } + } + + protected static byte[] GetRandomBytes(int count) + { + byte[] buffer = new byte[count]; + System.Random.Shared.NextBytes(buffer); + return buffer; + } + /// Specifies the form of the read/write operation to use. public enum ReadWriteMode { @@ -700,6 +725,7 @@ public abstract class StandaloneStreamConformanceTests : StreamConformanceTests } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] public virtual async Task ArgumentValidation_ThrowsExpectedException() { await foreach (Stream? stream in GetStreamsForValidation()) @@ -742,6 +768,8 @@ public virtual async Task ReadWriteAsync_Precanceled_ThrowsOperationCanceledExce [MemberData(nameof(AllReadWriteModes))] public virtual async Task Read_NonEmptyStream_Nop_Success(ReadWriteMode mode) { + if (SkipOnWasi(mode)) return; + using Stream? stream = await CreateReadOnlyStream(new byte[10]); if (stream is null) { @@ -758,6 +786,8 @@ public virtual async Task Read_NonEmptyStream_Nop_Success(ReadWriteMode mode) [MemberData(nameof(AllReadWriteModes))] public virtual async Task Write_Nop_Success(ReadWriteMode mode) { + if (SkipOnWasi(mode)) return; + using Stream? stream = await CreateReadWriteStream(); if (stream is null) { @@ -777,8 +807,11 @@ public virtual async Task Write_Nop_Success(ReadWriteMode mode) [InlineData(ReadWriteMode.SyncArray)] [InlineData(ReadWriteMode.AsyncArray)] [InlineData(ReadWriteMode.AsyncAPM)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] public virtual async Task Read_DataStoredAtDesiredOffset(ReadWriteMode mode) { + if (SkipOnWasi(mode)) return; + const byte Expected = 42; using Stream? stream = await CreateReadWriteStream(new byte[] { Expected }); @@ -802,8 +835,11 @@ public virtual async Task Read_DataStoredAtDesiredOffset(ReadWriteMode mode) [InlineData(ReadWriteMode.SyncArray)] [InlineData(ReadWriteMode.AsyncArray)] [InlineData(ReadWriteMode.AsyncAPM)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] public virtual async Task Write_DataReadFromDesiredOffset(ReadWriteMode mode) { + if (SkipOnWasi(mode)) return; + using Stream? stream = await CreateReadWriteStream(); if (stream is null) { @@ -821,6 +857,8 @@ public virtual async Task Write_DataReadFromDesiredOffset(ReadWriteMode mode) [MemberData(nameof(AllReadWriteModes))] public virtual async Task Read_EmptyStream_Nop_Success(ReadWriteMode mode) { + if (SkipOnWasi(mode)) return; + using Stream? stream = await CreateReadOnlyStream(); if (stream is null) { @@ -841,7 +879,9 @@ public virtual async Task Read_EmptyStream_Nop_Success(ReadWriteMode mode) [MemberData(nameof(AllReadWriteModesAndValue), 256)] public virtual async Task Read_PopulatedWithInitialData_KnownSize_Success(ReadWriteMode mode, int size) { - byte[] expected = RandomNumberGenerator.GetBytes(size); + if (SkipOnWasi(mode)) return; + + byte[] expected = GetRandomBytes(size); using Stream? stream = await CreateReadOnlyStream(expected); if (stream is null) @@ -872,7 +912,9 @@ public virtual async Task Read_PopulatedWithInitialData_KnownSize_Success(ReadWr [MemberData(nameof(AllReadWriteModesAndValue), 4097)] public virtual async Task Read_PopulatedWithInitialData_ToEof_Success(ReadWriteMode mode, int size) { - byte[] expected = RandomNumberGenerator.GetBytes(size); + if (SkipOnWasi(mode)) return; + + byte[] expected = GetRandomBytes(size); using Stream? stream = await CreateReadOnlyStream(expected); if (stream is null) @@ -904,7 +946,9 @@ public virtual async Task Read_PopulatedWithInitialData_ToEof_Success(ReadWriteM [MemberData(nameof(AllReadWriteModes))] public virtual async Task Read_PartiallySatisfied_RemainderOfBufferUntouched(ReadWriteMode mode) { - byte[] expected = RandomNumberGenerator.GetBytes(20); + if (SkipOnWasi(mode)) return; + + byte[] expected = GetRandomBytes(20); using Stream? stream = await CreateReadOnlyStream(expected); if (stream is null) @@ -935,7 +979,9 @@ public virtual async Task Read_PartiallySatisfied_RemainderOfBufferUntouched(Rea [InlineData(true)] public virtual async Task Read_CustomMemoryManager_Success(bool useAsync) { - byte[] expected = RandomNumberGenerator.GetBytes(20); + if (OperatingSystem.IsWasi() && !useAsync) return; + + byte[] expected = GetRandomBytes(20); using Stream? stream = await CreateReadOnlyStream(expected); if (stream is null) @@ -959,6 +1005,8 @@ await stream.ReadAsync(memoryManager.Memory) : [InlineData(true)] public virtual async Task Write_CustomMemoryManager_Success(bool useAsync) { + if (OperatingSystem.IsWasi() && !useAsync) return; + using Stream? stream = await CreateReadWriteStream(); if (stream is null) { @@ -967,7 +1015,7 @@ public virtual async Task Write_CustomMemoryManager_Success(bool useAsync) using MemoryManager memoryManager = new NativeMemoryManager(256); Assert.Equal(256, memoryManager.Memory.Length); - byte[] expected = RandomNumberGenerator.GetBytes(memoryManager.Memory.Length); + byte[] expected = GetRandomBytes(memoryManager.Memory.Length); expected.AsSpan().CopyTo(memoryManager.Memory.Span); if (useAsync) @@ -990,6 +1038,8 @@ public virtual async Task Write_CustomMemoryManager_Success(bool useAsync) public virtual async Task CopyTo_CopiesAllDataFromRightPosition_Success( bool useAsync, byte[] expected, int position) { + if (OperatingSystem.IsWasi() && !useAsync) return; + using Stream? stream = await CreateReadOnlyStream(expected); if (stream is null) { @@ -1029,7 +1079,7 @@ public virtual async Task CopyTo_CopiesAllDataFromRightPosition_Success( public static IEnumerable CopyTo_CopiesAllDataFromRightPosition_Success_MemberData() { - byte[] expected = RandomNumberGenerator.GetBytes(16 * 1024); + byte[] expected = GetRandomBytes(16 * 1024); foreach (bool useAsync in new[] { false, true }) { yield return new object[] { useAsync, expected, 0 }; // beginning @@ -1043,6 +1093,8 @@ public static IEnumerable CopyTo_CopiesAllDataFromRightPosition_Succes [MemberData(nameof(AllReadWriteModes))] public virtual async Task Write_Read_Success(ReadWriteMode mode) { + if (SkipOnWasi(mode)) return; + const int Length = 1024; using Stream? stream = await CreateReadWriteStream(); @@ -1051,7 +1103,7 @@ public virtual async Task Write_Read_Success(ReadWriteMode mode) return; } - byte[] expected = RandomNumberGenerator.GetBytes(Length); + byte[] expected = GetRandomBytes(Length); const int Copies = 3; for (int i = 0; i < Copies; i++) @@ -1074,6 +1126,8 @@ public virtual async Task Write_Read_Success(ReadWriteMode mode) [MemberData(nameof(AllReadWriteModes))] public virtual async Task Flush_ReadOnly_DoesntImpactReading(ReadWriteMode mode) { + if (SkipOnWasi(mode)) return; + using Stream? stream = await CreateReadOnlyStream(new byte[] { 0, 1, 2, 3, 4, 5 }); if (stream is null) { @@ -1094,6 +1148,8 @@ public virtual async Task Flush_ReadOnly_DoesntImpactReading(ReadWriteMode mode) [InlineData(ReadWriteMode.AsyncArray)] public virtual async Task Flush_MultipleTimes_Idempotent(ReadWriteMode mode) { + if (SkipOnWasi(mode)) return; + using Stream? stream = await CreateReadWriteStream(); if (stream is null) { @@ -1119,7 +1175,7 @@ public virtual async Task Flush_MultipleTimes_Idempotent(ReadWriteMode mode) await FlushAsync(mode, stream); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public virtual async Task Flush_SetLengthAtEndOfBuffer_OperatesOnValidData() { if (!CanSeek || !CanSetLengthGreaterThanCapacity) @@ -1225,7 +1281,7 @@ public virtual async Task Seek_RandomWalk_ReadConsistency() const int MaxBytesToRead = 21; // Write data to the file - byte[] buffer = RandomNumberGenerator.GetBytes(FileLength); + byte[] buffer = GetRandomBytes(FileLength); stream.Write(buffer, 0, buffer.Length); Assert.Equal(buffer.Length, stream.Position); Assert.Equal(buffer.Length, stream.Length); @@ -1267,7 +1323,7 @@ public virtual async Task Seek_Read_RoundtripsExpectedData(SeekMode mode) const int Length = 512; - byte[] expected = RandomNumberGenerator.GetBytes(Length); + byte[] expected = GetRandomBytes(Length); using Stream? stream = await CreateReadOnlyStream(expected); if (stream is null) { @@ -1293,7 +1349,7 @@ public virtual async Task Seek_PastEnd_ReadReturns0(SeekMode mode) const int Length = 512; - byte[] expected = RandomNumberGenerator.GetBytes(Length); + byte[] expected = GetRandomBytes(Length); using Stream? stream = await CreateReadOnlyStream(expected); if (stream is null) { @@ -1318,7 +1374,7 @@ public virtual async Task Seek_ReadWrite_RoundtripsExpectedData(SeekMode mode) const int Length = 512; - byte[] expected = RandomNumberGenerator.GetBytes(Length); + byte[] expected = GetRandomBytes(Length); using Stream? stream = await CreateReadWriteStream(expected); if (stream is null) { @@ -1442,6 +1498,8 @@ public virtual async Task SetLength_ChangesLengthAccordingly_Success() [MemberData(nameof(AllReadWriteModes))] public virtual async Task SetLength_DataFromShrinkNotPreserved_Success(ReadWriteMode mode) { + if (SkipOnWasi(mode)) return; + if (!CanSeek || !CanSetLength) { return; @@ -1514,6 +1572,8 @@ public async Task SetLength_MaxValue_ThrowsExpectedException() [MemberData(nameof(AllReadWriteModes))] public virtual async Task SeekPastEnd_Write_BeyondCapacity(ReadWriteMode mode) { + if (SkipOnWasi(mode)) return; + if (!CanSeek) { return; @@ -1527,7 +1587,7 @@ public virtual async Task SeekPastEnd_Write_BeyondCapacity(ReadWriteMode mode) long origLength = stream.Length; - byte[] expected = RandomNumberGenerator.GetBytes(10); + byte[] expected = GetRandomBytes(10); // Move past end; doesn't change stream length. int pastEnd = 5; @@ -1612,6 +1672,7 @@ protected static bool Bidirectional(StreamPair streams) => [Fact] [SkipOnPlatform(TestPlatforms.LinuxBionic, "SElinux blocks UNIX sockets in our CI environment")] [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "iOS/tvOS blocks binding to UNIX sockets")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] public virtual async Task ArgumentValidation_ThrowsExpectedException() { using StreamPair streams = await CreateConnectedStreamsAsync(); @@ -1675,7 +1736,7 @@ public virtual async Task ReadAsync_CancelPendingValueTask_ThrowsCancellationExc await ValidateCancelableReadAsyncValueTask_AfterInvocation_ThrowsCancellationException(readable, cancellationDelay); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [SkipOnPlatform(TestPlatforms.LinuxBionic, "SElinux blocks UNIX sockets in our CI environment")] [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "iOS/tvOS blocks binding to UNIX sockets")] public virtual async Task ReadWriteByte_Success() @@ -1684,7 +1745,7 @@ public virtual async Task ReadWriteByte_Success() foreach ((Stream writeable, Stream readable) in GetReadWritePairs(streams)) { - byte[] writerBytes = RandomNumberGenerator.GetBytes(42); + byte[] writerBytes = GetRandomBytes(42); var readerBytes = new byte[writerBytes.Length]; Task writes = Task.Run(() => @@ -1746,6 +1807,7 @@ from writeSize in new[] { 10 * 1024 * 1024 } [SkipOnPlatform(TestPlatforms.Browser, "Not supported on browser")] [Theory] [MemberData(nameof(ReadWrite_Success_Large_MemberData))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] public virtual async Task ReadWrite_Success_Large(ReadWriteMode mode, int writeSize, bool startWithFlush) => await ReadWrite_Success(mode, writeSize, startWithFlush); @@ -1753,8 +1815,11 @@ public virtual async Task ReadWrite_Success_Large(ReadWriteMode mode, int writeS [MemberData(nameof(ReadWrite_Success_MemberData))] [SkipOnPlatform(TestPlatforms.LinuxBionic, "SElinux blocks UNIX sockets in our CI environment")] [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "iOS/tvOS blocks binding to UNIX sockets")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] public virtual async Task ReadWrite_Success(ReadWriteMode mode, int writeSize, bool startWithFlush) { + if (SkipOnWasi(mode)) return; + foreach (CancellationToken nonCanceledToken in new[] { CancellationToken.None, new CancellationTokenSource().Token }) { using StreamPair streams = await CreateConnectedStreamsAsync(); @@ -1766,7 +1831,7 @@ public virtual async Task ReadWrite_Success(ReadWriteMode mode, int writeSize, b await FlushAsync(mode, writeable, nonCanceledToken); } - byte[] writerBytes = RandomNumberGenerator.GetBytes(writeSize); + byte[] writerBytes = GetRandomBytes(writeSize); var readerBytes = new byte[writerBytes.Length]; Task writes = Task.Run(async () => @@ -1813,6 +1878,8 @@ public virtual async Task ReadWrite_Success(ReadWriteMode mode, int writeSize, b [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "iOS/tvOS blocks binding to UNIX sockets")] public virtual async Task ReadWrite_MessagesSmallerThanReadBuffer_Success(ReadWriteMode mode) { + if (SkipOnWasi(mode)) return; + if (!FlushGuaranteesAllDataWritten) { return; @@ -1824,7 +1891,7 @@ public virtual async Task ReadWrite_MessagesSmallerThanReadBuffer_Success(ReadWr foreach ((Stream writeable, Stream readable) in GetReadWritePairs(streams)) { - byte[] writerBytes = RandomNumberGenerator.GetBytes(512); + byte[] writerBytes = GetRandomBytes(512); var readerBytes = new byte[writerBytes.Length * 2]; // Repeatedly write then read a message smaller in size than the read buffer @@ -1856,13 +1923,15 @@ public virtual async Task ReadWrite_MessagesSmallerThanReadBuffer_Success(ReadWr } } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [MemberData(nameof(AllReadWriteModesAndValue), false)] [MemberData(nameof(AllReadWriteModesAndValue), true)] [SkipOnPlatform(TestPlatforms.LinuxBionic, "SElinux blocks UNIX sockets in our CI environment")] [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "iOS/tvOS blocks binding to UNIX sockets")] public virtual async Task Read_Eof_Returns0(ReadWriteMode mode, bool dataAvailableFirst) { + if (SkipOnWasi(mode)) return; + using StreamPair streams = await CreateConnectedStreamsAsync(); (Stream writeable, Stream readable) = GetReadWritePair(streams); @@ -1901,8 +1970,11 @@ public virtual async Task Read_Eof_Returns0(ReadWriteMode mode, bool dataAvailab [InlineData(ReadWriteMode.AsyncAPM)] [SkipOnPlatform(TestPlatforms.LinuxBionic, "SElinux blocks UNIX sockets in our CI environment")] [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "iOS/tvOS blocks binding to UNIX sockets")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] public virtual async Task Read_DataStoredAtDesiredOffset(ReadWriteMode mode) { + if (SkipOnWasi(mode)) return; + using StreamPair streams = await CreateConnectedStreamsAsync(); (Stream writeable, Stream readable) = GetReadWritePair(streams); @@ -1932,8 +2004,11 @@ public virtual async Task Read_DataStoredAtDesiredOffset(ReadWriteMode mode) [InlineData(ReadWriteMode.AsyncAPM)] [SkipOnPlatform(TestPlatforms.LinuxBionic, "SElinux blocks UNIX sockets in our CI environment")] [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "iOS/tvOS blocks binding to UNIX sockets")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] public virtual async Task Write_DataReadFromDesiredOffset(ReadWriteMode mode) { + if (SkipOnWasi(mode)) return; + using StreamPair streams = await CreateConnectedStreamsAsync(); (Stream writeable, Stream readable) = GetReadWritePair(streams); @@ -2203,8 +2278,11 @@ await Task.Factory.StartNew(() => [InlineData(ReadWriteMode.AsyncAPM)] [SkipOnPlatform(TestPlatforms.LinuxBionic, "SElinux blocks UNIX sockets in our CI environment")] [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "iOS/tvOS blocks binding to UNIX sockets")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] public virtual async Task ZeroByteRead_BlocksUntilDataAvailableOrNops(ReadWriteMode mode) { + if (SkipOnWasi(mode)) return; + using StreamPair streams = await CreateConnectedStreamsAsync(); foreach ((Stream writeable, Stream readable) in GetReadWritePairs(streams)) { @@ -2273,6 +2351,8 @@ public virtual async Task ZeroByteRead_BlocksUntilDataAvailableOrNops(ReadWriteM [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "iOS/tvOS blocks binding to UNIX sockets")] public virtual async Task ZeroByteWrite_OtherDataReceivedSuccessfully(ReadWriteMode mode) { + if (SkipOnWasi(mode)) return; + byte[][] buffers = new[] { Array.Empty(), "hello"u8.ToArray(), Array.Empty(), "world"u8.ToArray() }; using StreamPair streams = await CreateConnectedStreamsAsync(); @@ -2321,7 +2401,7 @@ public virtual async Task ZeroByteWrite_OtherDataReceivedSuccessfully(ReadWriteM } } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [InlineData(false)] [InlineData(true)] [SkipOnPlatform(TestPlatforms.LinuxBionic, "SElinux blocks UNIX sockets in our CI environment")] @@ -2441,13 +2521,13 @@ public static IEnumerable CopyToAsync_AllDataCopied_MemberData() => [OuterLoop("May take several seconds", ~TestPlatforms.Browser)] [SkipOnPlatform(TestPlatforms.Browser, "Not supported on browser")] - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [InlineData(false)] [InlineData(true)] public virtual async Task CopyToAsync_AllDataCopied_Large(bool useAsync) => await CopyToAsync_AllDataCopied(1024 * 1024, useAsync); - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [MemberData(nameof(CopyToAsync_AllDataCopied_MemberData))] [SkipOnPlatform(TestPlatforms.LinuxBionic, "SElinux blocks UNIX sockets in our CI environment")] [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "iOS/tvOS blocks binding to UNIX sockets")] @@ -2457,7 +2537,7 @@ public virtual async Task CopyToAsync_AllDataCopied(int byteCount, bool useAsync (Stream writeable, Stream readable) = GetReadWritePair(streams); var results = new MemoryStream(); - byte[] dataToCopy = RandomNumberGenerator.GetBytes(byteCount); + byte[] dataToCopy = GetRandomBytes(byteCount); Task copyTask; if (useAsync) @@ -2492,6 +2572,7 @@ await Task.WhenAll(Enumerable.Range(0, 20).Select(_ => Task.Run(async () => [Fact] [SkipOnPlatform(TestPlatforms.LinuxBionic, "SElinux blocks UNIX sockets in our CI environment")] [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "iOS/tvOS blocks binding to UNIX sockets")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/108151", TestPlatforms.Wasi)] public virtual async Task Timeout_Roundtrips() { using StreamPair streams = await CreateConnectedStreamsAsync(); @@ -2528,6 +2609,7 @@ public virtual async Task Timeout_Roundtrips() [Fact] [SkipOnPlatform(TestPlatforms.LinuxBionic, "SElinux blocks UNIX sockets in our CI environment")] [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "iOS/tvOS blocks binding to UNIX sockets")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/108151", TestPlatforms.Wasi)] public virtual async Task ReadTimeout_Expires_Throws() { using StreamPair streams = await CreateConnectedStreamsAsync(); @@ -2924,6 +3006,8 @@ await WhenAllOrAnyFailed( [InlineData(ReadWriteMode.AsyncAPM)] public virtual async Task ZeroByteRead_PerformsZeroByteReadOnUnderlyingStreamWhenDataNeeded(ReadWriteMode mode) { + if (SkipOnWasi(mode)) return; + if (!ZeroByteReadPerformsZeroByteReadOnUnderlyingStream) { return; diff --git a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.UnixDomainSockets.cs b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.UnixDomainSockets.cs index b5d182a28b55c5..b465b5158fc96a 100644 --- a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.UnixDomainSockets.cs +++ b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.UnixDomainSockets.cs @@ -7,6 +7,7 @@ namespace System.IO.Pipes.Tests { + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support UnixDomain")] public class NamedPipeTest_UnixDomainSockets { [Fact] diff --git a/src/libraries/System.Net.Sockets/Directory.Build.props b/src/libraries/System.Net.Sockets/Directory.Build.props index bc799605d32edf..ce244cbea56199 100644 --- a/src/libraries/System.Net.Sockets/Directory.Build.props +++ b/src/libraries/System.Net.Sockets/Directory.Build.props @@ -3,7 +3,6 @@ Microsoft true - - browser;wasi + browser \ No newline at end of file diff --git a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj index e7a4cfbcce3c9f..043fd6ecaba8ce 100644 --- a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj +++ b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj @@ -1,7 +1,7 @@ - $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-unix;$(NetCoreAppCurrent)-osx;$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent) + $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-unix;$(NetCoreAppCurrent)-osx;$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent)-wasi;$(NetCoreAppCurrent) true @@ -48,7 +48,7 @@ - + @@ -183,14 +183,28 @@ Link="Common\System\Net\CompletionPortHelper.Windows.cs" /> + + + + + + + + + + + + + + - - - buffer) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + if (GetType() != typeof(NetworkStream)) { // NetworkStream is not sealed, and a derived type may have overridden Read(byte[], int, int) prior @@ -260,6 +272,8 @@ public override int Read(Span buffer) public override unsafe int ReadByte() { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + byte b; return Read(new Span(&b, 1)) == 0 ? -1 : b; } @@ -282,6 +296,8 @@ public override unsafe int ReadByte() // way to indicate an error. public override void Write(byte[] buffer, int offset, int count) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ValidateBufferArguments(buffer, offset, count); ThrowIfDisposed(); if (!CanWrite) @@ -303,6 +319,8 @@ public override void Write(byte[] buffer, int offset, int count) public override void Write(ReadOnlySpan buffer) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + if (GetType() != typeof(NetworkStream)) { // NetworkStream is not sealed, and a derived type may have overridden Write(byte[], int, int) prior @@ -414,6 +432,8 @@ protected override void Dispose(bool disposing) // An IASyncResult, representing the read. public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ValidateBufferArguments(buffer, offset, count); ThrowIfDisposed(); if (!CanRead) @@ -447,6 +467,8 @@ public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, Asy // The number of bytes read. May throw an exception. public override int EndRead(IAsyncResult asyncResult) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ArgumentNullException.ThrowIfNull(asyncResult); @@ -476,6 +498,8 @@ public override int EndRead(IAsyncResult asyncResult) // An IASyncResult, representing the write. public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ValidateBufferArguments(buffer, offset, count); ThrowIfDisposed(); if (!CanWrite) @@ -506,6 +530,8 @@ public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, As // Returns: The number of bytes read. May throw an exception. public override void EndWrite(IAsyncResult asyncResult) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ArgumentNullException.ThrowIfNull(asyncResult); @@ -659,6 +685,8 @@ public override void SetLength(long value) private int _currentWriteTimeout = -1; internal void SetSocketTimeoutOption(SocketShutdown mode, int timeout, bool silent) { + if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // https://github.com/dotnet/runtime/issues/108151 + if (timeout < 0) { timeout = 0; // -1 becomes 0 for the winsock stack diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SafeSocketHandle.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SafeSocketHandle.Unix.cs index e3d609c69483c9..cb5420dab85036 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SafeSocketHandle.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SafeSocketHandle.Unix.cs @@ -231,6 +231,11 @@ private unsafe SocketError DoCloseHandle(bool abortive) { return SocketPal.GetSocketErrorForErrorCode(CloseHandle(handle)); } + if (OperatingSystem.IsWasi()) + { + // WASI never blocks and doesn't support linger options + return SocketPal.GetSocketErrorForErrorCode(CloseHandle(handle)); + } // If abortive is not set, we're not running on the finalizer thread, so it's safe to block here. // We can honor the linger options set on the socket. It also means closesocket() might return diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs index 3e4650f547c483..c90bd301f501cf 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs @@ -61,6 +61,19 @@ partial void ValidateForMultiConnect(bool isMultiEndpoint) private static unsafe void LoadSocketTypeFromHandle( SafeSocketHandle handle, out AddressFamily addressFamily, out SocketType socketType, out ProtocolType protocolType, out bool blocking, out bool isListening, out bool isSocket) { + if (OperatingSystem.IsWasi()) + { + // FIXME: Unify with unix after https://github.com/WebAssembly/wasi-libc/issues/537 + blocking = false; + Interop.Error e = Interop.Sys.GetSocketType(handle, out addressFamily, out socketType, out protocolType, out isListening); + if (e == Interop.Error.ENOTSOCK) + { + throw new SocketException((int)SocketError.NotSocket); + } + handle.IsSocket = isSocket = true; + return; + } + if (Interop.Sys.FStat(handle, out Interop.Sys.FileStatus stat) == -1) { throw new SocketException((int)SocketError.NotSocket); diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs index e1f19c03d6d0ad..1202cacc380900 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs @@ -45,8 +45,8 @@ public partial class Socket : IDisposable // When the socket is created it will be in blocking mode. We'll only be able to Accept or Connect, // so we need to handle one of these cases at a time. - private bool _willBlock = true; // Desired state of the socket from the user. - private bool _willBlockInternal = true; // Actual win32 state of the socket. + private bool _willBlock = !OperatingSystem.IsWasi(); // Desired state of the socket from the user. + private bool _willBlockInternal = !OperatingSystem.IsWasi(); // Actual state of the socket. private bool _isListening; // Our internal state doesn't automatically get updated after a non-blocking connect @@ -64,15 +64,15 @@ public partial class Socket : IDisposable private ProtocolType _protocolType; // Bool marked true if the native socket option IP_PKTINFO or IPV6_PKTINFO has been set. - private bool _receivingPacketInformation; + private bool _receivingPacketInformation = OperatingSystem.IsWasi(); private int _closeTimeout = Socket.DefaultCloseTimeout; private bool _disposed; public Socket(SocketType socketType, ProtocolType protocolType) - : this(OSSupportsIPv6 ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork, socketType, protocolType) + : this(OSSupportsIPv6DualMode ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork, socketType, protocolType) { - if (OSSupportsIPv6) + if (OSSupportsIPv6DualMode) { DualMode = true; } @@ -169,6 +169,8 @@ private unsafe Socket(SafeSocketHandle handle, bool loadPropertiesFromHandle) break; case AddressFamily.Unix: + if (!Socket.OSSupportsUnixDomainSockets) throw new PlatformNotSupportedException(); + _rightEndPoint = new UnixDomainSocketEndPoint(buffer.Slice(0, bufferLength)); break; } @@ -201,6 +203,8 @@ private unsafe Socket(SafeSocketHandle handle, bool loadPropertiesFromHandle) break; case AddressFamily.Unix: + if (!Socket.OSSupportsUnixDomainSockets) throw new PlatformNotSupportedException(); + _remoteEndPoint = new UnixDomainSocketEndPoint(buffer.Slice(0, bufferLength)); break; } @@ -255,6 +259,10 @@ private static SafeSocketHandle ValidateHandle(SafeSocketHandle handle) public static bool OSSupportsIPv4 => SocketProtocolSupportPal.OSSupportsIPv4; public static bool OSSupportsIPv6 => SocketProtocolSupportPal.OSSupportsIPv6; + [UnsupportedOSPlatformGuard("wasi")] + internal static bool OSSupportsIPv6DualMode => !OperatingSystem.IsWasi() && OSSupportsIPv6; + [UnsupportedOSPlatformGuard("wasi")] + internal static bool OSSupportsThreads => !OperatingSystem.IsWasi(); public static bool OSSupportsUnixDomainSockets => SocketProtocolSupportPal.OSSupportsUnixDomainSockets; // Gets the amount of data pending in the network's input buffer that can be @@ -534,10 +542,12 @@ public int ReceiveTimeout { get { + if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 return (int)GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout)!; } set { + if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 ArgumentOutOfRangeException.ThrowIfLessThan(value, -1); if (value == -1) { @@ -552,11 +562,13 @@ public int SendTimeout { get { + if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 return (int)GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout)!; } set { + if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 ArgumentOutOfRangeException.ThrowIfLessThan(value, -1); if (value == -1) { @@ -572,10 +584,14 @@ public LingerOption? LingerState { get { + if (OperatingSystem.IsWasi()) return new LingerOption(false, 0); + return (LingerOption?)GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger); } set { + if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, value!); } } @@ -636,6 +652,8 @@ public bool DontFragment { get { + if (OperatingSystem.IsWasi()) return false; + if (_addressFamily == AddressFamily.InterNetwork || (_addressFamily == AddressFamily.InterNetworkV6 && DualMode)) { return (int)GetSocketOption(SocketOptionLevel.IP, SocketOptionName.DontFragment)! != 0 ? true : false; @@ -648,6 +666,8 @@ public bool DontFragment set { + if (OperatingSystem.IsWasi() && value) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + if (_addressFamily == AddressFamily.InterNetwork || (_addressFamily == AddressFamily.InterNetworkV6 && DualMode)) { SetSocketOption(SocketOptionLevel.IP, SocketOptionName.DontFragment, value ? 1 : 0); @@ -663,6 +683,10 @@ public bool MulticastLoopback { get { + if (OperatingSystem.IsWasi()) + { + return false; + } if (_addressFamily == AddressFamily.InterNetwork) { return (int)GetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastLoopback)! != 0 ? true : false; @@ -679,6 +703,7 @@ public bool MulticastLoopback set { + if (OperatingSystem.IsWasi() && value) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 if (_addressFamily == AddressFamily.InterNetwork) { SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastLoopback, value ? 1 : 0); @@ -699,7 +724,7 @@ public bool EnableBroadcast { get { - if (SocketType == SocketType.Stream) + if (OperatingSystem.IsWasi() || SocketType == SocketType.Stream) { return false; } @@ -707,6 +732,7 @@ public bool EnableBroadcast } set { + if (OperatingSystem.IsWasi() && value) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, value ? 1 : 0); } } @@ -723,6 +749,10 @@ public bool DualMode { return false; } + if (!OSSupportsIPv6DualMode) + { + return false; + } return ((int)GetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only)! == 0); } set @@ -731,6 +761,9 @@ public bool DualMode { throw new NotSupportedException(SR.net_invalidversion); } + + if (!OSSupportsIPv6DualMode && value) throw new PlatformNotSupportedException(); + SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, value ? 0 : 1); } } @@ -796,6 +829,8 @@ private void DoBind(EndPoint endPointSnapshot, SocketAddress socketAddress) // Establishes a connection to a remote system. public void Connect(EndPoint remoteEP) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ArgumentNullException.ThrowIfNull(remoteEP); @@ -840,6 +875,8 @@ public void Connect(EndPoint remoteEP) public void Connect(IPAddress address, int port) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ArgumentNullException.ThrowIfNull(address); @@ -863,6 +900,8 @@ public void Connect(IPAddress address, int port) public void Connect(string host, int port) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ArgumentNullException.ThrowIfNull(host); @@ -892,6 +931,8 @@ public void Connect(string host, int port) public void Connect(IPAddress[] addresses, int port) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ArgumentNullException.ThrowIfNull(addresses); @@ -987,6 +1028,8 @@ public void Listen(int backlog) // Creates a new Sockets.Socket instance to handle an incoming connection. public Socket Accept() { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); if (_rightEndPoint == null) @@ -1054,26 +1097,36 @@ public Socket Accept() // Sends a data buffer to a connected socket. public int Send(byte[] buffer, int size, SocketFlags socketFlags) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + return Send(buffer, 0, size, socketFlags); } public int Send(byte[] buffer, SocketFlags socketFlags) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + return Send(buffer, 0, buffer != null ? buffer.Length : 0, socketFlags); } public int Send(byte[] buffer) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + return Send(buffer, 0, buffer != null ? buffer.Length : 0, SocketFlags.None); } public int Send(IList> buffers) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + return Send(buffers, SocketFlags.None); } public int Send(IList> buffers, SocketFlags socketFlags) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + SocketError errorCode; int bytesTransferred = Send(buffers, socketFlags, out errorCode); if (errorCode != SocketError.Success) @@ -1085,6 +1138,8 @@ public int Send(IList> buffers, SocketFlags socketFlags) public int Send(IList> buffers, SocketFlags socketFlags, out SocketError errorCode) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ArgumentNullException.ThrowIfNull(buffers); @@ -1121,6 +1176,8 @@ public int Send(IList> buffers, SocketFlags socketFlags, out // Sends data to a connected socket, starting at the indicated location in the buffer. public int Send(byte[] buffer, int offset, int size, SocketFlags socketFlags) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + SocketError errorCode; int bytesTransferred = Send(buffer, offset, size, socketFlags, out errorCode); if (errorCode != SocketError.Success) @@ -1132,6 +1189,8 @@ public int Send(byte[] buffer, int offset, int size, SocketFlags socketFlags) public int Send(byte[] buffer, int offset, int size, SocketFlags socketFlags, out SocketError errorCode) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ValidateBufferArguments(buffer, offset, size); @@ -1171,6 +1230,8 @@ public int Send(byte[] buffer, int offset, int size, SocketFlags socketFlags, ou public int Send(ReadOnlySpan buffer, SocketFlags socketFlags) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + int bytesTransferred = Send(buffer, socketFlags, out SocketError errorCode); return errorCode == SocketError.Success ? bytesTransferred : @@ -1179,6 +1240,8 @@ public int Send(ReadOnlySpan buffer, SocketFlags socketFlags) public int Send(ReadOnlySpan buffer, SocketFlags socketFlags, out SocketError errorCode) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ValidateBlockingMode(); @@ -1204,6 +1267,8 @@ public int Send(ReadOnlySpan buffer, SocketFlags socketFlags, out SocketEr public void SendFile(string? fileName) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + SendFile(fileName, ReadOnlySpan.Empty, ReadOnlySpan.Empty, TransmitFileOptions.UseDefaultWorkerThread); } @@ -1230,6 +1295,8 @@ public void SendFile(string? fileName) /// An error occurred when attempting to access the socket. public void SendFile(string? fileName, byte[]? preBuffer, byte[]? postBuffer, TransmitFileOptions flags) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + SendFile(fileName, preBuffer.AsSpan(), postBuffer.AsSpan(), flags); } @@ -1256,6 +1323,8 @@ public void SendFile(string? fileName, byte[]? preBuffer, byte[]? postBuffer, Tr /// An error occurred when attempting to access the socket. public void SendFile(string? fileName, ReadOnlySpan preBuffer, ReadOnlySpan postBuffer, TransmitFileOptions flags) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); if (!IsConnectionOriented || !Connected) @@ -1273,6 +1342,8 @@ public void SendFile(string? fileName, ReadOnlySpan preBuffer, ReadOnlySpa // Sends data to a specific end point, starting at the indicated location in the buffer. public int SendTo(byte[] buffer, int offset, int size, SocketFlags socketFlags, EndPoint remoteEP) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ValidateBufferArguments(buffer, offset, size); @@ -1309,16 +1380,22 @@ public int SendTo(byte[] buffer, int offset, int size, SocketFlags socketFlags, // Sends data to a specific end point, starting at the indicated location in the data. public int SendTo(byte[] buffer, int size, SocketFlags socketFlags, EndPoint remoteEP) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + return SendTo(buffer, 0, size, socketFlags, remoteEP); } public int SendTo(byte[] buffer, SocketFlags socketFlags, EndPoint remoteEP) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + return SendTo(buffer, 0, buffer != null ? buffer.Length : 0, socketFlags, remoteEP); } public int SendTo(byte[] buffer, EndPoint remoteEP) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + return SendTo(buffer, 0, buffer != null ? buffer.Length : 0, SocketFlags.None, remoteEP); } @@ -1333,6 +1410,8 @@ public int SendTo(byte[] buffer, EndPoint remoteEP) /// The has been closed. public int SendTo(ReadOnlySpan buffer, EndPoint remoteEP) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + return SendTo(buffer, SocketFlags.None, remoteEP); } @@ -1348,6 +1427,8 @@ public int SendTo(ReadOnlySpan buffer, EndPoint remoteEP) /// The has been closed. public int SendTo(ReadOnlySpan buffer, SocketFlags socketFlags, EndPoint remoteEP) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ArgumentNullException.ThrowIfNull(remoteEP); @@ -1389,6 +1470,8 @@ public int SendTo(ReadOnlySpan buffer, SocketFlags socketFlags, EndPoint r /// The has been closed. public int SendTo(ReadOnlySpan buffer, SocketFlags socketFlags, SocketAddress socketAddress) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ArgumentNullException.ThrowIfNull(socketAddress); @@ -1416,22 +1499,30 @@ public int SendTo(ReadOnlySpan buffer, SocketFlags socketFlags, SocketAddr // Receives data from a connected socket. public int Receive(byte[] buffer, int size, SocketFlags socketFlags) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + return Receive(buffer, 0, size, socketFlags); } public int Receive(byte[] buffer, SocketFlags socketFlags) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + return Receive(buffer, 0, buffer != null ? buffer.Length : 0, socketFlags); } public int Receive(byte[] buffer) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + return Receive(buffer, 0, buffer != null ? buffer.Length : 0, SocketFlags.None); } // Receives data from a connected socket into a specific location of the receive buffer. public int Receive(byte[] buffer, int offset, int size, SocketFlags socketFlags) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + SocketError errorCode; int bytesTransferred = Receive(buffer, offset, size, socketFlags, out errorCode); if (errorCode != SocketError.Success) @@ -1443,6 +1534,8 @@ public int Receive(byte[] buffer, int offset, int size, SocketFlags socketFlags) public int Receive(byte[] buffer, int offset, int size, SocketFlags socketFlags, out SocketError errorCode) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ValidateBufferArguments(buffer, offset, size); ValidateBlockingMode(); @@ -1475,6 +1568,8 @@ public int Receive(byte[] buffer, int offset, int size, SocketFlags socketFlags, public int Receive(Span buffer, SocketFlags socketFlags) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + int bytesTransferred = Receive(buffer, socketFlags, out SocketError errorCode); return errorCode == SocketError.Success ? bytesTransferred : @@ -1483,6 +1578,8 @@ public int Receive(Span buffer, SocketFlags socketFlags) public int Receive(Span buffer, SocketFlags socketFlags, out SocketError errorCode) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ValidateBlockingMode(); @@ -1508,11 +1605,15 @@ public int Receive(Span buffer, SocketFlags socketFlags, out SocketError e public int Receive(IList> buffers) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + return Receive(buffers, SocketFlags.None); } public int Receive(IList> buffers, SocketFlags socketFlags) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + SocketError errorCode; int bytesTransferred = Receive(buffers, socketFlags, out errorCode); if (errorCode != SocketError.Success) @@ -1524,6 +1625,8 @@ public int Receive(IList> buffers, SocketFlags socketFlags) public int Receive(IList> buffers, SocketFlags socketFlags, out SocketError errorCode) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ArgumentNullException.ThrowIfNull(buffers); @@ -1561,6 +1664,8 @@ public int Receive(IList> buffers, SocketFlags socketFlags, o // the end point. public int ReceiveMessageFrom(byte[] buffer, int offset, int size, ref SocketFlags socketFlags, ref EndPoint remoteEP, out IPPacketInformation ipPacketInformation) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ValidateBufferArguments(buffer, offset, size); ValidateReceiveFromEndpointAndState(remoteEP, nameof(remoteEP)); @@ -1638,6 +1743,8 @@ public int ReceiveMessageFrom(byte[] buffer, int offset, int size, ref SocketFla /// You must call the Bind method before performing this operation. public int ReceiveMessageFrom(Span buffer, ref SocketFlags socketFlags, ref EndPoint remoteEP, out IPPacketInformation ipPacketInformation) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ArgumentNullException.ThrowIfNull(remoteEP); @@ -1698,6 +1805,8 @@ public int ReceiveMessageFrom(Span buffer, ref SocketFlags socketFlags, re // the end point. public int ReceiveFrom(byte[] buffer, int offset, int size, SocketFlags socketFlags, ref EndPoint remoteEP) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ValidateBufferArguments(buffer, offset, size); ValidateReceiveFromEndpointAndState(remoteEP, nameof(remoteEP)); @@ -1777,16 +1886,22 @@ public int ReceiveFrom(byte[] buffer, int offset, int size, SocketFlags socketFl // Receives a datagram and stores the source end point. public int ReceiveFrom(byte[] buffer, int size, SocketFlags socketFlags, ref EndPoint remoteEP) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + return ReceiveFrom(buffer, 0, size, socketFlags, ref remoteEP); } public int ReceiveFrom(byte[] buffer, SocketFlags socketFlags, ref EndPoint remoteEP) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + return ReceiveFrom(buffer, 0, buffer != null ? buffer.Length : 0, socketFlags, ref remoteEP); } public int ReceiveFrom(byte[] buffer, ref EndPoint remoteEP) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + return ReceiveFrom(buffer, 0, buffer != null ? buffer.Length : 0, SocketFlags.None, ref remoteEP); } @@ -1816,6 +1931,8 @@ public int ReceiveFrom(Span buffer, ref EndPoint remoteEP) /// The has been closed. public int ReceiveFrom(Span buffer, SocketFlags socketFlags, ref EndPoint remoteEP) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ValidateReceiveFromEndpointAndState(remoteEP, nameof(remoteEP)); @@ -1897,6 +2014,8 @@ public int ReceiveFrom(Span buffer, SocketFlags socketFlags, ref EndPoint /// The has been closed. public int ReceiveFrom(Span buffer, SocketFlags socketFlags, SocketAddress receivedAddress) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ArgumentNullException.ThrowIfNull(receivedAddress, nameof(receivedAddress)); @@ -2223,6 +2342,8 @@ public void SetIPProtectionLevel(IPProtectionLevel level) /// The has been closed. public bool Poll(int microSeconds, SelectMode mode) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); bool status; @@ -2268,6 +2389,8 @@ public bool Poll(TimeSpan timeout, SelectMode mode) => /// One or more sockets was disposed. public static void Select(IList? checkRead, IList? checkWrite, IList? checkError, int microSeconds) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + if ((checkRead == null || checkRead.Count == 0) && (checkWrite == null || checkWrite.Count == 0) && (checkError == null || checkError.Count == 0)) @@ -2366,6 +2489,8 @@ public void Disconnect(bool reuseSocket) public IAsyncResult BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback? callback, object? state) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ValidateBufferArguments(buffer, offset, size); @@ -2374,6 +2499,8 @@ public IAsyncResult BeginSend(byte[] buffer, int offset, int size, SocketFlags s public IAsyncResult? BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, out SocketError errorCode, AsyncCallback? callback, object? state) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ValidateBufferArguments(buffer, offset, size); @@ -2390,6 +2517,8 @@ public IAsyncResult BeginSend(byte[] buffer, int offset, int size, SocketFlags s public IAsyncResult BeginSend(IList> buffers, SocketFlags socketFlags, AsyncCallback? callback, object? state) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); return TaskToAsyncResult.Begin(SendAsync(buffers, socketFlags), callback, state); @@ -2397,6 +2526,8 @@ public IAsyncResult BeginSend(IList> buffers, SocketFlags soc public IAsyncResult? BeginSend(IList> buffers, SocketFlags socketFlags, out SocketError errorCode, AsyncCallback? callback, object? state) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); Task t = SendAsync(buffers, socketFlags); @@ -2417,11 +2548,15 @@ public int EndSend(IAsyncResult asyncResult, out SocketError errorCode) => public IAsyncResult BeginSendFile(string? fileName, AsyncCallback? callback, object? state) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + return BeginSendFile(fileName, null, null, TransmitFileOptions.UseDefaultWorkerThread, callback, state); } public IAsyncResult BeginSendFile(string? fileName, byte[]? preBuffer, byte[]? postBuffer, TransmitFileOptions flags, AsyncCallback? callback, object? state) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); if (!Connected) @@ -2438,6 +2573,8 @@ public IAsyncResult BeginSendFile(string? fileName, byte[]? preBuffer, byte[]? p public IAsyncResult BeginSendTo(byte[] buffer, int offset, int size, SocketFlags socketFlags, EndPoint remoteEP, AsyncCallback? callback, object? state) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ValidateBufferArguments(buffer, offset, size); ArgumentNullException.ThrowIfNull(remoteEP); @@ -2450,6 +2587,8 @@ public IAsyncResult BeginSendTo(byte[] buffer, int offset, int size, SocketFlags public IAsyncResult BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback? callback, object? state) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ValidateBufferArguments(buffer, offset, size); return TaskToAsyncResult.Begin(ReceiveAsync(new ArraySegment(buffer, offset, size), socketFlags, fromNetworkStream: false, default).AsTask(), callback, state); @@ -2457,6 +2596,8 @@ public IAsyncResult BeginReceive(byte[] buffer, int offset, int size, SocketFlag public IAsyncResult? BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, out SocketError errorCode, AsyncCallback? callback, object? state) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ValidateBufferArguments(buffer, offset, size); Task t = ReceiveAsync(new ArraySegment(buffer, offset, size), socketFlags, fromNetworkStream: false, default).AsTask(); @@ -2473,12 +2614,16 @@ public IAsyncResult BeginReceive(byte[] buffer, int offset, int size, SocketFlag public IAsyncResult BeginReceive(IList> buffers, SocketFlags socketFlags, AsyncCallback? callback, object? state) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); return TaskToAsyncResult.Begin(ReceiveAsync(buffers, socketFlags), callback, state); } public IAsyncResult? BeginReceive(IList> buffers, SocketFlags socketFlags, out SocketError errorCode, AsyncCallback? callback, object? state) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); Task t = ReceiveAsync(buffers, socketFlags); @@ -2499,6 +2644,8 @@ public int EndReceive(IAsyncResult asyncResult, out SocketError errorCode) => private static int EndSendReceive(IAsyncResult asyncResult, out SocketError errorCode) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + Task ti = TaskToAsyncResult.Unwrap(asyncResult); ((Task)ti).ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing).GetAwaiter().GetResult(); @@ -2515,6 +2662,8 @@ private static int EndSendReceive(IAsyncResult asyncResult, out SocketError erro public IAsyncResult BeginReceiveMessageFrom(byte[] buffer, int offset, int size, SocketFlags socketFlags, ref EndPoint remoteEP, AsyncCallback? callback, object? state) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"size:{size}"); ThrowIfDisposed(); @@ -2536,6 +2685,8 @@ public IAsyncResult BeginReceiveMessageFrom(byte[] buffer, int offset, int size, public int EndReceiveMessageFrom(IAsyncResult asyncResult, ref SocketFlags socketFlags, ref EndPoint endPoint, out IPPacketInformation ipPacketInformation) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ArgumentNullException.ThrowIfNull(endPoint); if (!CanTryAddressFamily(endPoint.AddressFamily)) { @@ -2554,6 +2705,8 @@ public int EndReceiveMessageFrom(IAsyncResult asyncResult, ref SocketFlags socke public IAsyncResult BeginReceiveFrom(byte[] buffer, int offset, int size, SocketFlags socketFlags, ref EndPoint remoteEP, AsyncCallback? callback, object? state) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ValidateBufferArguments(buffer, offset, size); ValidateReceiveFromEndpointAndState(remoteEP, nameof(remoteEP)); @@ -2572,6 +2725,8 @@ public IAsyncResult BeginReceiveFrom(byte[] buffer, int offset, int size, Socket public int EndReceiveFrom(IAsyncResult asyncResult, ref EndPoint endPoint) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ArgumentNullException.ThrowIfNull(endPoint); if (!CanTryAddressFamily(endPoint.AddressFamily)) { @@ -2632,6 +2787,8 @@ public IAsyncResult BeginAccept(Socket? acceptSocket, int receiveSize, AsyncCall public Socket EndAccept(out byte[] buffer, IAsyncResult asyncResult) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + Socket socket = EndAccept(out byte[] innerBuffer, out int bytesTransferred, asyncResult); buffer = new byte[bytesTransferred]; Buffer.BlockCopy(innerBuffer, 0, buffer, 0, bytesTransferred); @@ -2640,6 +2797,8 @@ public Socket EndAccept(out byte[] buffer, IAsyncResult asyncResult) public Socket EndAccept(out byte[] buffer, out int bytesTransferred, IAsyncResult asyncResult) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + Socket s; (s, buffer, bytesTransferred) = TaskToAsyncResult.End<(Socket, byte[], int)>(asyncResult); return s; @@ -3403,6 +3562,8 @@ internal void SetReceivingPacketInformation() { if (!_receivingPacketInformation) { + if (OperatingSystem.IsWasi()) return; // WASI is always set to receive PacketInformation + // DualMode: When bound to IPv6Any you must enable both socket options. // When bound to an IPv4 mapped IPv6 address you must enable the IPv4 socket option. IPEndPoint? ipEndPoint = _rightEndPoint as IPEndPoint; @@ -3430,6 +3591,16 @@ internal void SetReceivingPacketInformation() internal unsafe void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, int optionValue, bool silent) { + // WASI is always set to receive PacketInformation + if (OperatingSystem.IsWasi() && optionName == SocketOptionName.PacketInformation) + { + if (optionValue == 0) + { + UpdateStatusAfterSocketOptionErrorAndThrowException(SocketError.ProtocolOption); + } + return; + } + if (silent && (Disposed || _handle.IsInvalid)) { if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, "skipping the call"); @@ -3498,6 +3669,8 @@ private void SetIPv6MulticastOption(SocketOptionName optionName, IPv6MulticastOp private void SetLingerOption(LingerOption lref) { + if (OperatingSystem.IsWasi() && lref.Enabled) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + SocketError errorCode = SocketPal.SetLingerOption(_handle, lref); if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"SetLingerOption returns errorCode:{errorCode}"); @@ -3511,6 +3684,8 @@ private void SetLingerOption(LingerOption lref) private LingerOption? GetLingerOpt() { + if (OperatingSystem.IsWasi()) return new LingerOption(false, 0); + LingerOption? lingerOption; SocketError errorCode = SocketPal.GetLingerOption(_handle, out lingerOption); diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs index d5613dc91f481a..8463c5142b573c 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs @@ -30,7 +30,7 @@ namespace System.Net.Sockets // See comments on OperationQueue below for more details of how the queue coordination works. - internal sealed class SocketAsyncContext + internal sealed partial class SocketAsyncContext { // Cached operation instances for operations commonly repeated on the same socket instance, // e.g. async accepts, sends/receives with single and multiple buffers. More can be @@ -1256,12 +1256,12 @@ public void Trace(SocketAsyncContext context, string message, [CallerMemberName] } } - private readonly SafeSocketHandle _socket; + internal readonly SafeSocketHandle _socket; private OperationQueue _receiveQueue; private OperationQueue _sendQueue; private SocketAsyncEngine? _asyncEngine; private bool IsRegistered => _asyncEngine != null; - private bool _isHandleNonBlocking; + private bool _isHandleNonBlocking = OperatingSystem.IsWasi(); // WASI sockets are always non-blocking, because we don't have another thread which could be blocked private readonly object _registerLock = new object(); @@ -1330,13 +1330,18 @@ public bool StopAndAbort() // We don't need to synchronize with Register. // This method is called when the handle gets released. // The Register method will throw ODE when it tries to use the handle at this point. - _asyncEngine?.UnregisterSocket(_socket.DangerousGetHandle()); + _asyncEngine?.UnregisterSocket(_socket.DangerousGetHandle(), this); return aborted; } public void SetHandleNonBlocking() { + if (OperatingSystem.IsWasi()) + { + // WASI sockets are always non-blocking, because in ST we don't have another thread which could be blocked + return; + } // // Our sockets may start as blocking, and later transition to non-blocking, either because the user // explicitly requested non-blocking mode, or because we need non-blocking mode to support async @@ -1362,6 +1367,7 @@ public void SetHandleNonBlocking() private void PerformSyncOperation(ref OperationQueue queue, TOperation operation, int timeout, int observedSequenceNumber) where TOperation : AsyncOperation { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); Debug.Assert(timeout == -1 || timeout > 0, $"Unexpected timeout: {timeout}"); using (var e = new ManualResetEventSlim(false, 0)) @@ -1498,6 +1504,8 @@ public SocketError AcceptAsync(Memory socketAddress, out int socketAddress public SocketError Connect(Memory socketAddress) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); + Debug.Assert(socketAddress.Length > 0, $"Unexpected socketAddressLen: {socketAddress.Length}"); // Connect is different than the usual "readiness" pattern of other operations. // We need to call TryStartConnect to initiate the connect with the OS, @@ -1590,6 +1598,8 @@ public SocketError ReceiveAsync(Memory buffer, SocketFlags flags, out int public unsafe SocketError ReceiveFrom(Memory buffer, ref SocketFlags flags, Memory socketAddress, out int socketAddressLen, int timeout, out int bytesReceived) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); + Debug.Assert(timeout == -1 || timeout > 0, $"Unexpected timeout: {timeout}"); SocketFlags receivedFlags; @@ -1621,6 +1631,8 @@ public unsafe SocketError ReceiveFrom(Memory buffer, ref SocketFlags flags public unsafe SocketError ReceiveFrom(Span buffer, ref SocketFlags flags, Memory socketAddress, out int socketAddressLen, int timeout, out int bytesReceived) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); + SocketFlags receivedFlags; SocketError errorCode; int observedSequenceNumber; @@ -1731,6 +1743,8 @@ public SocketError ReceiveAsync(IList> buffers, SocketFlags f public unsafe SocketError ReceiveFrom(IList> buffers, ref SocketFlags flags, Memory socketAddress, out int socketAddressLen, int timeout, out int bytesReceived) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); + Debug.Assert(timeout == -1 || timeout > 0, $"Unexpected timeout: {timeout}"); SocketFlags receivedFlags; @@ -1798,6 +1812,8 @@ public SocketError ReceiveFromAsync(IList> buffers, SocketFla public SocketError ReceiveMessageFrom( Memory buffer, ref SocketFlags flags, Memory socketAddress, out int socketAddressLen, bool isIPv4, bool isIPv6, int timeout, out IPPacketInformation ipPacketInformation, out int bytesReceived) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); + Debug.Assert(timeout == -1 || timeout > 0, $"Unexpected timeout: {timeout}"); SocketFlags receivedFlags; @@ -1833,6 +1849,8 @@ public SocketError ReceiveMessageFrom( public unsafe SocketError ReceiveMessageFrom( Span buffer, ref SocketFlags flags, Memory socketAddress, out int socketAddressLen, bool isIPv4, bool isIPv6, int timeout, out IPPacketInformation ipPacketInformation, out int bytesReceived) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); + Debug.Assert(timeout == -1 || timeout > 0, $"Unexpected timeout: {timeout}"); SocketFlags receivedFlags; @@ -1923,6 +1941,8 @@ public SocketError SendAsync(Memory buffer, int offset, int count, SocketF public SocketError SendTo(byte[] buffer, int offset, int count, SocketFlags flags, Memory socketAddress, int timeout, out int bytesSent) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); + Debug.Assert(timeout == -1 || timeout > 0, $"Unexpected timeout: {timeout}"); bytesSent = 0; @@ -1953,6 +1973,8 @@ public SocketError SendTo(byte[] buffer, int offset, int count, SocketFlags flag public unsafe SocketError SendTo(ReadOnlySpan buffer, SocketFlags flags, Memory socketAddress, int timeout, out int bytesSent) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); + Debug.Assert(timeout == -1 || timeout > 0, $"Unexpected timeout: {timeout}"); bytesSent = 0; @@ -2030,6 +2052,8 @@ public SocketError SendAsync(IList> buffers, SocketFlags flag public SocketError SendTo(IList> buffers, SocketFlags flags, Memory socketAddress, int timeout, out int bytesSent) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); + Debug.Assert(timeout == -1 || timeout > 0, $"Unexpected timeout: {timeout}"); bytesSent = 0; @@ -2098,6 +2122,8 @@ public SocketError SendToAsync(IList> buffers, SocketFlags fl public SocketError SendFile(SafeFileHandle fileHandle, long offset, long count, int timeout, out long bytesSent) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); + Debug.Assert(timeout == -1 || timeout > 0, $"Unexpected timeout: {timeout}"); bytesSent = 0; diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Wasi.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Wasi.cs new file mode 100644 index 00000000000000..fbe89b6fa64c00 --- /dev/null +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Wasi.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using Microsoft.Win32.SafeHandles; +using System.Runtime.Versioning; + +namespace System.Net.Sockets +{ + internal sealed partial class SocketAsyncContext + { + public CancellationTokenSource unregisterPollHook = new(); + } +} diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs index 43364203118470..7405e579042232 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs @@ -138,7 +138,7 @@ private bool TryRegisterCore(IntPtr socketHandle, SocketAsyncContext context, ou return false; } - public void UnregisterSocket(IntPtr socketHandle) + public void UnregisterSocket(IntPtr socketHandle, SocketAsyncContext __) { _handleToContextMap.TryRemove(socketHandle, out _); } diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Wasi.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Wasi.cs new file mode 100644 index 00000000000000..3b69902260d812 --- /dev/null +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Wasi.cs @@ -0,0 +1,156 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Net.Sockets; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using static Interop; +using static Interop.Sys; + +namespace System.Net.Sockets +{ + internal sealed unsafe class SocketAsyncEngine + { + internal const bool InlineSocketCompletionsEnabled = true; + private static readonly SocketAsyncEngine s_engine = new SocketAsyncEngine(); + + public static bool TryRegisterSocket(IntPtr socketHandle, SocketAsyncContext context, out SocketAsyncEngine? engine, out Interop.Error error) + { + engine = s_engine; + + nint entryPtr = default; + error = Interop.Sys.GetWasiSocketDescriptor(socketHandle, &entryPtr); + if (error != Interop.Error.SUCCESS) + { + return false; + } + + RegisterWasiPollHook(context, BeforePollHook, HandleSocketEvent, context.unregisterPollHook.Token); + + return true; + } + +#pragma warning disable CA1822 + public void UnregisterSocket(IntPtr _, SocketAsyncContext context) +#pragma warning restore CA1822 + { + context.unregisterPollHook.Cancel(); + } + + // this method is invading private implementation details of wasi-libc + // we could get rid of it when https://github.com/WebAssembly/wasi-libc/issues/542 is resolved + // or after WASIp3 promises are implemented, whatever comes first + public static IList BeforePollHook(object? state) + { + var context = (SocketAsyncContext)state!; + if (context._socket.IsClosed) + { + return []; + } + + List pollableHandles = new(); + // fail fast if the handle is not found in the descriptor table + // probably because the socket was closed and the entry was removed, without unregistering the poll hook + nint entryPtr = default; + IntPtr socketHandle = context._socket.DangerousGetHandle(); + var error = Interop.Sys.GetWasiSocketDescriptor(socketHandle, &entryPtr); + if (error != Interop.Error.SUCCESS) + { + Environment.FailFast("Can't resolve libc descriptor for socket handle " + socketHandle); + } + + var entry = (descriptor_table_entry_t*)entryPtr; + switch (entry->tag) + { + case descriptor_table_entry_tag.DESCRIPTOR_TABLE_ENTRY_TCP_SOCKET: + { + tcp_socket_t* socket = &(entry->entry.tcp_socket); + switch (socket->state.tag) + { + case tcp_socket_state_tag.TCP_SOCKET_STATE_CONNECTING: + case tcp_socket_state_tag.TCP_SOCKET_STATE_LISTENING: + pollableHandles.Add(socket->socket_pollable.handle); + break; + case tcp_socket_state_tag.TCP_SOCKET_STATE_CONNECTED: + pollableHandles.Add(socket->state.state.connected.input_pollable.handle); + pollableHandles.Add(socket->state.state.connected.output_pollable.handle); + break; + case tcp_socket_state_tag.TCP_SOCKET_STATE_CONNECT_FAILED: + context.HandleEventsInline(Sys.SocketEvents.Error); + break; + case tcp_socket_state_tag.TCP_SOCKET_STATE_UNBOUND: + case tcp_socket_state_tag.TCP_SOCKET_STATE_BOUND: + break; + default: + throw new NotImplementedException("TCP:" + socket->state.tag); + } + break; + } + case descriptor_table_entry_tag.DESCRIPTOR_TABLE_ENTRY_UDP_SOCKET: + { + udp_socket_t* socket = &(entry->entry.udp_socket); + switch (socket->state.tag) + { + case udp_socket_state_tag.UDP_SOCKET_STATE_UNBOUND: + case udp_socket_state_tag.UDP_SOCKET_STATE_BOUND_NOSTREAMS: + // TODO ? pollableHandles.Add(socket->socket_pollable.handle); + context.HandleEventsInline(Sys.SocketEvents.Read | Sys.SocketEvents.Write); + break; + case udp_socket_state_tag.UDP_SOCKET_STATE_BOUND_STREAMING: + case udp_socket_state_tag.UDP_SOCKET_STATE_CONNECTED: + { + udp_socket_streams_t* streams; + if (socket->state.tag == udp_socket_state_tag.UDP_SOCKET_STATE_BOUND_STREAMING) + { + streams = &(socket->state.state.bound_streaming.streams); + } + else + { + streams = &(socket->state.state.connected.streams); + } + pollableHandles.Add(streams->incoming_pollable.handle); + pollableHandles.Add(streams->outgoing_pollable.handle); + break; + } + + default: + throw new NotImplementedException("UDP" + socket->state.tag); + } + break; + } + default: + throw new NotImplementedException("TYPE" + entry->tag); + } + return pollableHandles; + } + + public static void HandleSocketEvent(object? state) + { + SocketAsyncContext ctx = (SocketAsyncContext)state!; + try + { + using (ExecutionContext.SuppressFlow()) + { + ctx.HandleEventsInline(Sys.SocketEvents.Write | Sys.SocketEvents.Read); + } + } + catch (Exception e) + { + Environment.FailFast("Exception thrown from SocketAsyncEngine event loop: " + e.ToString(), e); + } + } + + private static void RegisterWasiPollHook(object? state, Func> beforePollHook, Action onResolveCallback, CancellationToken cancellationToken) + { + CallRegisterWasiPollHook((Thread)null!, state, beforePollHook, onResolveCallback, cancellationToken); + + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "RegisterWasiPollHook")] + static extern void CallRegisterWasiPollHook(Thread t, object? state, Func> beforePollHook, Action onResolveCallback, CancellationToken cancellationToken); + } + } +} diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs index 579c2dea66160c..eed14c336888e2 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs @@ -71,7 +71,8 @@ public static unsafe SocketError CreateSocket(AddressFamily addressFamily, Socke // The socket was created successfully; enable IPV6_V6ONLY by default for normal AF_INET6 sockets. // This fails on raw sockets so we just let them be in default state. - if (addressFamily == AddressFamily.InterNetworkV6 && socketType != SocketType.Raw) + // WASI is always IPv6-only when IPv6 is enabled. + if (!OperatingSystem.IsWasi() && addressFamily == AddressFamily.InterNetworkV6 && socketType != SocketType.Raw) { int on = 1; error = Interop.Sys.SetSockOpt(fd, SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, (byte*)&on, sizeof(int)); @@ -289,6 +290,11 @@ private static unsafe int SysSend(SafeSocketHandle socket, SocketFlags flags, IL int startIndex = bufferIndex, startOffset = offset; int maxBuffers = buffers.Count - startIndex; + if (OperatingSystem.IsWasi()) + { + // WASI doesn't have iovecs and recvmsg in preview2 + maxBuffers = Math.Max(maxBuffers, 1); + } bool allocOnStack = maxBuffers <= IovStackThreshold; Span handles = allocOnStack ? stackalloc GCHandle[IovStackThreshold] : new GCHandle[maxBuffers]; Span iovecs = allocOnStack ? stackalloc Interop.Sys.IOVector[IovStackThreshold] : new Interop.Sys.IOVector[maxBuffers]; @@ -376,6 +382,11 @@ private static unsafe int SysReceive(SafeSocketHandle socket, SocketFlags flags, Debug.Assert(socket.IsSocket); int maxBuffers = buffers.Count; + if (OperatingSystem.IsWasi()) + { + // WASI doesn't have iovecs and recvmsg in preview2 + maxBuffers = Math.Max(maxBuffers, 1); + } bool allocOnStack = maxBuffers <= IovStackThreshold; // When there are many buffers, reduce the number of pinned buffers based on available bytes. @@ -532,6 +543,12 @@ private static unsafe int SysReceiveMessageFrom( Debug.Assert(socket.IsSocket); int buffersCount = buffers.Count; + if (OperatingSystem.IsWasi()) + { + // WASI doesn't have iovecs and sendmsg in preview2 + buffersCount = Math.Max(buffersCount, 1); + } + bool allocOnStack = buffersCount <= IovStackThreshold; Span handles = allocOnStack ? stackalloc GCHandle[IovStackThreshold] : new GCHandle[buffersCount]; Span iovecs = allocOnStack ? stackalloc Interop.Sys.IOVector[IovStackThreshold] : new Interop.Sys.IOVector[buffersCount]; @@ -1056,6 +1073,8 @@ public static bool TryCompleteSendFile(SafeSocketHandle socket, SafeFileHandle h public static SocketError SetBlocking(SafeSocketHandle handle, bool shouldBlock, out bool willBlock) { + if(OperatingSystem.IsWasi() && shouldBlock) throw new PlatformNotSupportedException(); + handle.IsNonBlocking = !shouldBlock; willBlock = shouldBlock; return SocketError.Success; diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Wasi.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Wasi.cs new file mode 100644 index 00000000000000..3fd6dee783e01c --- /dev/null +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Wasi.cs @@ -0,0 +1,227 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Win32.SafeHandles; + +// types here are clone of private implementation details of wasi-libc +// we could get rid of it when https://github.com/WebAssembly/wasi-libc/issues/542 is resolved +// or after WASIp3 promises are implemented, whatever comes first + +namespace System.Net.Sockets +{ + [StructLayout(LayoutKind.Sequential)] + internal struct tcp_own_tcp_socket_t + { + public int handle; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct udp_own_udp_socket_t + { + public int handle; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct udp_own_incoming_datagram_stream_t + { + public int handle; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct udp_own_outgoing_datagram_stream_t + { + public int handle; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct streams_own_input_stream_t + { + public int handle; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct poll_own_pollable_t + { + public int handle; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct streams_own_output_stream_t + { + public int handle; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct tcp_socket_state_unbound_t + { + public int dummy; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct tcp_socket_state_bound_t + { + public int dummy; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct tcp_socket_state_connecting_t + { + public int dummy; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct tcp_socket_state_listening_t + { + public int dummy; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct tcp_socket_state_connected_t + { + public streams_own_input_stream_t input; + public poll_own_pollable_t input_pollable; + public streams_own_output_stream_t output; + public poll_own_pollable_t output_pollable; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct tcp_socket_state_connect_failed_t + { + public byte error_code; + } + + internal enum tcp_socket_state_tag + { + TCP_SOCKET_STATE_UNBOUND, + TCP_SOCKET_STATE_BOUND, + TCP_SOCKET_STATE_CONNECTING, + TCP_SOCKET_STATE_CONNECTED, + TCP_SOCKET_STATE_CONNECT_FAILED, + TCP_SOCKET_STATE_LISTENING, + } + + [StructLayout(LayoutKind.Explicit)] + internal struct tcp_socket_state_union + { + [FieldOffset(0)] public tcp_socket_state_unbound_t unbound; + [FieldOffset(0)] public tcp_socket_state_bound_t bound; + [FieldOffset(0)] public tcp_socket_state_connecting_t connecting; + [FieldOffset(0)] public tcp_socket_state_connected_t connected; + [FieldOffset(0)] public tcp_socket_state_connect_failed_t connect_failed; + [FieldOffset(0)] public tcp_socket_state_listening_t listening; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct tcp_socket_state_t + { + public tcp_socket_state_tag tag; + public tcp_socket_state_union state; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct tcp_socket_t + { + public tcp_own_tcp_socket_t socket; + public poll_own_pollable_t socket_pollable; + public bool blocking; + public bool fake_nodelay; + public bool fake_reuseaddr; + public byte family; + public tcp_socket_state_t state; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct udp_socket_streams_t + { + public udp_own_incoming_datagram_stream_t incoming; + public poll_own_pollable_t incoming_pollable; + public udp_own_outgoing_datagram_stream_t outgoing; + public poll_own_pollable_t outgoing_pollable; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct udp_socket_state_unbound_t + { + public int dummy; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct udp_socket_state_bound_nostreams_t + { + public int dummy; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct udp_socket_state_bound_streaming_t + { + public udp_socket_streams_t streams; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct udp_socket_state_connected_t + { + public udp_socket_streams_t streams; + } + + internal enum udp_socket_state_tag + { + UDP_SOCKET_STATE_UNBOUND, + UDP_SOCKET_STATE_BOUND_NOSTREAMS, + UDP_SOCKET_STATE_BOUND_STREAMING, + UDP_SOCKET_STATE_CONNECTED, + } + + [StructLayout(LayoutKind.Explicit)] + internal struct udp_socket_state_union + { + [FieldOffset(0)] public udp_socket_state_unbound_t unbound; + [FieldOffset(0)] public udp_socket_state_bound_nostreams_t bound_nostreams; + [FieldOffset(0)] public udp_socket_state_bound_streaming_t bound_streaming; + [FieldOffset(0)] public udp_socket_state_connected_t connected; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct udp_socket_state_t + { + public udp_socket_state_tag tag; + public udp_socket_state_union state; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct udp_socket_t + { + public udp_own_udp_socket_t socket; + public poll_own_pollable_t socket_pollable; + public bool blocking; + public byte family; + public udp_socket_state_t state; + } + + internal enum descriptor_table_entry_tag + { + DESCRIPTOR_TABLE_ENTRY_TCP_SOCKET, + DESCRIPTOR_TABLE_ENTRY_UDP_SOCKET, + } + + [StructLayout(LayoutKind.Explicit)] + internal struct descriptor_table_entry_union + { + [FieldOffset(0)] public tcp_socket_t tcp_socket; + [FieldOffset(0)] public udp_socket_t udp_socket; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct descriptor_table_entry_t + { + public descriptor_table_entry_tag tag; + public descriptor_table_entry_union entry; + } +} diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/TCPClient.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/TCPClient.cs index 54998d3e02e93e..41d54eba71d1c2 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/TCPClient.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/TCPClient.cs @@ -123,6 +123,8 @@ public bool ExclusiveAddressUse // Connects the Client to the specified port on the specified host. public void Connect(string hostname, int port) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ArgumentNullException.ThrowIfNull(hostname); @@ -140,6 +142,8 @@ public void Connect(string hostname, int port) // Connects the Client to the specified port on the specified host. public void Connect(IPAddress address, int port) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ArgumentNullException.ThrowIfNull(address); @@ -155,6 +159,8 @@ public void Connect(IPAddress address, int port) // Connect the Client to the specified end point. public void Connect(IPEndPoint remoteEP) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ArgumentNullException.ThrowIfNull(remoteEP); @@ -166,6 +172,10 @@ public void Connect(IPEndPoint remoteEP) public void Connect(IPAddress[] ipAddresses, int port) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + + ThrowIfDisposed(); + Client.Connect(ipAddresses, port); _family = Client.AddressFamily; _active = true; @@ -229,6 +239,8 @@ public IAsyncResult BeginConnect(IPAddress[] addresses, int port, AsyncCallback? public void EndConnect(IAsyncResult asyncResult) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + _clientSocket.EndConnect(asyncResult); _active = true; @@ -307,23 +319,46 @@ public int SendBufferSize // Gets or sets the receive time out value of the connection in milliseconds. public int ReceiveTimeout { - get { return (int)Client.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout)!; } - set { Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, value); } + get + { + if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // https://github.com/dotnet/runtime/issues/108151 + return (int)Client.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout)!; + } + set + { + if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // https://github.com/dotnet/runtime/issues/108151 + Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, value); + } } // Gets or sets the send time out value of the connection in milliseconds. public int SendTimeout { - get { return (int)Client.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout)!; } - set { Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, value); } + get + { + if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // https://github.com/dotnet/runtime/issues/108151 + return (int)Client.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout)!; + } + set + { + if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // https://github.com/dotnet/runtime/issues/108151 + Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, value); + } } // Gets or sets the value of the connection's linger option. [DisallowNull] public LingerOption? LingerState { - get { return Client.LingerState; } - set { Client.LingerState = value!; } + get + { + return Client.LingerState; + } + set + { + if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // https://github.com/dotnet/runtime/issues/108151 + Client.LingerState = value!; + } } // Enables or disables delay when send or receive buffers are full. diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/TCPListener.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/TCPListener.cs index 06f80e97a52f5f..647bed93739da8 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/TCPListener.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/TCPListener.cs @@ -191,6 +191,8 @@ public Socket AcceptSocket() throw new InvalidOperationException(SR.net_stopped); } + if (OperatingSystem.IsWasi() && _serverSocket!.Blocking) throw new PlatformNotSupportedException("Only use with Socket.Blocking=false on WASI"); + return _serverSocket!.Accept(); } @@ -201,6 +203,8 @@ public TcpClient AcceptTcpClient() throw new InvalidOperationException(SR.net_stopped); } + if (OperatingSystem.IsWasi() && _serverSocket!.Blocking) throw new PlatformNotSupportedException("Only use with Socket.Blocking=false on WASI"); + Socket acceptedSocket = _serverSocket!.Accept(); return new TcpClient(acceptedSocket); } @@ -252,7 +256,7 @@ public static TcpListener Create(int port) { // If OS supports IPv6 use dual mode so both address families work. listener = new TcpListener(IPAddress.IPv6Any, port); - listener.Server.DualMode = true; + if (!OperatingSystem.IsWasi()) listener.Server.DualMode = true; } else { @@ -286,6 +290,8 @@ private void CreateNewSocketIfNeeded() private TResult EndAcceptCore(IAsyncResult asyncResult) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + try { return TaskToAsyncResult.End(asyncResult); diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UDPClient.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UDPClient.cs index 8d628720d7e43a..60bc380ec195a1 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UDPClient.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UDPClient.cs @@ -284,6 +284,8 @@ public IAsyncResult BeginSend(byte[] datagram, int bytes, string? hostname, int public IAsyncResult BeginSend(byte[] datagram, int bytes, IPEndPoint? endPoint, AsyncCallback? requestCallback, object? state) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ValidateDatagram(datagram, bytes, endPoint); if (endPoint is null) @@ -299,6 +301,8 @@ public IAsyncResult BeginSend(byte[] datagram, int bytes, IPEndPoint? endPoint, public int EndSend(IAsyncResult asyncResult) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); return _active ? @@ -354,6 +358,8 @@ private void ValidateDatagram(byte[] datagram, int bytes, IPEndPoint? endPoint) public IAsyncResult BeginReceive(AsyncCallback? requestCallback, object? state) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); // Due to the nature of the ReceiveFrom() call and the ref parameter convention, @@ -368,6 +374,8 @@ public IAsyncResult BeginReceive(AsyncCallback? requestCallback, object? state) public byte[] EndReceive(IAsyncResult asyncResult, ref IPEndPoint? remoteEP) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); EndPoint tempRemoteEP = _family == AddressFamily.InterNetwork ? @@ -677,6 +685,8 @@ public void Close() public void Connect(string hostname, int port) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ArgumentNullException.ThrowIfNull(hostname); @@ -790,6 +800,8 @@ public void Connect(string hostname, int port) public void Connect(IPAddress addr, int port) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ArgumentNullException.ThrowIfNull(addr); @@ -805,6 +817,8 @@ public void Connect(IPAddress addr, int port) public void Connect(IPEndPoint endPoint) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ArgumentNullException.ThrowIfNull(endPoint); @@ -816,6 +830,8 @@ public void Connect(IPEndPoint endPoint) public byte[] Receive([NotNull] ref IPEndPoint? remoteEP) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); // this is a fix due to the nature of the ReceiveFrom() call and the @@ -837,6 +853,8 @@ public byte[] Receive([NotNull] ref IPEndPoint? remoteEP) // Sends a UDP datagram to the host at the remote end point. public int Send(byte[] dgram, int bytes, IPEndPoint? endPoint) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ArgumentNullException.ThrowIfNull(dgram); @@ -871,6 +889,8 @@ public int Send(byte[] dgram, int bytes, IPEndPoint? endPoint) /// An error occurred when accessing the socket. public int Send(ReadOnlySpan datagram, IPEndPoint? endPoint) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); if (_active && endPoint != null) @@ -913,6 +933,8 @@ public int Send(ReadOnlySpan datagram, IPEndPoint? endPoint) // Sends a UDP datagram to a remote host. public int Send(byte[] dgram, int bytes) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); ArgumentNullException.ThrowIfNull(dgram); @@ -937,6 +959,8 @@ public int Send(byte[] dgram, int bytes) /// An error occurred when accessing the socket. public int Send(ReadOnlySpan datagram) { + if (!Socket.OSSupportsThreads) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + ThrowIfDisposed(); if (!_active) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UnixDomainSocketEndPoint.Wasi.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UnixDomainSocketEndPoint.Wasi.cs new file mode 100644 index 00000000000000..e2bff511837a3b --- /dev/null +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/UnixDomainSocketEndPoint.Wasi.cs @@ -0,0 +1,73 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Text; + +namespace System.Net.Sockets +{ +#pragma warning disable CA1822 + public sealed partial class UnixDomainSocketEndPoint : System.Net.EndPoint + { + public UnixDomainSocketEndPoint(string path) + { + throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + } + + internal UnixDomainSocketEndPoint(ReadOnlySpan socketAddress) + { + throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + } + + internal string? BoundFileName + { + get + { + throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + } + } + + public override System.Net.Sockets.AddressFamily AddressFamily + { + get + { + throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + } + } + + public override System.Net.EndPoint Create(System.Net.SocketAddress socketAddress) + { + throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + } + + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) + { + throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + } + + public override int GetHashCode() + { + throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + } + + public override System.Net.SocketAddress Serialize() + { + throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + } + + public override string ToString() + { + throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + } + + internal UnixDomainSocketEndPoint CreateBoundEndPoint() + { + throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + } + + internal UnixDomainSocketEndPoint CreateUnboundEndPoint() + { + throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 + } + } +} diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Accept.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Accept.cs index 819c3bc111027d..d71a8737da4606 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Accept.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Accept.cs @@ -144,7 +144,7 @@ public async Task Accept_ConcurrentAcceptsAfterConnects_Success(int numberAccept } [OuterLoop] - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public async Task Accept_WithTargetSocket_Success() { if (!SupportsAcceptIntoExistingSocket) @@ -167,7 +167,7 @@ public async Task Accept_WithTargetSocket_Success() } [OuterLoop] - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [InlineData(false)] [InlineData(true)] public async Task Accept_WithTargetSocket_ReuseAfterDisconnect_Success(bool reuseSocket) @@ -239,7 +239,7 @@ public async Task Accept_WithAlreadyBoundTargetSocket_Fails() } [OuterLoop] - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public async Task Accept_WithInUseTargetSocket_Fails() { if (!SupportsAcceptIntoExistingSocket) @@ -299,6 +299,7 @@ public async Task AcceptAsync_MultipleAcceptsThenDispose_AcceptsThrowAfterDispos [Theory] [MemberData(nameof(AcceptGetsCanceledByDispose_Data))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] [ActiveIssue("https://github.com/dotnet/runtime/issues/73536", TestPlatforms.iOS | TestPlatforms.tvOS)] public async Task AcceptGetsCanceledByDispose(IPAddress loopback, bool owning) { @@ -352,7 +353,7 @@ await RetryHelper.ExecuteAsync(async () => }, maxAttempts: 10, retryWhen: e => e is XunitException); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public async Task AcceptReceive_Success() { if (!SupportsAcceptReceive) @@ -377,16 +378,19 @@ public async Task AcceptReceive_Success() } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class AcceptSync : Accept { public AcceptSync(ITestOutputHelper output) : base(output) {} } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class AcceptSyncForceNonBlocking : Accept { public AcceptSyncForceNonBlocking(ITestOutputHelper output) : base(output) {} } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class AcceptApm : Accept { public AcceptApm(ITestOutputHelper output) : base(output) {} @@ -464,6 +468,7 @@ public async Task AcceptAsync_Precanceled_Throws() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] public async Task AcceptAsync_CanceledDuringOperation_Throws() { using (Socket listen = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/AgnosticListenerTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/AgnosticListenerTest.cs index 5df4c048a37f9c..fd0d0831608991 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/AgnosticListenerTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/AgnosticListenerTest.cs @@ -31,7 +31,7 @@ public void Create_Success() } [OuterLoop] - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public async Task ConnectWithV4_Success() { TcpListener listener = SocketTestExtensions.CreateAndStartTcpListenerOnAnonymousPort(out int port); @@ -48,7 +48,7 @@ public async Task ConnectWithV4_Success() } [OuterLoop] - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public async Task ConnectWithV6_Success() { TcpListener listener = SocketTestExtensions.CreateAndStartTcpListenerOnAnonymousPort(out int port); @@ -65,7 +65,7 @@ public async Task ConnectWithV6_Success() } [OuterLoop] - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public async Task ConnectWithV4AndV6_Success() { TcpListener listener = SocketTestExtensions.CreateAndStartTcpListenerOnAnonymousPort(out int port); @@ -110,7 +110,7 @@ public void StaticCreate_Success() IPEndPoint ep = (IPEndPoint)listener.LocalEndpoint; Assert.Equal(ep.Address, IPAddress.IPv6Any); Assert.Equal(0, ep.Port); - Assert.True(listener.Server.DualMode); + if (!OperatingSystem.IsWasi()) Assert.True(listener.Server.DualMode); listener.Start(); listener.Stop(); diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs index deac6c8f2814d9..eb7739ea3674b0 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs @@ -11,6 +11,7 @@ namespace System.Net.Sockets.Tests { + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public class ArgumentValidation { // This type is used to test Socket.Select's argument validation. diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs index dda67f84155459..dea2a2a19238ea 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs @@ -17,7 +17,7 @@ namespace System.Net.Sockets.Tests public Connect(ITestOutputHelper output) : base(output) {} [OuterLoop] - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] // async SocketTestServer requires threads [MemberData(nameof(Loopbacks))] public async Task Connect_Success(IPAddress listenAt) { @@ -45,7 +45,7 @@ public async Task Connect_Udp_Success(IPAddress listenAt) Assert.True(client.Connected); } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] // async SocketTestServer requires threads [MemberData(nameof(Loopbacks))] public async Task Connect_Dns_Success(IPAddress listenAt) { @@ -68,7 +68,7 @@ public async Task Connect_Dns_Success(IPAddress listenAt) } [OuterLoop] - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] // async SocketTestServer requires threads [MemberData(nameof(Loopbacks))] public async Task Connect_MultipleIPAddresses_Success(IPAddress listenAt) { @@ -85,7 +85,7 @@ public async Task Connect_MultipleIPAddresses_Success(IPAddress listenAt) } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] // async SocketTestServer requires threads public async Task Connect_OnConnectedSocket_Fails() { int port; @@ -102,7 +102,7 @@ public async Task Connect_OnConnectedSocket_Fails() [PlatformSpecific(TestPlatforms.Windows)] // Unix currently does not support Disconnect [OuterLoop] - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] // async SocketTestServer requires threads public async Task Connect_AfterDisconnect_Fails() { int port; @@ -205,6 +205,7 @@ await RetryHelper.ExecuteAsync(async () => [OuterLoop("Connection failure takes long on Windows.")] [Fact] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support PortBlocker")] public async Task Connect_WithoutListener_ThrowSocketExceptionWithAppropriateInfo() { using PortBlocker portBlocker = new PortBlocker(() => @@ -227,6 +228,7 @@ public async Task Connect_WithoutListener_ThrowSocketExceptionWithAppropriateInf [Theory] [MemberData(nameof(LoopbacksAndAny))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] public async Task Connect_DatagramSockets_DontThrowConnectedException_OnSecondAttempt(IPAddress listenAt, IPAddress secondConnection) { using Socket listener = new Socket(listenAt.AddressFamily, SocketType.Dgram, ProtocolType.Udp); @@ -241,16 +243,19 @@ public async Task Connect_DatagramSockets_DontThrowConnectedException_OnSecondAt } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class ConnectSync : Connect { public ConnectSync(ITestOutputHelper output) : base(output) {} } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class ConnectSyncForceNonBlocking : Connect { public ConnectSyncForceNonBlocking(ITestOutputHelper output) : base(output) {} } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class ConnectApm : Connect { public ConnectApm(ITestOutputHelper output) : base(output) {} @@ -265,7 +270,7 @@ public sealed class ConnectEap : Connect { public ConnectEap(ITestOutputHelper output) : base(output) {} - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [InlineData(true)] [InlineData(false)] public async Task ConnectAsync_WithData_DataReceived(bool useArrayApi) @@ -484,7 +489,7 @@ protected Connect_NonParallel(ITestOutputHelper output) : base(output) { } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] // async SocketTestServer requires threads public async Task Connect_DualMode_MultiAddressFamilyConnect_RetrievedEndPoints_Success() { if (!SupportsMultiConnect) @@ -503,7 +508,7 @@ public async Task Connect_DualMode_MultiAddressFamilyConnect_RetrievedEndPoints_ } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] // async SocketTestServer requires threads public async Task Connect_DualMode_DnsConnect_RetrievedEndPoints_Success() { var localhostAddresses = Dns.GetHostAddresses("localhost"); @@ -539,11 +544,13 @@ public sealed class ConnectSync_NonParallel : Connect_NonParallel { public ConnectSyncForceNonBlocking_NonParallel(ITestOutputHelper output) : base(output) { } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class ConnectApm_NonParallel : Connect_NonParallel { public ConnectApm_NonParallel(ITestOutputHelper output) : base(output) { } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/CreateSocketTests.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/CreateSocketTests.cs index c31a2bd89a1e0f..22737d013c22ef 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/CreateSocketTests.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/CreateSocketTests.cs @@ -40,7 +40,7 @@ public CreateSocket(ITestOutputHelper output) new object[] { SocketType.Unknown, ProtocolType.Udp }, }; - private static bool SupportsRawSockets => Environment.IsPrivilegedProcess; + private static bool SupportsRawSockets => Environment.IsPrivilegedProcess && !OperatingSystem.IsWasi(); private static bool NotSupportsRawSockets => !SupportsRawSockets; [OuterLoop] @@ -118,6 +118,7 @@ public void Ctor_Raw_Supported_Success(AddressFamily addressFamily, ProtocolType [InlineData(AddressFamily.InterNetworkV6, ProtocolType.Udp)] [InlineData(AddressFamily.InterNetworkV6, ProtocolType.IcmpV6)] [ConditionalTheory(nameof(NotSupportsRawSockets))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] public void Ctor_Raw_NotSupported_ExpectedError(AddressFamily addressFamily, ProtocolType protocolType) { SocketException e = Assert.Throws(() => new Socket(addressFamily, SocketType.Raw, protocolType)); @@ -277,6 +278,17 @@ public void Ctor_Socket_FromPipeHandle_Ctor_Dispose_Success(bool ownsHandle) [ActiveIssue("https://github.com/dotnet/runtime/issues/52124", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public void Ctor_SafeHandle_BasicPropertiesPropagate_Success(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType) { + if(OperatingSystem.IsWasi() && addressFamily == AddressFamily.Unix) + { + // WASI doesn't support Unix domain sockets. + return; + } + if(OperatingSystem.IsWasi() && socketType == SocketType.Raw) + { + // WASI doesn't support Raw sockets. + return; + } + bool isRawPacket = (addressFamily == AddressFamily.Packet) && (socketType == SocketType.Raw); if (isRawPacket) @@ -357,8 +369,8 @@ public void Ctor_SafeHandle_BasicPropertiesPropagate_Success(AddressFamily addre } Assert.Equal(expectedProtocolType, copy.ProtocolType); - Assert.True(orig.Blocking); - Assert.True(copy.Blocking); + if (!OperatingSystem.IsWasi()) Assert.True(orig.Blocking); + if (!OperatingSystem.IsWasi()) Assert.True(copy.Blocking); if (orig.AddressFamily == copy.AddressFamily) { @@ -372,13 +384,16 @@ public void Ctor_SafeHandle_BasicPropertiesPropagate_Success(AddressFamily addre AssertEqualOrSameException(() => orig.LingerState.LingerTime, () => copy.LingerState.LingerTime); AssertEqualOrSameException(() => orig.NoDelay, () => copy.NoDelay); - Assert.Equal(orig.Available, copy.Available); - Assert.Equal(orig.ExclusiveAddressUse, copy.ExclusiveAddressUse); + if (!OperatingSystem.IsWasi()) Assert.Equal(orig.Available, copy.Available); + if (!OperatingSystem.IsWasi() || protocolType != ProtocolType.Udp) + { + Assert.Equal(orig.ExclusiveAddressUse, copy.ExclusiveAddressUse); + } Assert.Equal(orig.Handle, copy.Handle); Assert.Equal(orig.ReceiveBufferSize, copy.ReceiveBufferSize); - Assert.Equal(orig.ReceiveTimeout, copy.ReceiveTimeout); + if (!OperatingSystem.IsWasi()) Assert.Equal(orig.ReceiveTimeout, copy.ReceiveTimeout); Assert.Equal(orig.SendBufferSize, copy.SendBufferSize); - Assert.Equal(orig.SendTimeout, copy.SendTimeout); + if (!OperatingSystem.IsWasi()) Assert.Equal(orig.SendTimeout, copy.SendTimeout); #pragma warning disable 0618 Assert.Equal(orig.UseOnlyOverlappedIO, copy.UseOnlyOverlappedIO); #pragma warning restore 0618 @@ -409,27 +424,29 @@ public async Task Ctor_SafeHandle_Tcp_SendReceive_Success(AddressFamily addressF Assert.Equal(orig.RemoteEndPoint, client.RemoteEndPoint); // Validating accessing other properties - Assert.Equal(orig.Available, client.Available); - Assert.True(orig.Blocking); - Assert.True(client.Blocking); + if (!OperatingSystem.IsWasi()) // https://github.com/WebAssembly/wasi-libc/issues/538 + Assert.Equal(orig.Available, client.Available); + if (!OperatingSystem.IsWasi()) Assert.True(orig.Blocking); + if (!OperatingSystem.IsWasi()) Assert.True(client.Blocking); AssertEqualOrSameException(() => orig.DontFragment, () => client.DontFragment); AssertEqualOrSameException(() => orig.EnableBroadcast, () => client.EnableBroadcast); Assert.Equal(orig.ExclusiveAddressUse, client.ExclusiveAddressUse); Assert.Equal(orig.Handle, client.Handle); Assert.Equal(orig.IsBound, client.IsBound); - Assert.Equal(orig.LingerState.Enabled, client.LingerState.Enabled); - Assert.Equal(orig.LingerState.LingerTime, client.LingerState.LingerTime); - AssertEqualOrSameException(() => orig.MulticastLoopback, () => client.MulticastLoopback); + if (!OperatingSystem.IsWasi()) Assert.Equal(orig.LingerState.Enabled, client.LingerState.Enabled); + if (!OperatingSystem.IsWasi()) Assert.Equal(orig.LingerState.LingerTime, client.LingerState.LingerTime); + if (!OperatingSystem.IsWasi()) AssertEqualOrSameException(() => orig.MulticastLoopback, () => client.MulticastLoopback); Assert.Equal(orig.NoDelay, client.NoDelay); Assert.Equal(orig.ReceiveBufferSize, client.ReceiveBufferSize); - Assert.Equal(orig.ReceiveTimeout, client.ReceiveTimeout); + if (!OperatingSystem.IsWasi()) Assert.Equal(orig.ReceiveTimeout, client.ReceiveTimeout); Assert.Equal(orig.SendBufferSize, client.SendBufferSize); - Assert.Equal(orig.SendTimeout, client.SendTimeout); + if (!OperatingSystem.IsWasi()) Assert.Equal(orig.SendTimeout, client.SendTimeout); Assert.Equal(orig.Ttl, client.Ttl); // Validate setting various properties on the new instance and seeing them roundtrip back to the original. - client.ReceiveTimeout = 42; - Assert.Equal(client.ReceiveTimeout, orig.ReceiveTimeout); + if (!OperatingSystem.IsWasi()) // https://github.com/WebAssembly/wasi-libc/issues/539 + client.ReceiveTimeout = 42; + if (!OperatingSystem.IsWasi()) Assert.Equal(client.ReceiveTimeout, orig.ReceiveTimeout); // Validate sending and receiving Assert.Equal(1, await client.SendAsync(new byte[1] { 42 }, SocketFlags.None)); @@ -443,7 +460,7 @@ public async Task Ctor_SafeHandle_Tcp_SendReceive_Success(AddressFamily addressF Assert.Equal(42, buffer[0]); } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [InlineData(false)] [InlineData(true)] [ActiveIssue("https://github.com/dotnet/runtime/issues/52124", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] @@ -660,6 +677,7 @@ private static unsafe (int, int) pipe2(int flags = 0) [Fact] [PlatformSpecific(TestPlatforms.AnyUnix)] [ActiveIssue("https://github.com/dotnet/runtime/issues/52124", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] public unsafe void Ctor_SafeHandle_SocketPair_Success() { // This is platform dependent but it seems like this is same on all supported platforms. diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/DisconnectTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/DisconnectTest.cs index 1c0270eaca6bd9..9fe7f43c703b29 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/DisconnectTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/DisconnectTest.cs @@ -13,7 +13,7 @@ namespace System.Net.Sockets.Tests { protected Disconnect(ITestOutputHelper output) : base(output) { } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] // async SocketTestServer requires threads [InlineData(true)] [InlineData(false)] public async Task Disconnect_Success(bool reuseSocket) @@ -50,7 +50,7 @@ public async Task Disconnect_Success(bool reuseSocket) } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] // async SocketTestServer requires threads public async Task DisconnectAndReuse_ReconnectSync_ThrowsInvalidOperationException() { IPEndPoint loopback = new IPEndPoint(IPAddress.Loopback, 0); @@ -97,16 +97,19 @@ public void Disconnect_ObjectDisposed_ThrowsObjectDisposedException(bool reuseSo } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class Disconnect_Sync : Disconnect { public Disconnect_Sync(ITestOutputHelper output) : base(output) { } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class Disconnect_SyncForceNonBlocking : Disconnect { public Disconnect_SyncForceNonBlocking(ITestOutputHelper output) : base(output) { } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class Disconnect_Apm : Disconnect { public Disconnect_Apm(ITestOutputHelper output) : base(output) { } @@ -131,7 +134,7 @@ public sealed class Disconnect_CancellableTask : Disconnect(() => GetDisposedSocket().LingerState); } [Fact] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support Linger")] public void SetLingerState_Throws_ObjectDisposed() { Assert.Throws(() => @@ -258,12 +261,14 @@ public void SetEnableBroadcast_Throws_ObjectDisposed() } [Fact] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public void DualMode_Throws_ObjectDisposed() { Assert.Throws(() => GetDisposedSocket(AddressFamily.InterNetworkV6).DualMode); } [Fact] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public void SetDualMode_Throws_ObjectDisposed() { Assert.Throws(() => diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/DnsEndPointTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/DnsEndPointTest.cs index f23a1e835b7be3..c1b74b2caf3d57 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/DnsEndPointTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/DnsEndPointTest.cs @@ -11,6 +11,7 @@ namespace System.Net.Sockets.Tests { using Configuration = System.Net.Test.Common.Configuration; + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public class DnsEndPointTest : DualModeBase { private void OnConnectAsyncCompleted(object sender, SocketAsyncEventArgs args) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/DualModeSocketTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/DualModeSocketTest.cs index 17c5a3e18240b6..4c3fe672804612 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/DualModeSocketTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/DualModeSocketTest.cs @@ -15,6 +15,7 @@ namespace System.Net.Sockets.Tests { [Trait("IPv4", "true")] [Trait("IPv6", "true")] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public class DualModeConstructorAndProperty : DualModeBase { [Fact] @@ -76,6 +77,7 @@ public void IPv4Constructor_DualMode_SetterThrows() [Trait("IPv4", "true")] [Trait("IPv6", "true")] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public class DualModeConnectToIPAddress : DualModeBase { [Fact] // Base case @@ -168,6 +170,7 @@ private void DualModeConnect_IPAddressToHost_Fails_Helper(IPAddress connectTo, I [Trait("IPv4", "true")] [Trait("IPv6", "true")] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public class DualModeConnectToIPEndPoint : DualModeBase { [Fact] // Base case @@ -260,6 +263,7 @@ private void DualModeConnect_IPEndPointToHost_Fails_Helper(IPAddress connectTo, [Trait("IPv4", "true")] [Trait("IPv6", "true")] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public class DualModeConnectToIPAddressArray : DualModeBase { [Fact] // Base Case @@ -325,6 +329,7 @@ public void DualModeConnect_IPAddressListToHost_Success(IPAddress[] connectTo, I [Trait("IPv4", "true")] [Trait("IPv6", "true")] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public class DualModeConnectToHostString : DualModeBase { [ConditionalTheory(nameof(LocalhostIsBothIPv4AndIPv6))] @@ -343,6 +348,7 @@ public void DualModeConnect_LoopbackDnsToHost_Helper(IPAddress listenOn, bool du [Trait("IPv4", "true")] [Trait("IPv6", "true")] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public class DualModeConnectToDnsEndPoint : DualModeBase { [ConditionalTheory(nameof(LocalhostIsBothIPv4AndIPv6))] @@ -361,6 +367,7 @@ public void DualModeConnect_DnsEndPointToHost_Helper(IPAddress listenOn, bool du [Trait("IPv4", "true")] [Trait("IPv6", "true")] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public class DualModeBeginConnectToIPAddress : DualModeBase { [Fact] // Base case @@ -425,6 +432,7 @@ private async Task DualModeBeginConnect_IPAddressToHost_Fails_Helper(IPAddress c [Trait("IPv4", "true")] [Trait("IPv6", "true")] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public class DualModeBeginConnectToIPEndPoint : DualModeBase { [Fact] @@ -463,6 +471,7 @@ private async Task DualModeBeginConnect_IPEndPointToHost_Helper(IPAddress connec [Trait("IPv4", "true")] [Trait("IPv6", "true")] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public class DualModeBeginConnect : DualModeBase { [Theory] @@ -510,6 +519,7 @@ public async Task DualModeBeginConnect_DnsEndPointToHost_Helper(IPAddress listen [Trait("IPv4", "true")] [Trait("IPv6", "true")] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public class DualModeConnectAsync : DualModeBase { [Fact] // Base case @@ -659,6 +669,7 @@ public void DualModeConnectAsync_Static_DnsEndPointToHost_Helper(IPAddress liste [Trait("IPv4", "true")] [Trait("IPv6", "true")] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public class DualModeBind : DualModeBase { [Fact] @@ -812,6 +823,7 @@ protected static void AssertDualModeEnabled(Socket socket, IPAddress listenOn) [Trait("IPv4", "true")] [Trait("IPv6", "true")] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public class DualModeAcceptSync : DualModeAcceptBase { public DualModeAcceptSync(ITestOutputHelper output) : base(output) { } @@ -819,6 +831,7 @@ public DualModeAcceptSync(ITestOutputHelper output) : base(output) { } [Trait("IPv4", "true")] [Trait("IPv6", "true")] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public class DualModeAcceptApm : DualModeAcceptBase { public DualModeAcceptApm(ITestOutputHelper output) : base(output) { } @@ -826,6 +839,7 @@ public DualModeAcceptApm(ITestOutputHelper output) : base(output) { } [Trait("IPv4", "true")] [Trait("IPv6", "true")] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public class DualModeAcceptEap : DualModeAcceptBase { public DualModeAcceptEap(ITestOutputHelper output) : base(output) { } @@ -833,6 +847,7 @@ public DualModeAcceptEap(ITestOutputHelper output) : base(output) { } [Trait("IPv4", "true")] [Trait("IPv6", "true")] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public class DualModeAcceptTask : DualModeAcceptBase { public DualModeAcceptTask(ITestOutputHelper output) : base(output) { } @@ -913,6 +928,7 @@ private async Task DualModeSendTo_IPEndPointToHost_Failing_Helper(IPAddress conn [Trait("IPv4", "true")] [Trait("IPv6", "true")] [Collection(nameof(DisableParallelization))] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public class DualModeConnectionlessSendToSync : DualModeConnectionlessSendToBase { public DualModeConnectionlessSendToSync(ITestOutputHelper output) : base(output) @@ -923,6 +939,7 @@ public DualModeConnectionlessSendToSync(ITestOutputHelper output) : base(output) [Trait("IPv4", "true")] [Trait("IPv6", "true")] [Collection(nameof(DisableParallelization))] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public class DualModeConnectionlessSendToApm : DualModeConnectionlessSendToBase { public DualModeConnectionlessSendToApm(ITestOutputHelper output) : base(output) @@ -933,6 +950,7 @@ public DualModeConnectionlessSendToApm(ITestOutputHelper output) : base(output) [Trait("IPv4", "true")] [Trait("IPv6", "true")] [Collection(nameof(DisableParallelization))] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public class DualModeConnectionlessSendToEap : DualModeConnectionlessSendToBase { public DualModeConnectionlessSendToEap(ITestOutputHelper output) : base(output) @@ -943,6 +961,7 @@ public DualModeConnectionlessSendToEap(ITestOutputHelper output) : base(output) [Trait("IPv4", "true")] [Trait("IPv6", "true")] [Collection(nameof(DisableParallelization))] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public class DualModeConnectionlessSendToTask : DualModeConnectionlessSendToBase { public DualModeConnectionlessSendToTask(ITestOutputHelper output) : base(output) @@ -1044,6 +1063,7 @@ protected async Task ReceiveFrom_Failure_Helper(IPAddress listenOn, IPAddress co [Trait("IPv4", "true")] [Trait("IPv6", "true")] [Collection(nameof(DisableParallelization))] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public class DualModeConnectionlessReceiveFromSync : DualModeConnectionlessReceiveFromBase { public DualModeConnectionlessReceiveFromSync(ITestOutputHelper output) : base(output) @@ -1054,6 +1074,7 @@ public DualModeConnectionlessReceiveFromSync(ITestOutputHelper output) : base(ou [Trait("IPv4", "true")] [Trait("IPv6", "true")] [Collection(nameof(DisableParallelization))] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public class DualModeConnectionlessReceiveFromApm : DualModeConnectionlessReceiveFromBase { public DualModeConnectionlessReceiveFromApm(ITestOutputHelper output) : base(output) @@ -1064,6 +1085,7 @@ public DualModeConnectionlessReceiveFromApm(ITestOutputHelper output) : base(out [Trait("IPv4", "true")] [Trait("IPv6", "true")] [Collection(nameof(DisableParallelization))] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public class DualModeConnectionlessReceiveFromEap : DualModeConnectionlessReceiveFromBase { public DualModeConnectionlessReceiveFromEap(ITestOutputHelper output) : base(output) @@ -1074,6 +1096,7 @@ public DualModeConnectionlessReceiveFromEap(ITestOutputHelper output) : base(out [Trait("IPv4", "true")] [Trait("IPv6", "true")] [Collection(nameof(DisableParallelization))] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public class DualModeConnectionlessReceiveFromTask : DualModeConnectionlessReceiveFromBase { public DualModeConnectionlessReceiveFromTask(ITestOutputHelper output) : base(output) @@ -1084,6 +1107,7 @@ public DualModeConnectionlessReceiveFromTask(ITestOutputHelper output) : base(ou [Trait("IPv4", "true")] [Trait("IPv6", "true")] [Collection(nameof(DisableParallelization))] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public class DualModeConnectionlessReceiveMessageFrom : DualModeBase { [Fact] diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/EnableBroadcastTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/EnableBroadcastTest.cs index bdb28d978630af..c5c7e75b45032f 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/EnableBroadcastTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/EnableBroadcastTest.cs @@ -5,6 +5,7 @@ namespace System.Net.Sockets.Tests { + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support Broadcast")] public class EnableBroadcastTest { [Fact] diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ExecutionContextFlowTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ExecutionContextFlowTest.cs index 41f473826bde40..d1a5908176b5c3 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ExecutionContextFlowTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ExecutionContextFlowTest.cs @@ -10,6 +10,7 @@ namespace System.Net.Sockets.Tests { + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public class ExecutionContextFlowTest : FileCleanupTestBase { [Theory] diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/IPPacketInformationTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/IPPacketInformationTest.cs index 1eda62921fb50d..595a482c016f15 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/IPPacketInformationTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/IPPacketInformationTest.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Threading; +using System.Threading.Tasks; using Xunit; @@ -27,9 +28,9 @@ public void GetHashCode_DefaultValues_Success() } [Fact] - public void Equals_NonDefaultValue_Success() + public async Task Equals_NonDefaultValue_Success() { - IPPacketInformation packetInfo = GetNonDefaultIPPacketInformation(); + IPPacketInformation packetInfo = await GetNonDefaultIPPacketInformation(); IPPacketInformation packetInfoCopy = packetInfo; Assert.Equal(packetInfo, packetInfoCopy); @@ -48,40 +49,28 @@ public void Equals_NonDefaultValue_Success() } [Fact] - public void GetHashCode_NonDefaultValue_Success() + public async Task GetHashCode_NonDefaultValue_Success() { - IPPacketInformation packetInfo = GetNonDefaultIPPacketInformation(); + IPPacketInformation packetInfo = await GetNonDefaultIPPacketInformation(); Assert.Equal(packetInfo.GetHashCode(), packetInfo.GetHashCode()); } - private IPPacketInformation GetNonDefaultIPPacketInformation() + private async Task GetNonDefaultIPPacketInformation() { - const int ReceiveTimeout = 10000; - using (var receiver = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)) using (var sender = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)) { int port = receiver.BindToAnonymousPort(IPAddress.Loopback); - - var waitHandle = new ManualResetEvent(false); - - SocketAsyncEventArgs receiveArgs = new SocketAsyncEventArgs { - RemoteEndPoint = new IPEndPoint(IPAddress.Loopback, port), - UserToken = waitHandle - }; - - receiveArgs.SetBuffer(new byte[1], 0, 1); - receiveArgs.Completed += (_, args) => ((ManualResetEvent)args.UserToken).Set(); - - Assert.True(receiver.ReceiveMessageFromAsync(receiveArgs), "receiver.ReceiveMessageFromAsync"); - // Send a few packets, in case they aren't delivered reliably. - sender.SendTo(new byte[1], new IPEndPoint(IPAddress.Loopback, port)); + var receiveTask = receiver.ReceiveMessageFromAsync(new byte[1], new IPEndPoint(IPAddress.Loopback, port)); + var sendTask = sender.SendToAsync(new byte[1], new IPEndPoint(IPAddress.Loopback, port)); + + Assert.True(await Task.WhenAny(receiveTask, Task.Delay(TestSettings.PassingTestTimeout)) == receiveTask, "Timed out"); - Assert.True(waitHandle.WaitOne(ReceiveTimeout), "waitHandle.WaitOne"); + var result = await receiveTask; - return receiveArgs.ReceiveMessageFromPacketInfo; + return result.PacketInformation; } } } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/KeepAliveTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/KeepAliveTest.cs index 572bd77ea77fa9..1ce2ecc707dec7 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/KeepAliveTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/KeepAliveTest.cs @@ -128,7 +128,7 @@ public void Socket_Get_KeepAlive_Time_AsByteArray_OptionLengthZero_Failure() { using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) { - if (PlatformDetection.IsWindows) + if (PlatformDetection.IsWindows || PlatformDetection.IsWasi) { Assert.Throws(() => socket.GetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, 0)); } @@ -153,7 +153,7 @@ public void Socket_Get_KeepAlive_Time_AsByteArray_BufferNullOrTooSmall_Failure(b using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) { - if (PlatformDetection.IsWindows) + if (PlatformDetection.IsWindows || PlatformDetection.IsWasi) { Assert.Throws(() => socket.GetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, buffer)); } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/LingerStateTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/LingerStateTest.cs index 776b940d6ff1c8..8cbb2138bd666d 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/LingerStateTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/LingerStateTest.cs @@ -6,6 +6,7 @@ namespace System.Net.Sockets.Tests { + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support Linger")] public class LingerStateTest { private void TestLingerState_Success(Socket sock, bool enabled, int lingerTime) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/LocalEndPointTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/LocalEndPointTest.cs index 673e85b593e24e..b322ebe8cd4a5b 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/LocalEndPointTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/LocalEndPointTest.cs @@ -45,11 +45,20 @@ public async Task UdpSocket_WhenBoundToWildcardAddress_LocalEPDoesNotChangeOnSen byte[] buf = new byte[3]; EndPoint receiveFromEP = new IPEndPoint(Wildcard, 0); - receiver.ReceiveFrom(buf, ref receiveFromEP); + + var tcs = new TaskCompletionSource(); + SocketAsyncEventArgs args = new SocketAsyncEventArgs(); + args.RemoteEndPoint = receiveFromEP; + args.SetBuffer(buf, 0, buf.Length); + args.Completed += (s, e) => tcs.SetResult(e.RemoteEndPoint); + if (receiver.ReceiveFromAsync(args)) + { + Assert.True(await Task.WhenAny(tcs.Task, Task.Delay(TestSettings.PassingTestTimeout)) == tcs.Task, "Timed out"); + } Assert.Equal(new byte[] { 1, 2, 3 }, buf); - Assert.Equal(Loopback, ((IPEndPoint)receiveFromEP).Address); // received from specific address - Assert.Equal(senderPortAfterBind, ((IPEndPoint)receiveFromEP).Port); + Assert.Equal(Loopback, ((IPEndPoint)args.RemoteEndPoint).Address); // received from specific address + Assert.Equal(senderPortAfterBind, ((IPEndPoint)args.RemoteEndPoint).Port); } } @@ -71,14 +80,24 @@ public async Task UdpSocket_WhenNotBound_LocalEPChangeToWildcardOnSendTo() byte[] buf = new byte[3]; EndPoint receiveFromEP = new IPEndPoint(Wildcard, 0); - receiver.ReceiveFrom(buf, ref receiveFromEP); + + var tcs = new TaskCompletionSource(); + SocketAsyncEventArgs args = new SocketAsyncEventArgs(); + args.RemoteEndPoint = receiveFromEP; + args.SetBuffer(buf, 0, buf.Length); + args.Completed += (s, e) => tcs.SetResult(e.RemoteEndPoint); + if (receiver.ReceiveFromAsync(args)) + { + Assert.True(await Task.WhenAny(tcs.Task, Task.Delay(TestSettings.PassingTestTimeout)) == tcs.Task, "Timed out"); + } Assert.Equal(new byte[] { 1, 2, 3 }, buf); - Assert.Equal(Loopback, ((IPEndPoint)receiveFromEP).Address); // received from specific address + Assert.Equal(Loopback, ((IPEndPoint)args.RemoteEndPoint).Address); // received from specific address } } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] // see also https://github.com/WebAssembly/wasi-libc/issues/540 public async Task TcpClientSocket_WhenBoundToWildcardAddress_LocalEPChangeToSpecificOnConnect() { using (Socket server = CreateTcpSocket()) @@ -228,18 +247,21 @@ public LocalEndPointTestIPv6(ITestOutputHelper output) : base(output) { } } [Trait("IPv4", "true")] + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class LocalEndPointTestIPv4Sync : LocalEndPointTestIPv4 { public LocalEndPointTestIPv4Sync(ITestOutputHelper output) : base(output) { } } [Trait("IPv4", "true")] + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class LocalEndPointTestIPv4SyncForceNonBlocking : LocalEndPointTestIPv4 { public LocalEndPointTestIPv4SyncForceNonBlocking(ITestOutputHelper output) : base(output) { } } [Trait("IPv4", "true")] + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class LocalEndPointTestIPv4Apm : LocalEndPointTestIPv4 { public LocalEndPointTestIPv4Apm(ITestOutputHelper output) : base(output) { } @@ -258,18 +280,21 @@ public LocalEndPointTestIPv4Eap(ITestOutputHelper output) : base(output) { } } [Trait("IPv6", "true")] + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class LocalEndPointTestIPv6Sync : LocalEndPointTestIPv6 { public LocalEndPointTestIPv6Sync(ITestOutputHelper output) : base(output) { } } [Trait("IPv6", "true")] + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class LocalEndPointTestIPv6SyncForceNonBlocking : LocalEndPointTestIPv6 { public LocalEndPointTestIPv6SyncForceNonBlocking(ITestOutputHelper output) : base(output) { } } [Trait("IPv6", "true")] + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class LocalEndPointTestIPv6Apm : LocalEndPointTestIPv6 { public LocalEndPointTestIPv6Apm(ITestOutputHelper output) : base(output) { } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/LoggingTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/LoggingTest.cs index d23ee70a0b4a88..69c44d3d443bdf 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/LoggingTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/LoggingTest.cs @@ -20,6 +20,7 @@ public LoggingTest(ITestOutputHelper output) } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] public static void EventSource_ExistsWithCorrectId() { Type esType = typeof(Socket).Assembly.GetType("System.Net.NetEventSource", throwOnError: true, ignoreCase: false); diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/NetworkStreamTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/NetworkStreamTest.cs index a2df38adac5db5..f4e51b061a86fc 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/NetworkStreamTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/NetworkStreamTest.cs @@ -20,17 +20,17 @@ public class NetworkStreamTest : ConnectedStreamConformanceTests protected override bool FlushRequiredToWriteData => false; protected override Type UnsupportedConcurrentExceptionType => null; protected override bool ReadWriteValueTasksProtectSingleConsumption => true; - protected override Task CreateConnectedStreamsAsync() + protected override async Task CreateConnectedStreamsAsync() { using Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); listener.Bind(new IPEndPoint(IPAddress.Loopback, 0)); listener.Listen(); var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - client.Connect(listener.LocalEndPoint); - Socket server = listener.Accept(); + await client.ConnectAsync(listener.LocalEndPoint); + Socket server = await listener.AcceptAsync(); - return Task.FromResult((new NetworkStream(client, ownsSocket: true), new NetworkStream(server, ownsSocket: true))); + return (new NetworkStream(client, ownsSocket: true), new NetworkStream(server, ownsSocket: true)); } [Fact] @@ -75,7 +75,7 @@ public async Task Ctor_NonBlockingSocket_Throws() using (Socket server = await acceptTask) { server.Blocking = false; - Assert.Throws(() => new NetworkStream(server)); + if (!OperatingSystem.IsWasi()) Assert.Throws(() => new NetworkStream(server)); } } } @@ -163,6 +163,7 @@ public async Task Ctor_SocketFileAccessBool_CanReadAndWrite_DoesntOwn(FileAccess [Theory] [InlineData(false)] [InlineData(true)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] public async Task Ctor_SocketBool_CanReadAndWrite(bool ownsSocket) { using (Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) @@ -246,8 +247,11 @@ public async Task Ctor_SocketFileAccess_CanReadAndWrite() Assert.Equal(1, await clientStream.ReadAsync(buffer, 0, 1)); Assert.Equal('a', (char)buffer[0]); - Assert.Throws(() => { serverStream.BeginRead(buffer, 0, 1, null, null); }); - Assert.Throws(() => { clientStream.BeginWrite(buffer, 0, 1, null, null); }); + if (!OperatingSystem.IsWasi()) + { + Assert.Throws(() => { serverStream.BeginRead(buffer, 0, 1, null, null); }); + Assert.Throws(() => { clientStream.BeginWrite(buffer, 0, 1, null, null); }); + } Assert.Throws(() => { serverStream.ReadAsync(buffer, 0, 1); }); Assert.Throws(() => { clientStream.WriteAsync(buffer, 0, 1); }); @@ -293,6 +297,7 @@ await RunWithConnectedNetworkStreamsAsync(async (server, client) => [Theory] [InlineData(false)] [InlineData(true)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] public async Task DisposedClosed_MembersThrowObjectDisposedException(bool close) { await RunWithConnectedNetworkStreamsAsync((server, _) => @@ -326,14 +331,14 @@ public async Task DisposeSocketDirectly_ReadWriteThrowNetworkException(bool deri serverSocket.Dispose(); - ExpectIOException(() => server.Read(new byte[1], 0, 1)); - ExpectIOException(() => server.Write(new byte[1], 0, 1)); + if (!OperatingSystem.IsWasi()) ExpectIOException(() => server.Read(new byte[1], 0, 1)); + if (!OperatingSystem.IsWasi()) ExpectIOException(() => server.Write(new byte[1], 0, 1)); - ExpectIOException(() => server.Read((Span)new byte[1])); - ExpectIOException(() => server.Write((ReadOnlySpan)new byte[1])); + if (!OperatingSystem.IsWasi()) ExpectIOException(() => server.Read((Span)new byte[1])); + if (!OperatingSystem.IsWasi()) ExpectIOException(() => server.Write((ReadOnlySpan)new byte[1])); - ExpectIOException(() => server.BeginRead(new byte[1], 0, 1, null, null)); - ExpectIOException(() => server.BeginWrite(new byte[1], 0, 1, null, null)); + if (!OperatingSystem.IsWasi()) ExpectIOException(() => server.BeginRead(new byte[1], 0, 1, null, null)); + if (!OperatingSystem.IsWasi()) ExpectIOException(() => server.BeginWrite(new byte[1], 0, 1, null, null)); ExpectIOException(() => { _ = server.ReadAsync(new byte[1], 0, 1); }); ExpectIOException(() => { _ = server.WriteAsync(new byte[1], 0, 1); }); @@ -377,7 +382,7 @@ public async Task ReadableWriteableProperties_Roundtrip() serverStream.Readable = false; Assert.False(serverStream.Readable); Assert.False(serverStream.CanRead); - Assert.Throws(() => serverStream.Read(new byte[1], 0, 1)); + await Assert.ThrowsAsync(() => serverStream.ReadAsync(new byte[1], 0, 1)); serverStream.Readable = true; Assert.True(serverStream.Readable); @@ -388,7 +393,7 @@ public async Task ReadableWriteableProperties_Roundtrip() serverStream.Writeable = false; Assert.False(serverStream.Writeable); Assert.False(serverStream.CanWrite); - Assert.Throws(() => serverStream.Write(new byte[1], 0, 1)); + await Assert.ThrowsAsync(() => serverStream.WriteAsync(new byte[1], 0, 1)); serverStream.Writeable = true; Assert.True(serverStream.Writeable); @@ -560,7 +565,7 @@ private static async Task RunWithConnectedNetworkStreamsAsync(Func(() => socket.ReceiveFrom(new byte[1], SocketFlags.None, socketAddress)); + } + + [Fact] + public async Task NullSocketAddress_Throws_ArgumentExceptionAsync() + { + using Socket socket = CreateSocket(); + SocketAddress socketAddress = null; + await Assert.ThrowsAsync(() => socket.ReceiveFromAsync(new Memory(new byte[1]), SocketFlags.None, socketAddress).AsTask()); } @@ -94,6 +102,7 @@ public async Task NotBound_Throws_InvalidOperationException() [Theory] [InlineData(false)] [InlineData(true)] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support PortBlocker")] public async Task ReceiveSent_TCP_Success(bool ipv6) { if (ipv6 && PlatformDetection.IsApplePlatform) @@ -151,7 +160,7 @@ public async Task ReceiveSent_UDP_Success(bool ipv4) for (int i = 0; i < DatagramsToSend; i++) { rnd.NextBytes(sendBuffer); - sender.SendTo(sendBuffer, receiver.LocalEndPoint); + await sender.SendToAsync(sendBuffer, receiver.LocalEndPoint); SocketReceiveFromResult result = await ReceiveFromAsync(receiver, receiveBuffer, remoteEp); @@ -171,6 +180,7 @@ public async Task ReceiveSent_UDP_Success(bool ipv4) [Theory] [InlineData(false)] [InlineData(true)] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support DualMode")] public async Task ReceiveSent_DualMode_Success(bool ipv4) { const int Offset = 10; @@ -217,7 +227,7 @@ public async Task ReceiveSent_DualMode_Success(bool ipv4) [Theory] [InlineData(false)] [InlineData(true)] - public void ReceiveSent_SocketAddress_Success(bool ipv4) + public async Task ReceiveSent_SocketAddress_Success(bool ipv4) { const int DatagramSize = 256; const int DatagramsToSend = 16; @@ -241,17 +251,17 @@ public void ReceiveSent_SocketAddress_Success(bool ipv4) for (int i = 0; i < DatagramsToSend; i++) { rnd.NextBytes(sendBuffer); - client.SendTo(sendBuffer.AsSpan(), SocketFlags.None, serverSA); + await client.SendToAsync(sendBuffer, SocketFlags.None, serverSA); - int readBytes = server.ReceiveFrom(receiveBuffer, SocketFlags.None, sa); + int readBytes = await server.ReceiveFromAsync(receiveBuffer, SocketFlags.None, sa); Assert.Equal(sa, clientSA); Assert.Equal(client.LocalEndPoint, client.LocalEndPoint.Create(sa)); Assert.True(new Span(receiveBuffer, 0, readBytes).SequenceEqual(sendBuffer)); // and send it back to make sure it works. rnd.NextBytes(sendBuffer); - server.SendTo(sendBuffer, SocketFlags.None, sa); - readBytes = client.ReceiveFrom(receiveBuffer, SocketFlags.None, sa); + await server.SendToAsync(sendBuffer, SocketFlags.None, sa); + readBytes = await client.ReceiveFromAsync(receiveBuffer, SocketFlags.None, sa); Assert.Equal(sa, serverSA); Assert.Equal(server.LocalEndPoint, server.LocalEndPoint.Create(sa)); Assert.True(new Span(receiveBuffer, 0, readBytes).SequenceEqual(sendBuffer)); @@ -303,7 +313,7 @@ public async Task ReceiveSent_SocketAddressAsync_Success(bool ipv4) } [Fact] - public void ReceiveSent_SmallSocketAddress_Throws() + public async Task ReceiveSent_SmallSocketAddress_Throws() { using Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); server.BindToAnonymousPort(IPAddress.Loopback); @@ -313,7 +323,7 @@ public void ReceiveSent_SmallSocketAddress_Throws() SocketAddress serverSA = server.LocalEndPoint.Serialize(); SocketAddress sa = new SocketAddress(AddressFamily.InterNetwork, 2); - Assert.Throws(() => server.ReceiveFrom(receiveBuffer, SocketFlags.None, sa)); + await Assert.ThrowsAsync(async () => await server.ReceiveFromAsync(receiveBuffer, SocketFlags.None, sa)); } [Theory] @@ -333,6 +343,7 @@ public async Task ClosedBeforeOperation_Throws_ObjectDisposedException(bool clos [InlineData(true)] [InlineData(false)] [ActiveIssue("https://github.com/dotnet/runtime/issues/52124", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] public async Task ClosedDuringOperation_Throws_ObjectDisposedExceptionOrSocketException(bool closeOrDispose) { if (UsesSync && PlatformDetection.IsOSX) @@ -403,23 +414,26 @@ public async Task ShutdownSend_ReceiveFromShouldSucceed() using var sender = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); sender.BindToAnonymousPort(IPAddress.Loopback); - sender.SendTo(new byte[1], receiver.LocalEndPoint); + await sender.SendToAsync(new byte[1], receiver.LocalEndPoint); var r = await ReceiveFromAsync(receiver, new byte[1], sender.LocalEndPoint); Assert.Equal(1, r.ReceivedBytes); } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class ReceiveFrom_Sync : ReceiveFrom { public ReceiveFrom_Sync(ITestOutputHelper output) : base(output) { } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class ReceiveFrom_SyncForceNonBlocking : ReceiveFrom { public ReceiveFrom_SyncForceNonBlocking(ITestOutputHelper output) : base(output) { } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class ReceiveFrom_Apm : ReceiveFrom { public ReceiveFrom_Apm(ITestOutputHelper output) : base(output) { } @@ -463,7 +477,7 @@ public void EndReceiveFrom_AddressFamilyDoesNotMatch_Throws_ArgumentException() Assert.Throws("endPoint", () => socket.EndReceiveFrom(iar, ref invalidEndPoint)); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/54418", TestPlatforms.MacCatalyst)] public void BeginReceiveFrom_RemoteEpIsReturnedWhenCompletedSynchronously() { @@ -532,11 +546,13 @@ public void ReceiveFromAsync_NullAsyncEventArgs_Throws_ArgumentNullException() } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class ReceiveFrom_SpanSync : ReceiveFrom { public ReceiveFrom_SpanSync(ITestOutputHelper output) : base(output) { } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class ReceiveFrom_SpanSyncForceNonBlocking : ReceiveFrom { public ReceiveFrom_SpanSyncForceNonBlocking(ITestOutputHelper output) : base(output) { } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs index e8663bc8047e9d..d6161b3e9d0958 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs @@ -87,6 +87,7 @@ public async Task NotBound_Throws_InvalidOperationException() [Theory] [InlineData(false)] [InlineData(true)] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support PortBlocker")] public async Task ReceiveSent_TCP_Success(bool ipv6) { if (ipv6 && PlatformDetection.IsApplePlatform) @@ -145,15 +146,15 @@ public async Task ReceiveSent_UDP_Success(bool ipv4) for (int i = 0; i < DatagramsToSend; i++) { rnd.NextBytes(sendBuffer); - sender.SendTo(sendBuffer, receiver.LocalEndPoint); + await sender.SendToAsync(sendBuffer, receiver.LocalEndPoint); SocketReceiveMessageFromResult result = await ReceiveMessageFromAsync(receiver, receiveBuffer, remoteEp); - IPPacketInformation packetInformation = result.PacketInformation; Assert.Equal(DatagramSize, result.ReceivedBytes); AssertExtensions.SequenceEqual(emptyBuffer, new ReadOnlySpan(receiveInternalBuffer, 0, Offset)); AssertExtensions.SequenceEqual(sendBuffer, new ReadOnlySpan(receiveInternalBuffer, Offset, DatagramSize)); Assert.Equal(sender.LocalEndPoint, result.RemoteEndPoint); + IPPacketInformation packetInformation = result.PacketInformation; Assert.Equal(((IPEndPoint)sender.LocalEndPoint).Address, packetInformation.Address); } } @@ -272,6 +273,7 @@ public async Task SendBroadcast_BroadcastFlagIsSetOnReceive() [Fact] [PlatformSpecific(TestPlatforms.AnyUnix)] // Windows doesn't report MSG_TRUNC + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support MSG_TRUNC")] public async Task ReceiveTruncated_TruncatedFlagIsSetOnReceive() { using var receiver = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); @@ -285,16 +287,19 @@ public async Task ReceiveTruncated_TruncatedFlagIsSetOnReceive() } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class ReceiveMessageFrom_Sync : ReceiveMessageFrom { public ReceiveMessageFrom_Sync(ITestOutputHelper output) : base(output) { } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class ReceiveMessageFrom_SyncForceNonBlocking : ReceiveMessageFrom { public ReceiveMessageFrom_SyncForceNonBlocking(ITestOutputHelper output) : base(output) { } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class ReceiveMessageFrom_Apm : ReceiveMessageFrom { public ReceiveMessageFrom_Apm(ITestOutputHelper output) : base(output) { } @@ -345,7 +350,7 @@ public void EndReceiveMessageFrom_AddressFamilyDoesNotMatch_Throws_ArgumentExcep Assert.Throws("endPoint", () => socket.EndReceiveMessageFrom(iar, ref socketFlags, ref invalidEndPoint, out _)); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void BeginReceiveMessageFrom_RemoteEpIsReturnedWhenCompletedSynchronously() { EndPoint anyEp = new IPEndPoint(IPAddress.Any, 0); @@ -412,7 +417,7 @@ public void ReceiveFromAsync_NullAsyncEventArgs_Throws_ArgumentNullException() Assert.Throws(() => socket.ReceiveMessageFromAsync(null)); } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [InlineData(false, 0)] [InlineData(false, 1)] [InlineData(false, 2)] @@ -493,11 +498,13 @@ public void ReceiveSentMessages_ReuseEventArgs_Success(bool ipv4, int bufferMode } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class ReceiveMessageFrom_SpanSync : ReceiveMessageFrom { public ReceiveMessageFrom_SpanSync(ITestOutputHelper output) : base(output) { } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class ReceiveMessageFrom_SpanSyncForceNonBlocking : ReceiveMessageFrom { public ReceiveMessageFrom_SpanSyncForceNonBlocking(ITestOutputHelper output) : base(output) { } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SafeHandleTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SafeHandleTest.cs index 36b594654f7442..9e1453a1a7fa2e 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SafeHandleTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SafeHandleTest.cs @@ -18,7 +18,7 @@ public static void SafeHandle_NotIsInvalid() } [Fact] - [PlatformSpecific(TestPlatforms.Windows | TestPlatforms.AnyUnix)] + [PlatformSpecific(TestPlatforms.Windows | TestPlatforms.AnyUnix ^ TestPlatforms.Wasi)] public void SafeSocketHandle_CanUseInPInvoke() { const int AF_INET = 2; diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SelectAndPollTests.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SelectAndPollTests.cs index ff545111021418..a0bc4ed573441b 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SelectAndPollTests.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SelectAndPollTests.cs @@ -9,6 +9,7 @@ namespace System.Net.Sockets.Tests { + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public class SelectAndPollTests { private const int SelectTimeout = 100; diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SelectTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SelectTest.cs index 190a688475339c..0e7d6649bdf814 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SelectTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SelectTest.cs @@ -11,6 +11,7 @@ namespace System.Net.Sockets.Tests { + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public class SelectTest { private readonly ITestOutputHelper _log; @@ -353,6 +354,7 @@ private static void DisposeSockets(IEnumerable> soc } [Collection(nameof(DisableParallelization))] + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public class SelectTest_NonParallel { [OuterLoop] @@ -420,6 +422,7 @@ private static void DoAccept(Socket listenSocket, int connectionsToAccept) } [Collection(nameof(DisableParallelization))] + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] // Set of tests to not run together with any other tests. public class NoParallelSelectTests { diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendFile.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendFile.cs index 5cc59d332f4ddf..10313336876ce4 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendFile.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendFile.cs @@ -74,7 +74,7 @@ public async Task UdpConnection_ThrowsException(bool usePreAndPostbufferOverload using var listener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); listener.BindToAnonymousPort(IPAddress.Loopback); - client.Connect(listener.LocalEndPoint); + await client.ConnectAsync(listener.LocalEndPoint); if (usePreAndPostbufferOverload) { @@ -125,11 +125,11 @@ public async Task IncludeFile_Success(IPAddress listenAt, bool sendPreAndPostBuf int bytesReceived = 0; var receivedChecksum = new Fletcher32(); - var serverTask = Task.Run(() => + var serverTask = Task.Run(async () => { using (server) { - Socket remote = server.Accept(); + Socket remote = await server.AcceptAsync(); Assert.NotNull(remote); using (remote) @@ -176,11 +176,12 @@ public async Task NoFile_Succeeds(bool usePreBuffer, bool usePostBuffer) listener.BindToAnonymousPort(IPAddress.Loopback); listener.Listen(1); - client.Connect(listener.LocalEndPoint); - using Socket server = listener.Accept(); + await client.ConnectAsync(listener.LocalEndPoint); + using Socket server = await listener.AcceptAsync(); await SendFileAsync(server, null); - Assert.Equal(0, client.Available); + if (!OperatingSystem.IsWasi()) // https://github.com/WebAssembly/wasi-libc/issues/538 + Assert.Equal(0, client.Available); byte[] preBuffer = usePreBuffer ? new byte[1] : null; byte[] postBuffer = usePostBuffer ? new byte[1] : null; @@ -191,10 +192,11 @@ public async Task NoFile_Succeeds(bool usePreBuffer, bool usePostBuffer) byte[] receiveBuffer = new byte[1]; for (int i = 0; i < bytesExpected; i++) { - Assert.Equal(1, client.Receive(receiveBuffer)); + Assert.Equal(1, await client.ReceiveAsync(receiveBuffer)); } - Assert.Equal(0, client.Available); + if (!OperatingSystem.IsWasi()) // https://github.com/WebAssembly/wasi-libc/issues/538 + Assert.Equal(0, client.Available); } [Fact] @@ -361,31 +363,37 @@ private TempFile CreateFileToSend(int size, bool sendPreAndPostBuffers, out byte } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class SendFile_SyncSpan : SendFile { public SendFile_SyncSpan(ITestOutputHelper output) : base(output) { } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class SendFile_SyncSpanForceNonBlocking : SendFile { public SendFile_SyncSpanForceNonBlocking(ITestOutputHelper output) : base(output) { } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class SendFile_ArraySync : SendFile { public SendFile_ArraySync(ITestOutputHelper output) : base(output) { } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class SendFile_SyncForceNonBlocking : SendFile { public SendFile_SyncForceNonBlocking(ITestOutputHelper output) : base(output) { } } + [ActiveIssue("https://github.com/dotnet/runtime/issues/85690", TestPlatforms.Wasi)] public sealed class SendFile_Task : SendFile { public SendFile_Task(ITestOutputHelper output) : base(output) { } } + [ActiveIssue("https://github.com/dotnet/runtime/issues/85690", TestPlatforms.Wasi)] public sealed class SendFile_CancellableTask : SendFile { public SendFile_CancellableTask(ITestOutputHelper output) : base(output) { } @@ -445,6 +453,7 @@ public async Task SendFileAsync_CanceledDuringOperation_Throws(bool ipv6) } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class SendFile_Apm : SendFile { public SendFile_Apm(ITestOutputHelper output) : base(output) { } @@ -515,26 +524,31 @@ public async Task GreaterThan2GBFile_SendsAllBytes() } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class SendFile_NonParallel_SyncSpan : SendFile_NonParallel { public SendFile_NonParallel_SyncSpan(ITestOutputHelper output) : base(output) { } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class SendFile_NonParallel_SyncSpanForceNonBlocking : SendFile_NonParallel { public SendFile_NonParallel_SyncSpanForceNonBlocking(ITestOutputHelper output) : base(output) { } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class SendFile_NonParallel_ArraySync : SendFile_NonParallel { public SendFile_NonParallel_ArraySync(ITestOutputHelper output) : base(output) { } } + [ActiveIssue("https://github.com/dotnet/runtime/issues/85690", TestPlatforms.Wasi)] public sealed class SendFile_NonParallel_Task : SendFile_NonParallel { public SendFile_NonParallel_Task(ITestOutputHelper output) : base(output) { } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class SendFile_NonParallel_Apm : SendFile_NonParallel { public SendFile_NonParallel_Apm(ITestOutputHelper output) : base(output) { } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendPacketsAsync.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendPacketsAsync.cs index 79d05dc28bde57..4aefa65406d7c0 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendPacketsAsync.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendPacketsAsync.cs @@ -14,6 +14,7 @@ namespace System.Net.Sockets.Tests { [Collection(nameof(DisableParallelization))] + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public class SendPacketsAsync : IDisposable { private readonly ITestOutputHelper _log; diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendPacketsElementTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendPacketsElementTest.cs index d0e1b33cf517b8..1c791e67c99ac2 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendPacketsElementTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendPacketsElementTest.cs @@ -618,6 +618,7 @@ public void FileStreamCtorNull_Throws() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/85690", TestPlatforms.Wasi)] public void FileStreamCtorNormal_Success() { using (var stream = File.Create(Path.GetTempFileName(), 4096, FileOptions.DeleteOnClose | FileOptions.Asynchronous)) @@ -635,6 +636,7 @@ public void FileStreamCtorNormal_Success() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/85690", TestPlatforms.Wasi)] public void FileStreamCtorZeroCountLength_Success() { using (var stream = File.Create(Path.GetTempFileName(), 4096, FileOptions.DeleteOnClose | FileOptions.Asynchronous)) @@ -662,6 +664,7 @@ public void FileStreamCtorZeroCountLength_Success() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/85690", TestPlatforms.Wasi)] public void FileStreamCtorNegOffset_ArgumentOutOfRangeException() { using (var stream = File.Create(Path.GetTempFileName(), 4096, FileOptions.DeleteOnClose | FileOptions.Asynchronous)) @@ -686,6 +689,7 @@ public void FileStreamCtorNegOffset_ArgumentOutOfRangeException() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/85690", TestPlatforms.Wasi)] public void FileStreamCtorNegCount_ArgumentOutOfRangeException() { using (var stream = File.Create(Path.GetTempFileName(), 4096, FileOptions.DeleteOnClose | FileOptions.Asynchronous)) @@ -710,6 +714,7 @@ public void FileStreamCtorNegCount_ArgumentOutOfRangeException() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/85690", TestPlatforms.Wasi)] public void FileStreamCtorSynchronous_ArgumentException() { using (var stream = File.Create(Path.GetTempFileName(), 4096, FileOptions.DeleteOnClose)) @@ -724,6 +729,7 @@ public void FileStreamCtorSynchronous_ArgumentException() // File lengths are validated on send [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/85690", TestPlatforms.Wasi)] public void FileStreamCtorEndOfBufferTrue_Success() { using (var stream = File.Create(Path.GetTempFileName(), 4096, FileOptions.DeleteOnClose | FileOptions.Asynchronous)) @@ -764,6 +770,7 @@ public void FileStreamCtorEndOfBufferTrue_Success() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/85690", TestPlatforms.Wasi)] public void FileStreamCtorEndOfBufferFalse_Success() { using (var stream = File.Create(Path.GetTempFileName(), 4096, FileOptions.DeleteOnClose | FileOptions.Asynchronous)) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceive.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceive.cs index 209f91b2ffba95..ce7f48d90d30e1 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceive.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceive.cs @@ -156,7 +156,7 @@ await ConnectAsync(client, clientEndpoint) } } - client.LingerState = new LingerOption(true, LingerTime); + if (!OperatingSystem.IsWasi()) client.LingerState = new LingerOption(true, LingerTime); client.Shutdown(SocketShutdown.Send); await serverProcessingTask.WaitAsync(TestSettings.PassingTestTimeout).ConfigureAwait(false); } @@ -621,6 +621,7 @@ await Task.WhenAll( } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] public async Task SendRecv_0ByteReceive_Success() { using (Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) @@ -700,6 +701,7 @@ public async Task Send_0ByteSendTo_Success() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] public async Task Receive0ByteReturns_WhenPeerDisconnects() { using (Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) @@ -733,6 +735,7 @@ await Task.WhenAll( [Theory] [InlineData(false, 1)] [InlineData(true, 1)] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support Linger")] public async Task SendRecv_BlockingNonBlocking_LingerTimeout_Success(bool blocking, int lingerTimeout) { if (UsesSync) return; @@ -770,6 +773,7 @@ await Task.WhenAll( [Fact] [SkipOnPlatform(TestPlatforms.OSX | TestPlatforms.FreeBSD, "SendBufferSize, ReceiveBufferSize = 0 not supported on BSD like stacks.")] [ActiveIssue("https://github.com/dotnet/runtime/issues/52124", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] public async Task SendRecv_NoBuffering_Success() { if (UsesSync) return; @@ -857,6 +861,7 @@ public void SocketSendReceiveBufferSize_SetZero_ThrowsSocketException() } [Fact] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support PortBlocker")] public async Task SendAsync_ConcurrentDispose_SucceedsOrThrowsAppropriateException() { if (UsesSync) return; @@ -895,6 +900,7 @@ error is SocketException || } [Fact] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support PortBlocker")] public async Task ReceiveAsync_ConcurrentDispose_SucceedsOrThrowsAppropriateException() { if (UsesSync) return; @@ -946,6 +952,7 @@ error is SocketException || [Theory] [MemberData(nameof(UdpReceiveGetsCanceledByDispose_Data))] [SkipOnPlatform(TestPlatforms.OSX, "Not supported on OSX.")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] [ActiveIssue("https://github.com/dotnet/runtime/issues/52124", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public async Task UdpReceiveGetsCanceledByDispose(IPAddress address, bool owning) { @@ -1023,6 +1030,7 @@ await Task.WhenAny(disposeTask, receiveTask) [Theory] [MemberData(nameof(TcpReceiveSendGetsCanceledByDispose_Data))] [ActiveIssue("https://github.com/dotnet/runtime/issues/50568", TestPlatforms.Android | TestPlatforms.LinuxBionic)] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support PortBlocker")] public async Task TcpReceiveSendGetsCanceledByDispose(bool receiveOrSend, bool ipv6Server, bool dualModeClient, bool owning) { // Aborting sync operations for non-owning handles is not supported on Unix. @@ -1145,6 +1153,7 @@ await socketOperation } [Fact] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support PortBlocker")] public async Task TcpPeerReceivesFinOnShutdownWithPendingData() { // We try this a couple of times to deal with a timing race: if the Dispose happens @@ -1181,12 +1190,14 @@ await RetryHelper.ExecuteAsync(async () => } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class SendReceive_Sync : SendReceive { public SendReceive_Sync(ITestOutputHelper output) : base(output) { } [OuterLoop] [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support PortBlocker")] public async Task BlockingRead_DoesntRequireAnotherThreadPoolThread() { await RemoteExecutor.Invoke(() => @@ -1236,11 +1247,13 @@ select Task.Factory.StartNew(() => pair.Item1.Receive(new byte[1]), Cancellation } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class SendReceive_SyncForceNonBlocking : SendReceive { public SendReceive_SyncForceNonBlocking(ITestOutputHelper output) : base(output) {} } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class SendReceive_Apm : SendReceive { public SendReceive_Apm(ITestOutputHelper output) : base(output) {} @@ -1256,6 +1269,7 @@ public sealed class SendReceive_Eap : SendReceive public SendReceive_Eap(ITestOutputHelper output) : base(output) {} } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class SendReceive_SpanSync : SendReceive { public SendReceive_SpanSync(ITestOutputHelper output) : base(output) { } @@ -1305,10 +1319,10 @@ public async Task Send_0ByteSendTo_Span_Success() for (int i = 0; i < 3; i++) { // Send empty packet then real data. - int bytesSent = client.SendTo(ReadOnlySpan.Empty, server.LocalEndPoint!); + int bytesSent = await client.SendToAsync(ArraySegment.Empty, server.LocalEndPoint!); Assert.Equal(0, bytesSent); - client.SendTo(new byte[] { 99 }, server.LocalEndPoint); + await client.SendToAsync(new byte[] { 99 }, server.LocalEndPoint); // Read empty packet byte[] buffer = new byte[10]; @@ -1329,6 +1343,7 @@ public async Task Send_0ByteSendTo_Span_Success() } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class SendReceive_SpanSyncForceNonBlocking : SendReceive { public SendReceive_SpanSyncForceNonBlocking(ITestOutputHelper output) : base(output) { } @@ -1431,6 +1446,7 @@ public async Task Precanceled_Throws() [Theory] [InlineData(false)] [InlineData(true)] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support PortBlocker")] public async Task SendAsync_CanceledDuringOperation_Throws(bool ipv6) { const int CancelAfter = 200; // ms @@ -1465,6 +1481,7 @@ public async Task SendAsync_CanceledDuringOperation_Throws(bool ipv6) [Theory] [InlineData(false)] [InlineData(true)] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support PortBlocker")] public async Task ReceiveAsync_CanceledDuringOperation_Throws(bool ipv6) { (Socket client, Socket server) = SocketTestExtensions.CreateConnectedSocketPair(ipv6); @@ -1487,6 +1504,7 @@ public async Task ReceiveAsync_CanceledDuringOperation_Throws(bool ipv6) } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] public async Task CanceledOneOfMultipleReceives_Udp_Throws() { using (var client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)) @@ -1544,7 +1562,7 @@ public async Task DisposedSocket_ThrowsOperationCanceledException() } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public async Task BlockingAsyncContinuations_OperationsStillCompleteSuccessfully() { if (UsesSync) return; diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveMisc.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveMisc.cs index 44fe8a3b2cfff3..271c5d68896bec 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveMisc.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveMisc.cs @@ -10,7 +10,7 @@ namespace System.Net.Sockets.Tests { public class SendReceiveMisc { - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void SendRecvIovMaxTcp_Success() { // sending/receiving more than IOV_MAX segments causes EMSGSIZE on some platforms. @@ -76,7 +76,7 @@ public void SendRecvIovMaxTcp_Success() } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void SendIovMaxUdp_SuccessOrMessageSize() { // sending more than IOV_MAX segments causes EMSGSIZE on some platforms. @@ -117,7 +117,7 @@ public void SendIovMaxUdp_SuccessOrMessageSize() } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public async Task ReceiveIovMaxUdp_SuccessOrMessageSize() { // receiving more than IOV_MAX segments causes EMSGSIZE on some platforms. @@ -190,7 +190,7 @@ public async Task ReceiveIovMaxUdp_SuccessOrMessageSize() await receiveTask; } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [SkipOnPlatform(TestPlatforms.Windows, "All data is sent, even when very large (100M).")] public void SocketSendWouldBlock_ReturnsBytesSent() { @@ -219,7 +219,7 @@ public void SocketSendWouldBlock_ReturnsBytesSent() } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [PlatformSpecific(TestPlatforms.AnyUnix)] public async Task Socket_ReceiveFlags_Success() { @@ -284,7 +284,7 @@ public async Task Socket_ReceiveFlags_Success() } } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [InlineData(true)] [InlineData(false)] public void ReceiveFrom_MultipleRounds_Success(bool async) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveNonParallel.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveNonParallel.cs index 97cad8577407b2..20f1983f52e67f 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveNonParallel.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveNonParallel.cs @@ -104,16 +104,19 @@ public async Task SendToRecvFrom_Datagram_UDP(IPAddress loopbackAddress, bool us } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class SendReceiveNonParallel_Sync : SendReceiveNonParallel { public SendReceiveNonParallel_Sync(ITestOutputHelper output) : base(output) { } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class SendReceiveNonParallel_SyncForceNonBlocking : SendReceiveNonParallel { public SendReceiveNonParallel_SyncForceNonBlocking(ITestOutputHelper output) : base(output) { } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class SendReceiveNonParallel_Apm : SendReceiveNonParallel { public SendReceiveNonParallel_Apm(ITestOutputHelper output) : base(output) { } @@ -134,11 +137,13 @@ public sealed class SendReceiveNonParallel_Eap : SendReceiveNonParallel { public SendReceiveNonParallel_SpanSync(ITestOutputHelper output) : base(output) { } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class SendReceiveNonParallel_SpanSyncForceNonBlocking : SendReceiveNonParallel { public SendReceiveNonParallel_SpanSyncForceNonBlocking(ITestOutputHelper output) : base(output) { } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveUdpClient.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveUdpClient.cs index 252b862bce1abd..851ba978ebca65 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveUdpClient.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveUdpClient.cs @@ -10,7 +10,7 @@ namespace System.Net.Sockets.Tests public sealed class SendReceiveUdpClient : MemberDatas { [OuterLoop] - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [MemberData(nameof(LoopbacksAndUseMemory))] public async Task SendToRecvFromAsync_Datagram_UDP_UdpClient(IPAddress loopbackAddress, bool useMemoryOverload) { diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs index 7a3c33b64bf796..62598f6be434cc 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs @@ -69,7 +69,7 @@ public async Task NullSocketAddress_Throws_ArgumentException() using Socket socket = CreateSocket(); SocketAddress socketAddress = null; - Assert.Throws(() => socket.SendTo(new byte[1], SocketFlags.None, socketAddress)); + if (!OperatingSystem.IsWasi()) Assert.Throws(() => socket.SendTo(new byte[1], SocketFlags.None, socketAddress)); await AssertThrowsSynchronously(() => socket.SendToAsync(new byte[1], SocketFlags.None, socketAddress).AsTask()); } @@ -116,26 +116,31 @@ public async Task Disposed_Throws() } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class SendTo_SyncSpan : SendTo { public SendTo_SyncSpan(ITestOutputHelper output) : base(output) { } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class SendTo_SyncSpanForceNonBlocking : SendTo { public SendTo_SyncSpanForceNonBlocking(ITestOutputHelper output) : base(output) { } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class SendTo_ArraySync : SendTo { public SendTo_ArraySync(ITestOutputHelper output) : base(output) { } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class SendTo_SyncForceNonBlocking : SendTo { public SendTo_SyncForceNonBlocking(ITestOutputHelper output) : base(output) {} } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public sealed class SendTo_Apm : SendTo { public SendTo_Apm(ITestOutputHelper output) : base(output) {} diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Shutdown.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Shutdown.cs index 3f5a2003d9e348..00022fd6126782 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Shutdown.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Shutdown.cs @@ -71,7 +71,7 @@ private static void OnOperationCompleted(object sender, SocketAsyncEventArgs arg } [OuterLoop] // Explicitly waits for 5 seconds - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void Shutdown_TCP_CLOSED_Success() { // NOTE: this value should technically be at least as long as the amount diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.cs index afd56dfcc9ef76..9909192150ed0b 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.cs @@ -71,7 +71,7 @@ public void Dispose_MultipleCalls_Success() } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public async Task Dispose_WhileInUse_DisposeDelayed() { using (var listen = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) @@ -105,7 +105,7 @@ await Task.WhenAll( } } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [InlineData(false)] [InlineData(true)] public async Task ExecutionContext_FlowsIfNotSuppressed(bool suppressed) @@ -147,13 +147,13 @@ await Task.WhenAll( } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public async Task ExecutionContext_SocketAsyncEventArgs_Ctor_Default_FlowIsNotSuppressed() { await ExecutionContext_SocketAsyncEventArgs_Ctors(() => new SocketAsyncEventArgs(), false); } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [InlineData(true)] [InlineData(false)] public async Task ExecutionContext_SocketAsyncEventArgs_Ctor_UnsafeSuppressExecutionContextFlow(bool suppressed) @@ -358,7 +358,7 @@ await Task.WhenAll( tcs2 = new TaskCompletionSource(); Assert.True(client.ReceiveAsync(receiveSaea)); - server.Send(new byte[1]); + await server.SendAsync(new byte[1]); await Task.WhenAll(tcs1.Task, tcs2.Task); receiveSaea.Completed -= handler2; @@ -367,7 +367,7 @@ await Task.WhenAll( tcs2 = new TaskCompletionSource(); Assert.True(client.ReceiveAsync(receiveSaea)); - server.Send(new byte[1]); + await server.SendAsync(new byte[1]); await tcs1.Task; Assert.False(tcs2.Task.IsCompleted); @@ -430,6 +430,7 @@ public void CancelConnectAsync_StaticConnect_CancelsInProgressConnect() [InlineData(false, 10_000)] [InlineData(true, 1)] // This should fit with SYN flag [InlineData(true, 10_000)] // This should be too big to fit completely to first packet. + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support FastOpen")] public async Task ConnectAsync_WithData_OK(bool useFastOpen, int size) { if (useFastOpen && PlatformDetection.IsWindows && !PlatformDetection.IsWindows10OrLater) @@ -521,7 +522,7 @@ public async Task ConnectAsync_WithData_OK(bool useFastOpen, int size) } } - [ConditionalTheory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [InlineData(false, 1)] [InlineData(false, 10_000)] [InlineData(true, 1)] // This should fit with SYN flag @@ -687,7 +688,7 @@ private static void OnAcceptCompleted(object sender, SocketAsyncEventArgs args) } [OuterLoop] - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [PlatformSpecific(TestPlatforms.Windows)] // Unix platforms don't yet support receiving data with AcceptAsync. public void AcceptAsync_WithReceiveBuffer_Success() { @@ -775,6 +776,7 @@ public void AcceptAsync_WithReceiveBuffer_Failure() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] public async Task SocketConnectAsync_IPAddressAny_SocketAsyncEventArgsReusableAfterFailure() { var e = new SocketAsyncEventArgs(); @@ -1041,7 +1043,7 @@ void CreateSocketAsyncEventArgs() // separated out so that JIT doesn't extend li }, 30_000)); } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [InlineData(false)] [InlineData(true)] public async Task SendTo_DifferentEP_Success(bool ipv4) @@ -1093,6 +1095,8 @@ internal static class ConnectExtensions { internal static void Connect(this Socket socket, EndPoint ep, Memory buffer, int timeout) { + Assert.False(OperatingSystem.IsWasi()); // wait below requires threading + var re = new ManualResetEventSlim(); var saea = new SocketAsyncEventArgs(); saea.SetBuffer(buffer); diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs index 4ff0052bffb06a..58db131e2476d3 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs @@ -387,10 +387,12 @@ static async Task HandlerServerCode(string ipcPortString) } } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public class Synchronous : PolymorphicTests { } + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public class Apm : PolymorphicTests { } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketOptionNameTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketOptionNameTest.cs index 26752dd31f5dcb..8962f6b128e041 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketOptionNameTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketOptionNameTest.cs @@ -12,6 +12,7 @@ namespace System.Net.Sockets.Tests { + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public partial class SocketOptionNameTest { private static bool SocketsReuseUnicastPortSupport => Capability.SocketsReuseUnicastPortSupport().HasValue; @@ -720,6 +721,7 @@ public void SetUnsupportedRawSocketOption_DoesNotDisconnectSocket() [Collection(nameof(DisableParallelization))] // Set of tests to not run together with any other tests. + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public class NoParallelTests { [Fact] diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/System.Net.Sockets.Tests.csproj b/src/libraries/System.Net.Sockets/tests/FunctionalTests/System.Net.Sockets.Tests.csproj index 2b5c1cea632f06..43844aea397681 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/System.Net.Sockets.Tests.csproj +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/System.Net.Sockets.Tests.csproj @@ -2,9 +2,10 @@ true true - $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-unix;$(NetCoreAppCurrent)-browser + $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-unix;$(NetCoreAppCurrent)-browser;$(NetCoreAppCurrent)-wasi true true + true diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TcpClientTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TcpClientTest.cs index 6a86b2c5fabf92..609e215fea88d7 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TcpClientTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TcpClientTest.cs @@ -38,7 +38,7 @@ public void Ctor_InvalidArguments_Throws() AssertExtensions.Throws("port", () => new TcpClient("localhost", -1)); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void Connect_InvalidArguments_Throws() { using (var client = new TcpClient()) @@ -53,6 +53,21 @@ public void Connect_InvalidArguments_Throws() } } + [Fact] + public async Task ConnectAsync_InvalidArguments_Throws() + { + using (var client = new TcpClient()) + { + await AssertExtensions.ThrowsAsync("host", () => client.ConnectAsync((string)null, 0)); + await AssertExtensions.ThrowsAsync("port", () => client.ConnectAsync("localhost", -1)); + + await AssertExtensions.ThrowsAsync("address", () => client.ConnectAsync((IPAddress)null, 0)); + await AssertExtensions.ThrowsAsync("port", () => client.ConnectAsync(IPAddress.Loopback, -1)); + + await AssertExtensions.ThrowsAsync("remoteEP", () => client.ConnectAsync(null)); + } + } + [Fact] public void GetStream_NotConnected_Throws() { @@ -62,7 +77,7 @@ public void GetStream_NotConnected_Throws() } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void Active_Roundtrips() { using (var client = new DerivedTcpClient()) @@ -78,7 +93,7 @@ public void Active_Roundtrips() } } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [InlineData(false)] [InlineData(true)] public void DisposeClose_OperationsThrow(bool close) @@ -198,7 +213,7 @@ public async Task ConnectAsync_DnsEndPoint_Success(int mode) } [OuterLoop] - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [InlineData(0)] [InlineData(1)] [InlineData(2)] @@ -454,7 +469,7 @@ public async Task Dispose_CancelsConnectAsync(bool connectByName) } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void Connect_Dual_Success() { if (!Socket.OSSupportsIPv6) @@ -481,7 +496,7 @@ public void Connect_Dual_Success() } } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [InlineData(false, "::ffff:127.0.0.1")] [InlineData(false, "127.0.0.1")] [InlineData(false, "localhost")] diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TcpListenerTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TcpListenerTest.cs index 2e47f057a8d8c0..a328b292754157 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TcpListenerTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TcpListenerTest.cs @@ -101,7 +101,7 @@ public void Start_InvalidArgs_Throws() listener.Stop(); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public async Task Pending_TrueWhenWaitingRequest() { var listener = new TcpListener(IPAddress.Loopback, 0); @@ -120,7 +120,7 @@ public async Task Pending_TrueWhenWaitingRequest() Assert.Throws(() => listener.Pending()); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void Accept_Invalid_Throws() { var listener = new TcpListener(IPAddress.Loopback, 0); @@ -145,8 +145,15 @@ public void Accept_Invalid_Throws() [InlineData(2)] // Async with Cancellation [InlineData(3)] // APM [ActiveIssue("https://github.com/dotnet/runtime/issues/51392", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] public async Task Accept_AcceptsPendingSocketOrClient(int mode) { + if (OperatingSystem.IsWasi() && (mode == 0 || mode == 3)) + { + // Sync and APM are not supported on WASI + return; + } + var listener = new TcpListener(IPAddress.Loopback, 0); listener.Start(); @@ -204,6 +211,7 @@ public void IPv6_Only_Works() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] public async Task Accept_StartAfterStop_AcceptsSuccessfully() { var listener = new TcpListener(IPAddress.Loopback, 0); @@ -240,6 +248,7 @@ public void ExclusiveAddressUse_ListenerNotStarted_SetAndReadSuccessfully() } [Fact] + [SkipOnPlatform(TestPlatforms.Wasi, "In wasi-libc ExclusiveAddressUse is emulated by fake SO_REUSEADDR")] public void ExclusiveAddressUse_SetStartListenerThenRead_ReadSuccessfully() { var listener = new TcpListener(IPAddress.Loopback, 0); @@ -254,6 +263,7 @@ public void ExclusiveAddressUse_SetStartListenerThenRead_ReadSuccessfully() } [Fact] + [SkipOnPlatform(TestPlatforms.Wasi, "In wasi-libc ExclusiveAddressUse is emulated by fake SO_REUSEADDR")] public void ExclusiveAddressUse_SetStartAndStopListenerThenRead_ReadSuccessfully() { var listener = new TcpListener(IPAddress.Loopback, 0); @@ -271,7 +281,7 @@ public void ExclusiveAddressUse_SetStartAndStopListenerThenRead_ReadSuccessfully Assert.True(listener.ExclusiveAddressUse); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void EndAcceptSocket_WhenStopped_ThrowsObjectDisposedException() { var listener = new TcpListener(IPAddress.Loopback, 0); @@ -286,7 +296,7 @@ public void EndAcceptSocket_WhenStopped_ThrowsObjectDisposedException() Assert.Throws(() => listener.EndAcceptSocket(iar)); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void EndAcceptTcpClient_WhenStopped_ThrowsObjectDisposedException() { var listener = new TcpListener(IPAddress.Loopback, 0); diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs index a57d61c110707f..69f61fc180a49c 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs @@ -47,6 +47,7 @@ public TelemetryTest(ITestOutputHelper output) } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/107981", TestPlatforms.Wasi)] public static void EventSource_ExistsWithCorrectId() { Type esType = typeof(Socket).Assembly.GetType("System.Net.Sockets.SocketsTelemetry", throwOnError: true, ignoreCase: false); @@ -60,9 +61,9 @@ public static void EventSource_ExistsWithCorrectId() public static IEnumerable SocketMethods_MemberData() { - yield return new[] { "Sync" }; + if (!OperatingSystem.IsWasi()) yield return new[] { "Sync" }; yield return new[] { "Task" }; - yield return new[] { "Apm" }; + if (!OperatingSystem.IsWasi()) yield return new[] { "Apm" }; yield return new[] { "Eap" }; } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TimeoutTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TimeoutTest.cs index a442af692e5a70..60cadec1d802d4 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TimeoutTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TimeoutTest.cs @@ -7,6 +7,7 @@ namespace System.Net.Sockets.Tests { [Collection(nameof(DisableParallelization))] + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support Timeout yet")] // see https://github.com/WebAssembly/wasi-libc/issues/539 public class TimeoutTest { [Fact] diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs index 4b8db5e71d6d2f..cd5a209dfa5b4b 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UdpClientTest.cs @@ -60,7 +60,7 @@ public void Ctor_NullEndpoint_Throws() AssertExtensions.Throws("localEP", () => new UdpClient(null)); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void Ctor_CanSend() { using (var udpClient = new DerivedUdpClient()) @@ -71,6 +71,16 @@ public void Ctor_CanSend() } [Fact] + public async Task Ctor_CanSendAsync() + { + using (var udpClient = new DerivedUdpClient()) + { + Assert.Equal(1, await udpClient.SendAsync(new byte[1], 1, new IPEndPoint(IPAddress.Loopback, UnusedPort))); + Assert.False(udpClient.Active); + } + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void Ctor_Int_CanSend() { try @@ -87,7 +97,7 @@ public void Ctor_Int_CanSend() } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void Ctor_IntAddressFamily_IPv4_CanSend() { try @@ -104,7 +114,7 @@ public void Ctor_IntAddressFamily_IPv4_CanSend() } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void Ctor_IntAddressFamily_IPv6_CanSend() { try @@ -121,7 +131,7 @@ public void Ctor_IntAddressFamily_IPv6_CanSend() } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void Ctor_IPEndPoint_CanSend() { try @@ -138,7 +148,7 @@ public void Ctor_IPEndPoint_CanSend() } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void Ctor_StringInt_CanSend() { using (var udpClient = new DerivedUdpClient("localhost", UnusedPort)) @@ -148,7 +158,7 @@ public void Ctor_StringInt_CanSend() } } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [InlineData(false)] [InlineData(true)] public void DisposeClose_OperationsThrow(bool close) @@ -267,6 +277,7 @@ public void Ttl_Roundtrips() [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/51392", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + [SkipOnPlatform(TestPlatforms.Wasi, "Not supported on Wasi.")] public void DontFragment_Roundtrips() { using (var udpClient = new UdpClient()) @@ -282,6 +293,7 @@ public void DontFragment_Roundtrips() [Theory] [InlineData(AddressFamily.InterNetwork)] [InlineData(AddressFamily.InterNetworkV6)] + [SkipOnPlatform(TestPlatforms.Wasi, "Not supported on Wasi.")] public void MulticastLoopback_Roundtrips(AddressFamily addressFamily) { using (var udpClient = new UdpClient(addressFamily)) @@ -295,6 +307,7 @@ public void MulticastLoopback_Roundtrips(AddressFamily addressFamily) } [Fact] + [SkipOnPlatform(TestPlatforms.Wasi, "Not supported on Wasi.")] public void EnableBroadcast_Roundtrips() { using (var udpClient = new UdpClient()) @@ -321,7 +334,7 @@ public void ExclusiveAddressUse_Roundtrips() } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void InvalidArguments_Throw() { using (var udpClient = new UdpClient("localhost", UnusedPort)) @@ -332,7 +345,7 @@ public void InvalidArguments_Throw() } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void BeginSend_NegativeBytes_Throws() { using (UdpClient udpClient = new UdpClient(AddressFamily.InterNetwork)) @@ -347,7 +360,7 @@ public void BeginSend_NegativeBytes_Throws() } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void BeginSend_BytesMoreThanArrayLength_Throws() { using (UdpClient udpClient = new UdpClient(AddressFamily.InterNetwork)) @@ -362,7 +375,7 @@ public void BeginSend_BytesMoreThanArrayLength_Throws() } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void BeginSend_AsyncOperationCompletes_Success() { using (UdpClient udpClient = new UdpClient()) @@ -376,7 +389,7 @@ public void BeginSend_AsyncOperationCompletes_Success() } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void Send_InvalidArguments_Throws() { using (var udpClient = new DerivedUdpClient()) @@ -389,8 +402,6 @@ public void Send_InvalidArguments_Throws() udpClient.Active = true; Assert.Throws(() => udpClient.Send(new byte[1], 1, new IPEndPoint(IPAddress.Loopback, 0))); Assert.Throws(() => udpClient.Send(new ReadOnlySpan(new byte[1]), new IPEndPoint(IPAddress.Loopback, 0))); - Assert.Throws(() => {udpClient.SendAsync(new byte[1], 1, new IPEndPoint(IPAddress.Loopback, 0));}); - Assert.Throws(() => udpClient.SendAsync(new ReadOnlyMemory(new byte[1]), new IPEndPoint(IPAddress.Loopback, 0))); } } @@ -423,7 +434,7 @@ public void Client_Idempotent() } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void Connect_InvalidArguments_Throws() { using (var udpClient = new UdpClient()) @@ -458,7 +469,7 @@ public async Task ConnectAsync_IPAddressHost_Success() } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void Connect_StringHost_Success() { using (var c = new UdpClient()) @@ -467,7 +478,7 @@ public void Connect_StringHost_Success() } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void Connect_IPAddressHost_Success() { using (var c = new UdpClient()) @@ -508,7 +519,7 @@ public void AllowNatTraversal_AnyUnix(bool allow) } } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [InlineData(false)] [InlineData(true)] public void Send_Receive_Success(bool ipv4) @@ -572,7 +583,7 @@ private static void AssertReceive(UdpClient receiver, byte[] sentData) Assert.True(Enumerable.SequenceEqual(sentData, data)); } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [InlineData(false)] [InlineData(true)] public void Send_Available_Success(bool ipv4) @@ -588,7 +599,7 @@ public void Send_Available_Success(bool ipv4) } } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [InlineData(false)] [InlineData(true)] public void BeginEndSend_BeginEndReceive_Success(bool ipv4) @@ -752,6 +763,7 @@ public async Task SendAsync_PreCanceled_Throws(bool ipv4) } [Fact] + [SkipOnPlatform(TestPlatforms.Wasi, "Not supported on Wasi.")] public void JoinDropMulticastGroup_InvalidArguments_Throws() { using (var udpClient = new UdpClient(AddressFamily.InterNetwork)) @@ -800,6 +812,7 @@ public void UdpReceiveResult_Equality() } [Fact] + [SkipOnPlatform(TestPlatforms.Wasi, "Not supported on Wasi.")] public void BeginSend_IPv6Socket_IPv4Dns_Success() { using (var receiver = new UdpClient("127.0.0.1", DiscardPort)) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs index a49b8aa235a4ee..7cb10777d81aa6 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs @@ -15,6 +15,7 @@ namespace System.Net.Sockets.Tests { + [SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support UnixDomainSocket")] public class UnixDomainSocketTest { private readonly ITestOutputHelper _log; diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.LibraryBuild.WASI.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.LibraryBuild.WASI.xml index 435d0ef7bdfc70..184012c1e4e644 100644 --- a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.LibraryBuild.WASI.xml +++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.LibraryBuild.WASI.xml @@ -4,6 +4,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs index ef2038c86aa84d..285b85485decbf 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs @@ -6,6 +6,7 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; using System.Threading.Tasks; +using System.Collections.Generic; namespace System.Threading { @@ -18,6 +19,11 @@ internal static System.Threading.Tasks.Task RegisterWasiPollableHandle(int handl return WasiEventLoop.RegisterWasiPollableHandle(handle, ownsPollable, cancellationToken); } + internal static void RegisterWasiPollHook(object? state, Func> beforePollHook, Action onResolveCallback, CancellationToken cancellationToken) + { + WasiEventLoop.RegisterWasiPollHook(state, beforePollHook, onResolveCallback, cancellationToken); + } + internal static T PollWasiEventLoopUntilResolved(Task mainTask) { return WasiEventLoop.PollWasiEventLoopUntilResolved(mainTask); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs index 5034e23dd011d8..d89fa4f34e65e9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs @@ -15,6 +15,7 @@ internal static class WasiEventLoop // it will be leaked and stay in this list forever. // it will also keep the Pollable handle alive and prevent it from being disposed private static readonly List s_pollables = new(); + private static readonly List s_hooks = new(); private static bool s_checkScheduled; private static Pollable? s_resolvedPollable; private static Task? s_mainTask; @@ -38,6 +39,15 @@ internal static Task RegisterWasiPollable(Pollable pollable, bool ownsPollable, return holder.taskCompletionSource.Task; } + internal static void RegisterWasiPollHook(object? state, Func> beforePollHook, Action onResolveCallback, CancellationToken cancellationToken) + { + // this will register the poll hook into s_hooks + var holder = new PollHook(state, beforePollHook, onResolveCallback, cancellationToken); + s_hooks.Add(holder); + + ScheduleCheck(); + } + internal static T PollWasiEventLoopUntilResolved(Task mainTask) { @@ -86,7 +96,7 @@ internal static void PollWasiEventLoopUntilResolvedVoid(Task mainTask) internal static void ScheduleCheck() { - if (!s_checkScheduled && s_pollables.Count > 0) + if (!s_checkScheduled && (s_pollables.Count > 0 || s_hooks.Count > 0)) { s_checkScheduled = true; ThreadPool.UnsafeQueueUserWorkItem(CheckPollables, null); @@ -98,6 +108,7 @@ internal static void CheckPollables(object? _) s_checkScheduled = false; var holders = new List(s_pollables.Count); + var hooks = new List(s_pollables.Count); var pending = new List(s_pollables.Count); for (int i = 0; i < s_pollables.Count; i++) { @@ -108,8 +119,24 @@ internal static void CheckPollables(object? _) pending.Add(holder.pollable); } } + for (int i = 0; i < s_hooks.Count; i++) + { + var hook = s_hooks[i]; + if (!hook.isDisposed) + { + var handles = hook.BeforePollHook(hook.State); + for (int h = 0; h < handles.Count; h++) + { + var handle = handles[h]; + var pollableCpy = new Pollable(new Pollable.THandle(handle)); + hooks.Add(hook); + pending.Add(pollableCpy); + } + } + } s_pollables.Clear(); + s_hooks.Clear(); if (pending.Count > 0) { @@ -124,15 +151,23 @@ internal static void CheckPollables(object? _) pending.Add(s_resolvedPollable); } + // this could block, this is blocking WASI API call var readyIndexes = PollInterop.Poll(pending); + + var holdersCount = holders.Count; for (int i = 0; i < readyIndexes.Length; i++) { uint readyIndex = readyIndexes[i]; - if (resolvedPollableIndex != readyIndex) + if (readyIndex < holdersCount) { var holder = holders[(int)readyIndex]; holder.ResolveAndDispose(); } + else if (resolvedPollableIndex != readyIndex) + { + var hook = hooks[(int)readyIndex-holdersCount]; + hook.Resolve(); + } } for (int i = 0; i < holders.Count; i++) @@ -143,9 +178,18 @@ internal static void CheckPollables(object? _) s_pollables.Add(holder); } } + } - ScheduleCheck(); + for (int i = 0; i < hooks.Count; i++) + { + PollHook hook = hooks[i]; + if (!hook.isDisposed) + { + s_hooks.Add(hook); + } } + + ScheduleCheck(); } private sealed class PollableHolder @@ -205,5 +249,42 @@ public static void CancelAndDispose(object? s) self.taskCompletionSource.TrySetCanceled(self.cancellationToken); } } + + private sealed class PollHook + { + public bool isDisposed; + public readonly object? State; + public readonly Func> BeforePollHook; + public readonly Action OnResolveCallback; + public readonly CancellationTokenRegistration cancellationTokenRegistration; + + public PollHook(object? state, Func> beforePollHook, Action onResolveCallback, CancellationToken cancellationToken) + { + this.State = state; + this.BeforePollHook = beforePollHook; + this.OnResolveCallback = onResolveCallback; + + // static method is used to avoid allocating a delegate + cancellationTokenRegistration = cancellationToken.Register(Dispose, this); + } + + public void Resolve() + { + OnResolveCallback(State); + } + + public static void Dispose(object? s) + { + PollHook self = (PollHook)s!; + if (self.isDisposed) + { + return; + } + + // it will be removed from s_hooks on the next run + self.isDisposed = true; + self.cancellationTokenRegistration.Dispose(); + } + } } } diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index e412063ebd50bc..a9990bfe8669dd 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -587,6 +587,7 @@ + diff --git a/src/mono/sample/wasi/Directory.Build.targets b/src/mono/sample/wasi/Directory.Build.targets index 90f89953129d78..e148ab91a59ced 100644 --- a/src/mono/sample/wasi/Directory.Build.targets +++ b/src/mono/sample/wasi/Directory.Build.targets @@ -39,6 +39,7 @@ $(WasiCommand) --wasi http + $(WasiCommand) --wasi inherit-network --wasi tcp --wasi udp --wasi allow-ip-name-lookup $(WasiCommand) --dir . $(WasiCommand) $(_DotnetWasmName) $(WasiCommand) $(_SampleProjectName) diff --git a/src/mono/sample/wasi/sockets-p2/Program.cs b/src/mono/sample/wasi/sockets-p2/Program.cs new file mode 100644 index 00000000000000..44af1d475d61df --- /dev/null +++ b/src/mono/sample/wasi/sockets-p2/Program.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Net.Http.Headers; +using System.Net.Http; +using System.Threading.Tasks; +using System.Threading; +using System.Runtime.CompilerServices; +using System.Net; +using System.Net.Sockets; +using System.Text; + +public static class WasiMainWrapper +{ + public static async Task MainAsync(string[] args) + { + IPHostEntry ipHostInfo = await Dns.GetHostEntryAsync("example.com"); + IPAddress ipAddress = ipHostInfo.AddressList[0]; + Console.WriteLine($"IP Address: {ipAddress}"); + + IPEndPoint ipEndPoint = new(ipAddress, 80); + using Socket client = new(ipEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + + await client.ConnectAsync(ipEndPoint); + + // Send message. + var message = @"GET / HTTP/1.1 +Host: example.com +Accept: */* + +"; + var messageBytes = Encoding.UTF8.GetBytes(message); + var start = 0; + while (start < messageBytes.Length) + { + start += await client.SendAsync(messageBytes.AsMemory(start), SocketFlags.None); + } + + // Receive ack. + var buffer = new byte[2048]; + var received = await client.ReceiveAsync(buffer, SocketFlags.None); + var response = Encoding.UTF8.GetString(buffer, 0, received); + Console.WriteLine(response); + + client.Shutdown(SocketShutdown.Both); + + return 0; + } + + public static int Main(string[] args) + { + return PollWasiEventLoopUntilResolved((Thread)null!, MainAsync(args)); + + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "PollWasiEventLoopUntilResolved")] + static extern T PollWasiEventLoopUntilResolved(Thread t, Task mainTask); + } + +} diff --git a/src/mono/sample/wasi/sockets-p2/Wasip2.Sockets.Console.Sample.csproj b/src/mono/sample/wasi/sockets-p2/Wasip2.Sockets.Console.Sample.csproj new file mode 100644 index 00000000000000..9f14b7b45051c0 --- /dev/null +++ b/src/mono/sample/wasi/sockets-p2/Wasip2.Sockets.Console.Sample.csproj @@ -0,0 +1,13 @@ + + + $(NetCoreAppCurrent) + <_WasiNeedsHttp>true + <_WasiNeedsSocket>true + + + + + diff --git a/src/native/libs/System.Native/entrypoints.c b/src/native/libs/System.Native/entrypoints.c index 2165430d65f56a..fcde21046a7c4b 100644 --- a/src/native/libs/System.Native/entrypoints.c +++ b/src/native/libs/System.Native/entrypoints.c @@ -187,6 +187,7 @@ static const Entry s_sysNative[] = DllImportEntry(SystemNative_FreeSocketEventBuffer) DllImportEntry(SystemNative_TryChangeSocketEventRegistration) DllImportEntry(SystemNative_WaitForSocketEvents) + DllImportEntry(SystemNative_GetWasiSocketDescriptor) DllImportEntry(SystemNative_PlatformSupportsDualModeIPv4PacketInfo) DllImportEntry(SystemNative_GetDomainSocketSizes) DllImportEntry(SystemNative_GetMaximumAddressSize) diff --git a/src/native/libs/System.Native/pal_networking.c b/src/native/libs/System.Native/pal_networking.c index 82e3960e6fc335..3efd4f7ae4f963 100644 --- a/src/native/libs/System.Native/pal_networking.c +++ b/src/native/libs/System.Native/pal_networking.c @@ -1054,7 +1054,6 @@ static struct cmsghdr* GET_CMSG_NXTHDR(struct msghdr* mhdr, struct cmsghdr* cmsg #pragma clang diagnostic pop #endif } -#endif // CMSG_SPACE int32_t SystemNative_TryGetIPPacketInformation(MessageHeader* messageHeader, int32_t isIPv4, IPPacketInformation* packetInfo) @@ -1064,7 +1063,6 @@ SystemNative_TryGetIPPacketInformation(MessageHeader* messageHeader, int32_t isI return 0; } -#if defined(CMSG_SPACE) struct msghdr header; ConvertMessageHeaderToMsghdr(&header, messageHeader, -1); @@ -1093,13 +1091,35 @@ SystemNative_TryGetIPPacketInformation(MessageHeader* messageHeader, int32_t isI } return 0; -#else // CMSG_SPACE - (void)messageHeader; - (void)isIPv4; - (void)packetInfo; - return Error_ENOTSUP; -#endif // CMSG_SPACE } +#else // !CMSG_SPACE +int32_t +SystemNative_TryGetIPPacketInformation(MessageHeader* messageHeader, int32_t isIPv4, IPPacketInformation* packetInfo) +{ + if (messageHeader == NULL || packetInfo == NULL) + { + return 0; + } + + if (isIPv4 != 0) + { + struct sockaddr_in* inetSockAddr = (struct sockaddr_in*)messageHeader->SocketAddress; + + ConvertInAddrToByteArray(&packetInfo->Address.Address[0], NUM_BYTES_IN_IPV4_ADDRESS, &inetSockAddr->sin_addr); + packetInfo->Address.IsIPv6 = 0; + } + else + { + struct sockaddr_in6* inet6SockAddr = (struct sockaddr_in6*)messageHeader->SocketAddress; + + ConvertIn6AddrToByteArray(&packetInfo->Address.Address[0], NUM_BYTES_IN_IPV6_ADDRESS, &inet6SockAddr->sin6_addr); + packetInfo->Address.IsIPv6 = 1; + packetInfo->Address.ScopeId = inet6SockAddr->sin6_scope_id; + } + packetInfo->InterfaceIndex = 0; + return 1; +} +#endif // !CMSG_SPACE static int8_t GetMulticastOptionName(int32_t multicastOption, int8_t isIPv6, int* optionName) { @@ -1554,9 +1574,10 @@ int32_t SystemNative_ReceiveMessage(intptr_t socket, MessageHeader* messageHeade ssize_t res; #if !defined(CMSG_SPACE) - // TODO https://github.com/dotnet/runtime/issues/98957 - return Error_ENOTSUP; -#else // !CMSG_SPACE + // we will only use 0th buffer + struct iovec* msg_iov = (struct iovec*)messageHeader->IOVectors; + while ((res = recvfrom(fd, msg_iov[0].iov_base, msg_iov[0].iov_len, socketFlags, (sockaddr *)messageHeader->SocketAddress, (socklen_t*) &(messageHeader->SocketAddressLen))) < 0 && errno == EINTR); +#else // CMSG_SPACE struct msghdr header; ConvertMessageHeaderToMsghdr(&header, messageHeader, fd); @@ -1572,6 +1593,7 @@ int32_t SystemNative_ReceiveMessage(intptr_t socket, MessageHeader* messageHeade messageHeader->ControlBufferLen = Min((int32_t)header.msg_controllen, messageHeader->ControlBufferLen); messageHeader->Flags = ConvertSocketFlagsPlatformToPal(header.msg_flags); +#endif // CMSG_SPACE if (res != -1) { @@ -1581,7 +1603,6 @@ int32_t SystemNative_ReceiveMessage(intptr_t socket, MessageHeader* messageHeade *received = 0; return SystemNative_ConvertErrorPlatformToPal(errno); -#endif // !CMSG_SPACE } int32_t SystemNative_Send(intptr_t socket, void* buffer, int32_t bufferLen, int32_t flags, int32_t* sent) @@ -1635,11 +1656,8 @@ int32_t SystemNative_SendMessage(intptr_t socket, MessageHeader* messageHeader, return Error_ENOTSUP; } -#if !defined(CMSG_SPACE) - // TODO https://github.com/dotnet/runtime/issues/98957 - return Error_ENOTSUP; -#else // !CMSG_SPACE ssize_t res; +#if defined(CMSG_SPACE) struct msghdr header; ConvertMessageHeaderToMsghdr(&header, messageHeader, fd); @@ -1652,6 +1670,12 @@ int32_t SystemNative_SendMessage(intptr_t socket, MessageHeader* messageHeader, #else while ((res = sendmsg(fd, &header, socketFlags)) < 0 && errno == EINTR); #endif +#else // CMSG_SPACE + // we will only use 0th buffer + struct iovec* msg_iov = (struct iovec*)messageHeader->IOVectors; + while ((res = sendto(fd, msg_iov[0].iov_base, msg_iov[0].iov_len, socketFlags, (sockaddr *)messageHeader->SocketAddress, (socklen_t)messageHeader->SocketAddressLen)) < 0 && errno == EINTR); +#endif // CMSG_SPACE + if (res != -1) { *sent = res; @@ -1660,7 +1684,6 @@ int32_t SystemNative_SendMessage(intptr_t socket, MessageHeader* messageHeader, *sent = 0; return SystemNative_ConvertErrorPlatformToPal(errno); -#endif // !CMSG_SPACE } int32_t SystemNative_Accept(intptr_t socket, uint8_t* socketAddress, int32_t* socketAddressLen, intptr_t* acceptedSocket) @@ -1675,7 +1698,11 @@ int32_t SystemNative_Accept(intptr_t socket, uint8_t* socketAddress, int32_t* so socklen_t addrLen = (socklen_t)*socketAddressLen; int accepted; #if HAVE_ACCEPT4 && defined(SOCK_CLOEXEC) +#if defined(TARGET_WASI) // WASI is always FD_CLOEXEC and we always need SOCK_NONBLOCK. SOCK_CLOEXEC doesn't make sense in WASI. + while ((accepted = accept4(fd, (struct sockaddr*)socketAddress, &addrLen, SOCK_NONBLOCK)) < 0 && errno == EINTR); +#else // !TARGET_WASI while ((accepted = accept4(fd, (struct sockaddr*)socketAddress, &addrLen, SOCK_CLOEXEC)) < 0 && errno == EINTR); +#endif // !TARGET_WASI #else while ((accepted = accept(fd, (struct sockaddr*)socketAddress, &addrLen)) < 0 && errno == EINTR); #if defined(FD_CLOEXEC) @@ -1690,7 +1717,6 @@ int32_t SystemNative_Accept(intptr_t socket, uint8_t* socketAddress, int32_t* so errno = oldErrno; } #endif -#endif #if !defined(__linux__) // On macOS and FreeBSD new socket inherits flags from accepting fd. // Our socket code expects new socket to be in blocking mode by default. @@ -1701,6 +1727,7 @@ int32_t SystemNative_Accept(intptr_t socket, uint8_t* socketAddress, int32_t* so accepted = -1; errno = oldErrno; } +#endif #endif if (accepted == -1) { @@ -2221,7 +2248,11 @@ int32_t SystemNative_GetSockOpt( socklen_t optLen = (socklen_t)*optionLen; // On Unix, SO_REUSEPORT controls the ability to bind multiple sockets to the same address. int err = getsockopt(fd, SOL_SOCKET, SO_REUSEPORT, optionValue, &optLen); - +#elif defined(SO_REUSEADDR) + socklen_t optLen = (socklen_t)*optionLen; + int err = getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, optionValue, &optLen); +#endif +#if defined(SO_REUSEPORT) || defined(SO_REUSEADDR) if (err != 0) { return SystemNative_ConvertErrorPlatformToPal(errno); @@ -2238,7 +2269,7 @@ int32_t SystemNative_GetSockOpt( value = value == 0 ? 1 : 0; } *(int32_t*)optionValue = value; -#else // !SO_REUSEPORT +#else // !SO_REUSEPORT !SO_REUSEADDR *optionValue = 0; #endif return Error_SUCCESS; @@ -2358,7 +2389,6 @@ SystemNative_SetSockOpt(intptr_t socket, int32_t socketOptionLevel, int32_t sock // We make both SocketOptionName_SO_REUSEADDR and SocketOptionName_SO_EXCLUSIVEADDRUSE control SO_REUSEPORT/SO_REUSEADDR. if (socketOptionName == SocketOptionName_SO_EXCLUSIVEADDRUSE || socketOptionName == SocketOptionName_SO_REUSEADDR) { -#ifdef SO_REUSEPORT if (optionLen != sizeof(int32_t)) { return Error_EINVAL; @@ -2379,6 +2409,7 @@ SystemNative_SetSockOpt(intptr_t socket, int32_t socketOptionLevel, int32_t sock } } +#ifdef SO_REUSEPORT // An application that sets SO_REUSEPORT/SO_REUSEADDR can reuse the endpoint with another // application that sets the same option. If one application sets SO_REUSEPORT and another // sets SO_REUSEADDR the second application will fail to bind. We set both options, this @@ -2389,7 +2420,10 @@ SystemNative_SetSockOpt(intptr_t socket, int32_t socketOptionLevel, int32_t sock err = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &value, (socklen_t)optionLen); } return err == 0 ? Error_SUCCESS : SystemNative_ConvertErrorPlatformToPal(errno); -#else // !SO_REUSEPORT +#elif defined(SO_REUSEADDR) + int err = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &value, (socklen_t)optionLen); + return err == 0 ? Error_SUCCESS : SystemNative_ConvertErrorPlatformToPal(errno); +#else // !SO_REUSEPORT !SO_REUSEADDR return Error_SUCCESS; #endif } @@ -2778,6 +2812,9 @@ int32_t SystemNative_Socket(int32_t addressFamily, int32_t socketType, int32_t p #ifdef SOCK_CLOEXEC platformSocketType |= SOCK_CLOEXEC; +#endif +#if defined(TARGET_WASI) + platformSocketType |= SOCK_NONBLOCK; // WASI sockets are always non-blocking, because in ST we don't have another thread which could be blocked #endif *createdSocket = socket(platformAddressFamily, platformSocketType, platformProtocolType); if (*createdSocket == -1) @@ -2841,6 +2878,11 @@ int32_t SystemNative_GetSocketType(intptr_t socket, int32_t* addressFamily, int3 !TryConvertSocketTypePlatformToPal(typeValue, socketType)) #endif { +#if defined(TARGET_WASI) + if (errno == EBADF){ + return Error_ENOTSOCK; + } +#endif // TARGET_WASI *socketType = SocketType_UNKNOWN; } @@ -3303,7 +3345,8 @@ static int32_t WaitForSocketEventsInner(int32_t port, SocketEvent* buffer, int32 return Error_SUCCESS; } -#else +#else // !HAVE_KQUEUE !HAVE_EPOLL + static const size_t SocketEventBufferElementSize = 0; static int32_t CloseSocketEventPortInner(int32_t port) @@ -3324,8 +3367,37 @@ static int32_t WaitForSocketEventsInner(int32_t port, SocketEvent* buffer, int32 { return Error_ENOSYS; } +#endif // !HAVE_KQUEUE !HAVE_EPOLL -#endif +#if defined(TARGET_WASI) +// from https://github.com/WebAssembly/wasi-libc/blob/230d4be6c54bec93181050f9e25c87150506bdd0/libc-bottom-half/headers/private/wasi/descriptor_table.h +bool descriptor_table_get_ref(int fd, void **entry); + +// this method is invading private implementation details of wasi-libc +// we could get rid of it when https://github.com/WebAssembly/wasi-libc/issues/542 is resolved +// or after WASIp3 promises are implemented, whatever comes first +int32_t SystemNative_GetWasiSocketDescriptor(intptr_t socket, void** entry) +{ + if (entry == NULL) + { + return Error_EFAULT; + } + + int fd = ToFileDescriptor(socket); + if(!descriptor_table_get_ref(fd, entry)) + { + return Error_EFAULT; + } + return Error_SUCCESS; +} +#else +int32_t SystemNative_GetWasiSocketDescriptor(intptr_t socket, void** entry) +{ + (void)socket; + (void)entry; + return Error_ENOSYS; +} +#endif // TARGET_WASI int32_t SystemNative_CreateSocketEventPort(intptr_t* port) { diff --git a/src/native/libs/System.Native/pal_networking.h b/src/native/libs/System.Native/pal_networking.h index 8044ce00b02664..c393a3dbad4619 100644 --- a/src/native/libs/System.Native/pal_networking.h +++ b/src/native/libs/System.Native/pal_networking.h @@ -404,6 +404,8 @@ PALEXPORT int32_t SystemNative_GetAtOutOfBandMark(intptr_t socket, int32_t* avai PALEXPORT int32_t SystemNative_GetBytesAvailable(intptr_t socket, int32_t* available); +PALEXPORT int32_t SystemNative_GetWasiSocketDescriptor(intptr_t socket, void** entry); + PALEXPORT int32_t SystemNative_CreateSocketEventPort(intptr_t* port); PALEXPORT int32_t SystemNative_CloseSocketEventPort(intptr_t port);