Skip to content

Commit

Permalink
[WASI] sockets (dotnet#106977)
Browse files Browse the repository at this point in the history
  • Loading branch information
pavelsavara authored and lambdageek committed Oct 3, 2024
1 parent 3ee8a33 commit 505e8ba
Show file tree
Hide file tree
Showing 74 changed files with 1,624 additions and 240 deletions.
2 changes: 2 additions & 0 deletions eng/testing/tests.wasi.targets
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
<_XHarnessArgs Condition="'$(WasmXHarnessTestsTimeout)' != ''" >$(_XHarnessArgs) &quot;--timeout=$(WasmXHarnessTestsTimeout)&quot;</_XHarnessArgs>
<_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasi --engine-arg=http</_XHarnessArgs>
<_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasi --engine-arg=inherit-network</_XHarnessArgs>
<_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasi --engine-arg=tcp</_XHarnessArgs>
<_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasi --engine-arg=udp</_XHarnessArgs>
<_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasi --engine-arg=allow-ip-name-lookup</_XHarnessArgs>
<_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--env --engine-arg=DOTNET_WASI_PRINT_EXIT_CODE=1</_XHarnessArgs>
<_XHarnessArgs Condition="'$(WasmXHarnessArgsCli)' != ''" >$(_XHarnessArgs) $(WasmXHarnessArgsCli)</_XHarnessArgs>
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

namespace System.IO.Pipes.Tests
{
[SkipOnPlatform(TestPlatforms.Wasi, "Wasi doesn't support UnixDomain")]
public class NamedPipeTest_UnixDomainSockets
{
[Fact]
Expand Down
3 changes: 1 addition & 2 deletions src/libraries/System.Net.Sockets/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
<PropertyGroup>
<StrongNameKeyId>Microsoft</StrongNameKeyId>
<IncludePlatformAttributes>true</IncludePlatformAttributes>
<!-- WASI until https://github.com/dotnet/runtime/issues/98957 -->
<UnsupportedOSPlatforms>browser;wasi</UnsupportedOSPlatforms>
<UnsupportedOSPlatforms>browser</UnsupportedOSPlatforms>
</PropertyGroup>
</Project>
24 changes: 18 additions & 6 deletions src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-unix;$(NetCoreAppCurrent)-osx;$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent)</TargetFrameworks>
<TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-unix;$(NetCoreAppCurrent)-osx;$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent)-wasi;$(NetCoreAppCurrent)</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<!-- SYSTEM_NET_SOCKETS_DLL is required to allow source-level code sharing for types defined within the
System.Net.Internals namespace. -->
Expand Down Expand Up @@ -48,7 +48,7 @@
<Compile Include="System\Net\Sockets\TransmitFileOptions.cs" />
<Compile Include="System\Net\Sockets\UDPClient.cs" />
<Compile Include="System\Net\Sockets\UdpReceiveResult.cs" />
<Compile Include="System\Net\Sockets\UnixDomainSocketEndPoint.cs" />
<Compile Include="System\Net\Sockets\UnixDomainSocketEndPoint.cs" Condition="'$(TargetPlatformIdentifier)' != 'wasi'"/>
<!-- Common sources -->
<Compile Include="$(CommonPath)DisableRuntimeMarshalling.cs"
Link="Common\DisableRuntimeMarshalling.cs" />
Expand Down Expand Up @@ -183,14 +183,28 @@
Link="Common\System\Net\CompletionPortHelper.Windows.cs" />
</ItemGroup>

<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'wasi'">
<Compile Include="System\Net\Sockets\UnixDomainSocketEndPoint.Wasi.cs" />
<Compile Include="System\Net\Sockets\SocketAsyncContext.Wasi.cs" />
<Compile Include="System\Net\Sockets\SocketAsyncEngine.Wasi.cs"/>
<Compile Include="System\Net\Sockets\SocketPal.Wasi.cs" />
<Compile Include="$(CommonPath)Interop\Wasi\System.Native\Interop.SocketEvent.cs"
Link="Common\Interop\Wasi\System.Native\Interop.SocketEvent.cs" />
</ItemGroup>

<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'unix' or '$(TargetPlatformIdentifier)' == 'osx' or '$(TargetPlatformIdentifier)' == 'ios' or '$(TargetPlatformIdentifier)' == 'tvos'">
<Compile Include="System\Net\Sockets\UnixDomainSocketEndPoint.Unix.cs"/>
<Compile Include="System\Net\Sockets\SocketAsyncEngine.Unix.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.SocketEvent.cs"
Link="Common\Interop\Unix\System.Native\Interop.SocketEvent.cs" />
</ItemGroup>

<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'unix' or '$(TargetPlatformIdentifier)' == 'wasi' or '$(TargetPlatformIdentifier)' == 'osx' or '$(TargetPlatformIdentifier)' == 'ios' or '$(TargetPlatformIdentifier)' == 'tvos'">
<Compile Include="System\Net\Sockets\SafeSocketHandle.Unix.cs" />
<Compile Include="System\Net\Sockets\Socket.Unix.cs" />
<Compile Include="System\Net\Sockets\SocketAsyncContext.Unix.cs" />
<Compile Include="System\Net\Sockets\SocketAsyncEngine.Unix.cs" />
<Compile Include="System\Net\Sockets\SocketAsyncEventArgs.Unix.cs" />
<Compile Include="System\Net\Sockets\SocketPal.Unix.cs" />
<Compile Include="System\Net\Sockets\UnixDomainSocketEndPoint.Unix.cs" />
<Compile Include="$(CommonPath)System\Net\InteropIPAddressExtensions.Unix.cs"
Link="Common\System\Net\InteropIPAddressExtensions.Unix.cs" />
<Compile Include="$(CommonPath)System\Net\SocketAddressPal.Unix.cs"
Expand Down Expand Up @@ -277,8 +291,6 @@
Link="Common\Interop\Unix\System.Native\Interop.Shutdown.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Socket.cs"
Link="Common\Interop\Unix\System.Native\Interop.Socket.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.SocketEvent.cs"
Link="Common\Interop\Unix\System.Native\Interop.SocketEvent.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.SocketAddress.cs"
Link="Common\Interop\Unix\System.Native\Interop.SocketAddress.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Pipe.cs"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public NetworkStream(Socket socket, FileAccess access, bool ownsSocket)
{
ArgumentNullException.ThrowIfNull(socket);

if (!socket.Blocking)
if (!OperatingSystem.IsWasi() && !socket.Blocking)
{
// Stream.Read*/Write* are incompatible with the semantics of non-blocking sockets, and
// allowing non-blocking sockets could result in non-deterministic failures from those
Expand Down Expand Up @@ -118,6 +118,8 @@ public override int ReadTimeout
{
get
{
if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // https://github.com/dotnet/runtime/issues/108151

int timeout = (int)_streamSocket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout)!;
if (timeout == 0)
{
Expand All @@ -127,6 +129,8 @@ public override int ReadTimeout
}
set
{
if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // https://github.com/dotnet/runtime/issues/108151

if (value <= 0 && value != System.Threading.Timeout.Infinite)
{
throw new ArgumentOutOfRangeException(nameof(value), SR.net_io_timeout_use_gt_zero);
Expand All @@ -141,6 +145,8 @@ public override int WriteTimeout
{
get
{
if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // https://github.com/dotnet/runtime/issues/108151

int timeout = (int)_streamSocket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout)!;
if (timeout == 0)
{
Expand All @@ -150,6 +156,8 @@ public override int WriteTimeout
}
set
{
if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // https://github.com/dotnet/runtime/issues/108151

if (value <= 0 && value != System.Threading.Timeout.Infinite)
{
throw new ArgumentOutOfRangeException(nameof(value), SR.net_io_timeout_use_gt_zero);
Expand Down Expand Up @@ -218,6 +226,8 @@ public override long Seek(long offset, SeekOrigin origin)
// Number of bytes we read, or 0 if the socket is closed.
public override int Read(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 (!CanRead)
Expand All @@ -237,6 +247,8 @@ public override int Read(byte[] buffer, int offset, int count)

public override int Read(Span<byte> 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
Expand All @@ -260,6 +272,8 @@ public override int Read(Span<byte> 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<byte>(&b, 1)) == 0 ? -1 : b;
}
Expand All @@ -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)
Expand All @@ -303,6 +319,8 @@ public override void Write(byte[] buffer, int offset, int count)

public override void Write(ReadOnlySpan<byte> 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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading

0 comments on commit 505e8ba

Please sign in to comment.