Skip to content

Commit

Permalink
Implement required HttpClient apis in System.Net.Quic (dotnet#1703)
Browse files Browse the repository at this point in the history
  • Loading branch information
jkotalik authored Jan 17, 2020
1 parent 95cff58 commit e3cfc67
Show file tree
Hide file tree
Showing 25 changed files with 693 additions and 117 deletions.
28 changes: 26 additions & 2 deletions src/libraries/System.Net.Quic/ref/System.Net.Quic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// Changes to this file must follow the http://aka.ms/api-review process.
// ------------------------------------------------------------------------------

using System.Buffers;
using System.Net.Security;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -20,9 +21,11 @@ public QuicConnection(IPEndPoint remoteEndPoint, System.Net.Security.SslClientAu
public IPEndPoint RemoteEndPoint => throw null;
public QuicStream OpenUnidirectionalStream() => throw null;
public QuicStream OpenBidirectionalStream() => throw null;
public long GetRemoteAvailableUnidirectionalStreamCount() => throw null;
public long GetRemoteAvailableBidirectionalStreamCount() => throw null;
public System.Threading.Tasks.ValueTask<QuicStream> AcceptStreamAsync(System.Threading.CancellationToken cancellationToken = default) => throw null;
public System.Net.Security.SslApplicationProtocol NegotiatedApplicationProtocol => throw null;
public ValueTask CloseAsync(System.Threading.CancellationToken cancellationToken = default) => throw null;
public ValueTask CloseAsync(long errorCode, System.Threading.CancellationToken cancellationToken = default) => throw null;
public void Dispose() => throw null;
}
public sealed partial class QuicListener : IDisposable
Expand All @@ -48,9 +51,15 @@ internal QuicStream() { }
public override int Read(byte[] buffer, int offset, int count) => throw null;
public override void Write(byte[] buffer, int offset, int count) => throw null;
public long StreamId => throw null;
public void AbortRead() => throw null;
public void AbortRead(long errorCode) => throw null;
public void AbortWrite(long errorCode) => throw null;
public ValueTask WriteAsync(ReadOnlyMemory<byte> data, bool endStream, System.Threading.CancellationToken cancellationToken = default) => throw null;
public ValueTask WriteAsync(ReadOnlySequence<byte> data, System.Threading.CancellationToken cancellationToken = default) => throw null;
public ValueTask WriteAsync(ReadOnlySequence<byte> data, bool endStream, System.Threading.CancellationToken cancellationToken = default) => throw null;
public ValueTask WriteAsync(ReadOnlyMemory<ReadOnlyMemory<byte>> data, System.Threading.CancellationToken cancellationToken = default) => throw null;
public ValueTask WriteAsync(ReadOnlyMemory<ReadOnlyMemory<byte>> data, bool endStream, System.Threading.CancellationToken cancellationToken = default) => throw null;
public ValueTask ShutdownWriteCompleted(System.Threading.CancellationToken cancellationToken = default) => throw null;
public void Shutdown() => throw null;
}
public class QuicClientConnectionOptions
{
Expand All @@ -70,4 +79,19 @@ public class QuicListenerOptions
public long MaxUnidirectionalStreams { get => throw null; set => throw null; }
public TimeSpan IdleTimeout { get => throw null; set => throw null; }
}
public class QuicException : Exception
{
public QuicException(string message) : base(message) { }
}
public class QuicConnectionAbortedException : QuicException
{
public QuicConnectionAbortedException(string message, long errorCode) : base(message) { }
public long ErrorCode { get; }

}
public class QuicStreamAbortedException : QuicException
{
public QuicStreamAbortedException(string message, long errorCode) : base(message) { }
public long ErrorCode { get; }
}
}
1 change: 1 addition & 0 deletions src/libraries/System.Net.Quic/ref/System.Net.Quic.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\System.IO\ref\System.IO.csproj" />
<ProjectReference Include="..\..\System.Memory\ref\System.Memory.csproj" />
<ProjectReference Include="..\..\System.IO.FileSystem.Primitives\ref\System.IO.FileSystem.Primitives.csproj" />
<ProjectReference Include="..\..\System.Net.Primitives\ref\System.Net.Primitives.csproj" />
<ProjectReference Include="..\..\System.Net.Security\ref\System.Net.Security.csproj" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ internal delegate uint ConnectionStartDelegate(
internal delegate uint ConnectionShutdownDelegate(
IntPtr connection,
uint flags,
ushort errorCode);
long errorCode);

[StructLayout(LayoutKind.Sequential)]
internal struct StreamEventDataRecv
Expand Down Expand Up @@ -454,7 +454,7 @@ internal delegate uint StreamCloseDelegate(
internal delegate uint StreamShutdownDelegate(
IntPtr stream,
uint flags,
ushort errorCode);
long errorCode);

internal delegate uint StreamSendDelegate(
IntPtr stream,
Expand Down
5 changes: 4 additions & 1 deletion src/libraries/System.Net.Quic/src/System.Net.Quic.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<Compile Include="System\Net\Quic\Implementations\MsQuic\Internal\MsQuicParameterHelpers.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\Internal\MsQuicSecurityConfig.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\Internal\MsQuicSession.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\Internal\MsQuicStatusException.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\Internal\QuicExceptionHelpers.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\Internal\ResettableCompletionSource.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\MsQuicConnection.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\MsQuicImplementationProvider.cs" />
Expand All @@ -33,6 +33,9 @@
<Compile Include="System\Net\Quic\QuicListener.cs" />
<Compile Include="System\Net\Quic\QuicListenerOptions.cs" />
<Compile Include="System\Net\Quic\QuicStream.cs" />
<Compile Include="System\Net\Quic\QuicException.cs" />
<Compile Include="System\Net\Quic\QuicConnectionAbortedException.cs" />
<Compile Include="System\Net\Quic\QuicStreamAbortedException.cs" />
<Compile Include="Interop\Interop.MsQuic.cs" />
<Compile Include="Interop\MsQuicEnums.cs" />
<Compile Include="Interop\MsQuicNativeMethods.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,16 @@ internal override QuicStreamProvider OpenBidirectionalStream()
return new MockStream(this, streamId, bidirectional: true);
}

internal override long GetRemoteAvailableUnidirectionalStreamCount()
{
throw new NotImplementedException();
}

internal override long GetRemoteAvailableBidirectionalStreamCount()
{
throw new NotImplementedException();
}

internal async Task<Socket> CreateOutboundMockStreamAsync(long streamId)
{
Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
Expand Down Expand Up @@ -168,7 +178,7 @@ internal override async ValueTask<QuicStreamProvider> AcceptStreamAsync(Cancella
return new MockStream(socket, streamId, bidirectional: bidirectional);
}

internal override ValueTask CloseAsync(CancellationToken cancellationToken = default)
internal override ValueTask CloseAsync(long errorCode, CancellationToken cancellationToken = default)
{
Dispose();
return default;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Buffers;
using System.Diagnostics;
using System.Net.Sockets;
using System.Threading;
Expand Down Expand Up @@ -120,7 +121,6 @@ internal override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, bool e
{
await ConnectAsync(cancellationToken).ConfigureAwait(false);
}

await _socket.SendAsync(buffer, SocketFlags.None, cancellationToken).ConfigureAwait(false);

if (endStream)
Expand All @@ -129,6 +129,64 @@ internal override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, bool e
}
}

internal override ValueTask WriteAsync(ReadOnlySequence<byte> buffers, CancellationToken cancellationToken = default)
{
return WriteAsync(buffers, endStream: false, cancellationToken);
}
internal override async ValueTask WriteAsync(ReadOnlySequence<byte> buffers, bool endStream, CancellationToken cancellationToken = default)
{
CheckDisposed();

if (!_canWrite)
{
throw new NotSupportedException();
}

if (_socket == null)
{
await ConnectAsync(cancellationToken).ConfigureAwait(false);
}

foreach (ReadOnlyMemory<byte> buffer in buffers)
{
await _socket.SendAsync(buffer, SocketFlags.None, cancellationToken).ConfigureAwait(false);
}

if (endStream)
{
_socket.Shutdown(SocketShutdown.Send);
}
}

internal override ValueTask WriteAsync(ReadOnlyMemory<ReadOnlyMemory<byte>> buffers, CancellationToken cancellationToken = default)
{
return WriteAsync(buffers, endStream: false, cancellationToken);
}
internal override async ValueTask WriteAsync(ReadOnlyMemory<ReadOnlyMemory<byte>> buffers, bool endStream, CancellationToken cancellationToken = default)
{
CheckDisposed();

if (!_canWrite)
{
throw new NotSupportedException();
}

if (_socket == null)
{
await ConnectAsync(cancellationToken).ConfigureAwait(false);
}

foreach (ReadOnlyMemory<byte> buffer in buffers.ToArray())
{
await _socket.SendAsync(buffer, SocketFlags.None, cancellationToken).ConfigureAwait(false);
}

if (endStream)
{
_socket.Shutdown(SocketShutdown.Send);
}
}

internal override void Flush()
{
CheckDisposed();
Expand All @@ -141,18 +199,31 @@ internal override Task FlushAsync(CancellationToken cancellationToken)
return Task.CompletedTask;
}

internal override void AbortRead()
internal override void AbortRead(long errorCode)
{
throw new NotImplementedException();
}

internal override void AbortWrite(long errorCode)
{
throw new NotImplementedException();
}


internal override ValueTask ShutdownWriteCompleted(CancellationToken cancellationToken = default)
{
CheckDisposed();

return default;
}

internal override void Shutdown()
{
CheckDisposed();

_socket.Shutdown(SocketShutdown.Send);
}

private void CheckDisposed()
{
if (_disposed)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ internal class MsQuicApi : IDisposable

private unsafe MsQuicApi()
{
MsQuicStatusException.ThrowIfFailed(Interop.MsQuic.MsQuicOpen(version: 1, out MsQuicNativeMethods.NativeApi* registration));
QuicExceptionHelpers.ThrowIfFailed(
Interop.MsQuic.MsQuicOpen(version: 1, out MsQuicNativeMethods.NativeApi* registration),
"Could not open MsQuic.");

MsQuicNativeMethods.NativeApi nativeRegistration = *registration;

Expand Down Expand Up @@ -184,10 +186,12 @@ public async ValueTask<MsQuicSecurityConfig> CreateSecurityConfig(X509Certificat
MsQuicSecurityConfig secConfig = null;
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
uint secConfigCreateStatus = MsQuicStatusCodes.InternalError;
uint status;
uint createConfigStatus;

// If no certificate is provided, provide a null one.
if (certificate != null)
{
status = SecConfigCreateDelegate(
createConfigStatus = SecConfigCreateDelegate(
_registrationContext,
(uint)QUIC_SEC_CONFIG_FLAG.CERT_CONTEXT,
certificate.Handle,
Expand All @@ -197,7 +201,7 @@ public async ValueTask<MsQuicSecurityConfig> CreateSecurityConfig(X509Certificat
}
else
{
status = SecConfigCreateDelegate(
createConfigStatus = SecConfigCreateDelegate(
_registrationContext,
(uint)QUIC_SEC_CONFIG_FLAG.CERT_NULL,
IntPtr.Zero,
Expand All @@ -206,7 +210,9 @@ public async ValueTask<MsQuicSecurityConfig> CreateSecurityConfig(X509Certificat
SecCfgCreateCallbackHandler);
}

MsQuicStatusException.ThrowIfFailed(status);
QuicExceptionHelpers.ThrowIfFailed(
createConfigStatus,
"Could not create security configuration.");

void SecCfgCreateCallbackHandler(
IntPtr context,
Expand All @@ -220,7 +226,9 @@ void SecCfgCreateCallbackHandler(

await tcs.Task.ConfigureAwait(false);

MsQuicStatusException.ThrowIfFailed(secConfigCreateStatus);
QuicExceptionHelpers.ThrowIfFailed(
secConfigCreateStatus,
"Could not create security configuration.");

return secConfig;
}
Expand All @@ -234,7 +242,8 @@ public IntPtr SessionOpen(byte[] alpn)
alpn,
IntPtr.Zero,
ref sessionPtr);
MsQuicStatusException.ThrowIfFailed(status);

QuicExceptionHelpers.ThrowIfFailed(status, "Could not open session.");

return sessionPtr;
}
Expand Down
Loading

0 comments on commit e3cfc67

Please sign in to comment.