├── tools └── Key.snk ├── global.json ├── test ├── Channels.Tests.Performance │ ├── random.json.gz │ ├── project.json │ ├── Channels.Tests.Performance.xproj │ └── Program.cs └── Channels.Tests │ ├── Properties │ └── AssemblyInfo.cs │ ├── project.json │ ├── Channels.Tests.xproj │ ├── BufferPoolFacts.cs │ ├── SignalFacts.cs │ └── ReadableBufferReaderFacts.cs ├── README.md ├── NuGet.config ├── src ├── Channels │ ├── IReadableBufferAwaiter.cs │ ├── ChannelReadResult.cs │ ├── StreamChannel.cs │ ├── CommonVectors.cs │ ├── IChannel.cs │ ├── IBufferPool.cs │ ├── ArrayBufferPool.cs │ ├── project.json │ ├── UnownedBuffer.cs │ ├── ReadableChannelAwaitable.cs │ ├── Channels.xproj │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── IWritableChannel.cs │ ├── WritableChannel.cs │ ├── PreservedBuffer.cs │ ├── IReadableChannel.cs │ ├── ReadableBufferReader.cs │ ├── MemoryEnumerator.cs │ ├── ReadableChannel.cs │ ├── DefaultWritableBufferExtensions.cs │ ├── ChannelFactory.cs │ ├── Gate.cs │ ├── MemoryPoolBlock.cs │ ├── DefaultReadableBufferExtensions.cs │ ├── MemoryPoolSlab.cs │ └── WritableBuffer.cs ├── Channels.Networking.Windows.RIO │ ├── Internal │ │ ├── Winsock │ │ │ ├── Protocol.cs │ │ │ ├── SocketType.cs │ │ │ ├── AddressFamilies.cs │ │ │ ├── SuppressUnmanagedCodeSecurityAttribute.cs │ │ │ ├── NotificationCompletionType.cs │ │ │ ├── RioReceiveFlags.cs │ │ │ ├── RioSendFlags.cs │ │ │ ├── NotificationCompletion.cs │ │ │ ├── NotificationCompletionEvent.cs │ │ │ ├── SocketAddress.cs │ │ │ ├── SocketFlags.cs │ │ │ ├── NotificationCompletionIocp.cs │ │ │ ├── Ipv4InternetAddress.cs │ │ │ ├── WindowsSocketsData.cs │ │ │ ├── RioExtensionFunctionTable.cs │ │ │ ├── RegisteredIO.cs │ │ │ ├── Version.cs │ │ │ └── RioDelegates.cs │ │ ├── RioRequestResult.cs │ │ ├── RioBufferSegment.cs │ │ └── RioThreadPool.cs │ ├── project.json │ ├── Properties │ │ └── AssemblyInfo.cs │ └── Channels.Networking.Windows.RIO.xproj ├── Channels.Networking.Sockets │ ├── Internal │ │ ├── ContinuationMode.cs │ │ ├── SocketExtensions.cs │ │ ├── Signal.cs │ │ └── MicroBufferPool.cs │ ├── project.json │ ├── Channels.Networking.Sockets.xproj │ └── Properties │ │ └── AssemblyInfo.cs ├── Channels.File │ ├── project.json │ ├── ReadableFileChannelFactoryExtensions.cs │ └── Channels.File.xproj ├── Channels.Compression │ ├── project.json │ ├── Channels.Compression.xproj │ ├── Interop │ │ └── Interop.zlib.Unix.cs │ ├── ZLibNative.Windows.cs │ └── ZLibException.cs ├── Channels.Text.Primitives │ ├── WritableChannelExtensions.cs │ ├── project.json │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Channels.Text.Primitives.xproj │ ├── WritableChannelFormatter.cs │ ├── SplitEnumerable.cs │ ├── SplitEnumerator.cs │ └── AsciiUtilities.cs └── Channels.Networking.Libuv │ ├── Interop │ ├── UvException.cs │ ├── PlatformApis.cs │ ├── UvRequest.cs │ ├── UvPipeHandle.cs │ ├── UvLoopHandle.cs │ ├── UvShutdownReq.cs │ ├── UvHandle.cs │ ├── UvTcpHandle.cs │ ├── UvAsyncHandle.cs │ ├── UvMemory.cs │ └── UvConnectRequest.cs │ ├── project.json │ ├── Properties │ └── AssemblyInfo.cs │ ├── Channels.Networking.Libuv.xproj │ ├── Internal │ ├── WriteReqPool.cs │ └── WorkQueue.cs │ ├── UvTcpClient.cs │ ├── UvTcpListener.cs │ └── UvThread.cs ├── .vscode ├── tasks.json └── launch.json ├── .gitignore ├── samples └── Channels.Samples │ ├── Program.cs │ ├── LibuvHttpClientSample.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── Models │ └── Pet.cs │ ├── project.json │ ├── Channels.Samples.xproj │ ├── CompressionSample.cs │ ├── AspNetHttpServerSample.cs │ ├── HttpServer │ ├── FormReader.cs │ ├── HttpResponseStream.cs │ ├── ResponseHeaderDictionary.cs │ ├── HttpRequestParser.cs │ └── HttpConnection.Features.cs │ ├── RawLibuvHttpClientSample.cs │ ├── HttpClient │ └── ChannelHttpContent.cs │ ├── RawLibuvHttpServerSample.cs │ └── Framing │ └── Codec.cs ├── LICENSE.md ├── .gitattributes ├── appveyor.yml └── .travis.yml /tools/Key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidfowl/Channels/HEAD/tools/Key.snk -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "projects": [ "src", "test" ], 3 | "sdk": { 4 | "version": "1.0.0-preview2-003121" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/Channels.Tests.Performance/random.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidfowl/Channels/HEAD/test/Channels.Tests.Performance/random.json.gz -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Channels (Push based Streams) 2 | 3 | Channels has been renamed to Pipelines (System.IO.Pipelines) and has moved to corefxlab https://github.com/dotnet/corefxlab. 4 | -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/Channels/IReadableBufferAwaiter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Channels 4 | { 5 | public interface IReadableBufferAwaiter 6 | { 7 | bool IsCompleted { get; } 8 | 9 | ChannelReadResult GetResult(); 10 | 11 | void OnCompleted(Action continuation); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Channels.Networking.Windows.RIO/Internal/Winsock/Protocol.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | namespace Channels.Networking.Windows.RIO.Internal.Winsock 5 | { 6 | public enum Protocol : short 7 | { 8 | IpProtocolTcp = 6, 9 | } 10 | } -------------------------------------------------------------------------------- /src/Channels.Networking.Windows.RIO/Internal/Winsock/SocketType.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | namespace Channels.Networking.Windows.RIO.Internal.Winsock 5 | { 6 | public enum SocketType : short 7 | { 8 | Stream = 1, 9 | } 10 | } -------------------------------------------------------------------------------- /src/Channels.Networking.Windows.RIO/Internal/Winsock/AddressFamilies.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | namespace Channels.Networking.Windows.RIO.Internal.Winsock 5 | { 6 | public enum AddressFamilies : short 7 | { 8 | Internet = 2, 9 | } 10 | } -------------------------------------------------------------------------------- /src/Channels.Networking.Windows.RIO/Internal/Winsock/SuppressUnmanagedCodeSecurityAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | #if !NET451 5 | namespace System.Security 6 | { 7 | public class SuppressUnmanagedCodeSecurityAttribute : Attribute 8 | { 9 | } 10 | } 11 | #endif -------------------------------------------------------------------------------- /src/Channels/ChannelReadResult.cs: -------------------------------------------------------------------------------- 1 | namespace Channels 2 | { 3 | public struct ChannelReadResult 4 | { 5 | public ChannelReadResult(ReadableBuffer buffer, bool isCompleted) 6 | { 7 | Buffer = buffer; 8 | IsCompleted = isCompleted; 9 | } 10 | 11 | public ReadableBuffer Buffer { get; } 12 | 13 | public bool IsCompleted { get; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Channels.Networking.Sockets/Internal/ContinuationMode.cs: -------------------------------------------------------------------------------- 1 | namespace Channels.Networking.Sockets.Internal 2 | { 3 | /// 4 | /// Used by Signal to control how callbacks are invoked 5 | /// 6 | internal enum ContinuationMode 7 | { 8 | Synchronous, 9 | ThreadPool, 10 | // TODO: sync-context? but if so: whose? the .Current at creation? at SetResult? 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | "command": "dotnet", 4 | "isShellCommand": true, 5 | "args": [], 6 | "tasks": [ 7 | { 8 | "taskName": "build", 9 | "args": [ 10 | "${workspaceRoot}/samples/Channels.Samples/project.json" 11 | ], 12 | "isBuildCommand": true, 13 | "problemMatcher": "$msCompile" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /src/Channels.File/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0-*", 3 | 4 | "buildOptions": { 5 | "allowUnsafe": true, 6 | "keyFile": "../../tools/Key.snk", 7 | "xmlDoc": true 8 | }, 9 | 10 | "dependencies": { 11 | "Channels": { 12 | "target": "project" 13 | }, 14 | "System.Threading.Overlapped": "4.0.1" 15 | }, 16 | 17 | 18 | "frameworks": { 19 | "netstandard1.3": { }, 20 | "net46": {} 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Channels.Compression/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0-*", 3 | 4 | "buildOptions": { 5 | "allowUnsafe": true, 6 | "keyFile": "../../tools/Key.snk", 7 | "xmlDoc": true 8 | }, 9 | 10 | "dependencies": { 11 | "Channels": { 12 | "target": "project" 13 | }, 14 | "System.Diagnostics.Contracts": "4.0.1" 15 | }, 16 | 17 | 18 | "frameworks": { 19 | "net451": {}, 20 | "netstandard1.3": {} 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Channels.Networking.Windows.RIO/Internal/Winsock/NotificationCompletionType.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | namespace Channels.Networking.Windows.RIO.Internal.Winsock 5 | { 6 | public enum NotificationCompletionType : int 7 | { 8 | Polling = 0, 9 | EventCompletion = 1, 10 | IocpCompletion = 2 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Channels.Networking.Windows.RIO/Internal/Winsock/RioReceiveFlags.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | namespace Channels.Networking.Windows.RIO.Internal.Winsock 5 | { 6 | public enum RioReceiveFlags : uint 7 | { 8 | None = 0x00000000, 9 | DontNotify = 0x00000001, 10 | Defer = 0x00000002, 11 | Waitall = 0x00000004, 12 | CommitOnly = 0x00000008 13 | } 14 | } -------------------------------------------------------------------------------- /src/Channels.Networking.Windows.RIO/Internal/Winsock/RioSendFlags.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | 6 | namespace Channels.Networking.Windows.RIO.Internal.Winsock 7 | { 8 | [Flags] 9 | public enum RioSendFlags : uint 10 | { 11 | None = 0x00000000, 12 | DontNotify = 0x00000001, 13 | Defer = 0x00000002, 14 | CommitOnly = 0x00000008 15 | } 16 | } -------------------------------------------------------------------------------- /src/Channels.Text.Primitives/WritableChannelExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Text.Formatting; 4 | using System.Threading.Tasks; 5 | 6 | namespace Channels.Text.Primitives 7 | { 8 | public static class WritableChannelExtensions 9 | { 10 | public static WritableChannelFormatter GetFormatter(this IWritableChannel channel, EncodingData formattingData) 11 | { 12 | return new WritableChannelFormatter(channel, formattingData); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | [Oo]bj/ 2 | [Bb]in/ 3 | TestResults/ 4 | .nuget/ 5 | *.sln.ide/ 6 | _ReSharper.*/ 7 | packages/ 8 | artifacts/ 9 | PublishProfiles/ 10 | .vs/ 11 | *.user 12 | *.suo 13 | *.cache 14 | *.docstates 15 | _ReSharper.* 16 | nuget.exe 17 | *net45.csproj 18 | *net451.csproj 19 | *k10.csproj 20 | *.psess 21 | *.vsp 22 | *.pidb 23 | *.userprefs 24 | *DS_Store 25 | *.ncrunchsolution 26 | *.*sdf 27 | *.ipch 28 | project.lock.json 29 | runtimes/ 30 | .build/ 31 | .testPublish/ 32 | launchSettings.json 33 | *.tmp 34 | BDN.Auto/ 35 | BenchmarkDotNet.Artifacts/ 36 | -------------------------------------------------------------------------------- /src/Channels.Networking.Windows.RIO/Internal/Winsock/NotificationCompletion.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Runtime.InteropServices; 5 | 6 | namespace Channels.Networking.Windows.RIO.Internal.Winsock 7 | { 8 | [StructLayout(LayoutKind.Sequential)] 9 | public struct NotificationCompletion 10 | { 11 | public NotificationCompletionType Type; 12 | public NotificationCompletionIocp Iocp; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Channels.Networking.Windows.RIO/Internal/Winsock/NotificationCompletionEvent.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | using System.Runtime.InteropServices; 6 | 7 | namespace Channels.Networking.Windows.RIO.Internal.Winsock 8 | { 9 | [StructLayout(LayoutKind.Sequential)] 10 | public struct NotificationCompletionEvent 11 | { 12 | public IntPtr EventHandle; 13 | public bool NotifyReset; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /samples/Channels.Samples/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Channels.Samples.Framing; 7 | using Channels.Text.Primitives; 8 | 9 | namespace Channels.Samples 10 | { 11 | public class Program 12 | { 13 | public static void Main(string[] args) 14 | { 15 | AspNetHttpServerSample.Run(); 16 | // RawLibuvHttpServerSample.Run(); 17 | // ProtocolHandling.Run(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Channels.Networking.Libuv/Interop/UvException.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | 6 | namespace Channels.Networking.Libuv.Interop 7 | { 8 | public class UvException : Exception 9 | { 10 | public UvException(string message, int statusCode) : base(message) 11 | { 12 | StatusCode = statusCode; 13 | } 14 | 15 | public int StatusCode { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Channels.Networking.Windows.RIO/Internal/RioRequestResult.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Runtime.InteropServices; 5 | 6 | namespace Channels.Networking.Windows.RIO.Internal 7 | { 8 | [StructLayout(LayoutKind.Sequential)] 9 | public struct RioRequestResult 10 | { 11 | public int Status; 12 | public uint BytesTransferred; 13 | public long ConnectionCorrelation; 14 | public long RequestCorrelation; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Channels.Networking.Windows.RIO/Internal/Winsock/SocketAddress.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Runtime.InteropServices; 5 | 6 | namespace Channels.Networking.Windows.RIO.Internal.Winsock 7 | { 8 | [StructLayout(LayoutKind.Sequential)] 9 | public unsafe struct SocketAddress 10 | { 11 | public AddressFamilies Family; 12 | public ushort Port; 13 | public Ipv4InternetAddress IpAddress; 14 | public fixed byte Padding[8]; 15 | } 16 | } -------------------------------------------------------------------------------- /src/Channels.File/ReadableFileChannelFactoryExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace Channels.File 7 | { 8 | public static class ReadableFileChannelFactoryExtensions 9 | { 10 | public static IReadableChannel ReadFile(this ChannelFactory factory, string path) 11 | { 12 | var channel = factory.CreateChannel(); 13 | 14 | var file = new ReadableFileChannel(channel); 15 | file.OpenReadFile(path); 16 | return file; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) David Fowler All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you 4 | may not use this file except in compliance with the License. You may 5 | obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | implied. See the License for the specific language governing permissions 13 | and limitations under the License. 14 | -------------------------------------------------------------------------------- /src/Channels.Networking.Windows.RIO/Internal/Winsock/SocketFlags.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | namespace Channels.Networking.Windows.RIO.Internal.Winsock 5 | { 6 | public enum SocketFlags : uint 7 | { 8 | Overlapped = 0x01, 9 | MultipointCRoot = 0x02, 10 | MultipointCLeaf = 0x04, 11 | MultipointDRoot = 0x08, 12 | MultipointDLeaf = 0x10, 13 | AccessSystemSecurity = 0x40, 14 | NoHandleInherit = 0x80, 15 | RegisteredIO = 0x100 16 | } 17 | } -------------------------------------------------------------------------------- /src/Channels/StreamChannel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Channels 5 | { 6 | internal class StreamChannel : IChannel 7 | { 8 | public StreamChannel(ChannelFactory factory, Stream stream) 9 | { 10 | Input = factory.MakeReadableChannel(stream); 11 | Output = factory.MakeWriteableChannel(stream); 12 | } 13 | 14 | public IReadableChannel Input { get; } 15 | 16 | public IWritableChannel Output { get; } 17 | 18 | public void Dispose() 19 | { 20 | Input.Complete(); 21 | Output.Complete(); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Channels.Networking.Windows.RIO/Internal/Winsock/NotificationCompletionIocp.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | using System.Runtime.InteropServices; 6 | using System.Threading; 7 | 8 | namespace Channels.Networking.Windows.RIO.Internal.Winsock 9 | { 10 | [StructLayout(LayoutKind.Sequential)] 11 | public unsafe struct NotificationCompletionIocp 12 | { 13 | public IntPtr IocpHandle; 14 | public ulong QueueCorrelation; 15 | public NativeOverlapped* Overlapped; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Channels/CommonVectors.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace Channels 5 | { 6 | // Move to text library? 7 | internal class CommonVectors 8 | { 9 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 10 | public static Vector GetVector(byte vectorByte) 11 | { 12 | // Vector .ctor is a bit fussy to get working; however this always seems to work 13 | // https://github.com/dotnet/coreclr/issues/7459#issuecomment-253965670 14 | return Vector.AsVectorByte(new Vector(vectorByte * 0x0101010101010101ul)); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Channels/IChannel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Channels 4 | { 5 | /// 6 | /// Defines a class that provides a duplex channel from which data can be read from and written to. 7 | /// 8 | public interface IChannel : IDisposable 9 | { 10 | /// 11 | /// Gets the half of the duplex channel. 12 | /// 13 | IReadableChannel Input { get; } 14 | 15 | /// 16 | /// Gets the half of the duplex channel. 17 | /// 18 | IWritableChannel Output { get; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Channels.Networking.Windows.RIO/Internal/Winsock/Ipv4InternetAddress.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Runtime.InteropServices; 5 | 6 | namespace Channels.Networking.Windows.RIO.Internal.Winsock 7 | { 8 | [StructLayout(LayoutKind.Explicit, Size = 4)] 9 | public struct Ipv4InternetAddress 10 | { 11 | [FieldOffset(0)] 12 | public byte Byte1; 13 | [FieldOffset(1)] 14 | public byte Byte2; 15 | [FieldOffset(2)] 16 | public byte Byte3; 17 | [FieldOffset(3)] 18 | public byte Byte4; 19 | } 20 | } -------------------------------------------------------------------------------- /src/Channels/IBufferPool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | 4 | namespace Channels 5 | { 6 | /// 7 | /// An interface that represents a that channels will use to allocate memory. 8 | /// 9 | public interface IBufferPool : IDisposable 10 | { 11 | /// 12 | /// Leases a from the 13 | /// 14 | /// The size of the requested buffer 15 | /// A which is a wrapper around leased memory 16 | OwnedMemory Lease(int size); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Channels.Networking.Libuv/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0-beta-*", 3 | "description": "Networking implementation of Channels based on Libuv", 4 | "packOptions": { 5 | "projectUrl": "https://github.com/davidfowl/Channels" 6 | }, 7 | "buildOptions": { 8 | "allowUnsafe": true, 9 | "keyFile": "../../tools/Key.snk" 10 | }, 11 | 12 | "dependencies": { 13 | "NETStandard.Library": "1.6.0", 14 | "Channels": { 15 | "target": "project" 16 | }, 17 | "Libuv": "1.9.0" 18 | }, 19 | 20 | "frameworks": { 21 | "net451": {}, 22 | "netstandard1.3": { 23 | "dependencies": { 24 | "System.Threading.Thread": "4.0.0" 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Channels.Text.Primitives/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0-beta-*", 3 | "description": "Primitives for dealing with reading and writing text to and from channels", 4 | "packOptions": { 5 | "projectUrl": "https://github.com/davidfowl/Channels" 6 | }, 7 | "buildOptions": { 8 | "allowUnsafe": true, 9 | "keyFile": "../../tools/Key.snk", 10 | "xmlDoc": true 11 | }, 12 | 13 | "dependencies": { 14 | "NETStandard.Library": "1.6.0", 15 | "System.Text.Formatting": "0.1.0-*", 16 | "System.Text.Primitives": "0.1.0-*", 17 | "Channels": { 18 | "target": "project" 19 | } 20 | }, 21 | 22 | "frameworks": { 23 | "net451": {}, 24 | "netstandard1.3": {} 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Channels.Networking.Libuv/Interop/PlatformApis.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Runtime.InteropServices; 5 | 6 | namespace Channels.Networking.Libuv.Interop 7 | { 8 | public static class PlatformApis 9 | { 10 | static PlatformApis() 11 | { 12 | IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); 13 | IsDarwin = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); 14 | } 15 | 16 | public static bool IsWindows { get; } 17 | 18 | public static bool IsDarwin { get; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Channels.Networking.Sockets/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0-beta-*", 3 | "description": "Networking implementation of Channels based on System.Net.Socket using the SocketAsyncEventArgs API", 4 | "packOptions": { 5 | "projectUrl": "https://github.com/davidfowl/Channels" 6 | }, 7 | "buildOptions": { 8 | "allowUnsafe": true, 9 | "keyFile": "../../tools/Key.snk" 10 | }, 11 | 12 | "dependencies": { 13 | "NETStandard.Library": "1.6.0", 14 | "Channels": { 15 | "target": "project" 16 | } 17 | }, 18 | 19 | "frameworks": { 20 | "net451": { }, 21 | "netstandard1.3": { 22 | "dependencies": { 23 | "System.Threading.ThreadPool": "4.0.10" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Channels.Networking.Windows.RIO/Internal/RioBufferSegment.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | using System.Runtime.InteropServices; 6 | 7 | namespace Channels.Networking.Windows.RIO.Internal 8 | { 9 | [StructLayout(LayoutKind.Sequential)] 10 | public struct RioBufferSegment 11 | { 12 | public RioBufferSegment(IntPtr bufferId, uint offset, uint length) 13 | { 14 | BufferId = bufferId; 15 | Offset = offset; 16 | Length = length; 17 | } 18 | 19 | IntPtr BufferId; 20 | public readonly uint Offset; 21 | public uint Length; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /samples/Channels.Samples/LibuvHttpClientSample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net.Http; 5 | using System.Threading.Tasks; 6 | 7 | namespace Channels.Samples 8 | { 9 | public class LibuvHttpClientSample 10 | { 11 | public static async Task Run() 12 | { 13 | var client = new HttpClient(new LibuvHttpClientHandler()); 14 | 15 | while (true) 16 | { 17 | var response = await client.GetAsync("http://localhost:5000"); 18 | 19 | Console.WriteLine(response); 20 | 21 | Console.WriteLine(await response.Content.ReadAsStringAsync()); 22 | 23 | await Task.Delay(1000); 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Channels.Networking.Libuv/Interop/UvRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Channels.Networking.Libuv.Interop 5 | { 6 | public class UvRequest : UvMemory 7 | { 8 | private GCHandle _pin; 9 | 10 | protected UvRequest() : base () 11 | { 12 | } 13 | 14 | protected override bool ReleaseHandle() 15 | { 16 | DestroyMemory(handle); 17 | handle = IntPtr.Zero; 18 | return true; 19 | } 20 | 21 | public virtual void Pin() 22 | { 23 | _pin = GCHandle.Alloc(this, GCHandleType.Normal); 24 | } 25 | 26 | public virtual void Unpin() 27 | { 28 | _pin.Free(); 29 | } 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/Channels/ArrayBufferPool.cs: -------------------------------------------------------------------------------- 1 | using System.Buffers; 2 | 3 | namespace Channels 4 | { 5 | public class ArrayBufferPool : IBufferPool 6 | { 7 | public static readonly ArrayBufferPool Instance = new ArrayBufferPool(ArrayPool.Shared); 8 | 9 | private readonly ArrayPool _pool; 10 | 11 | public ArrayBufferPool(ArrayPool pool) 12 | { 13 | _pool = pool; 14 | } 15 | 16 | public OwnedMemory Lease(int size) 17 | { 18 | // Unfortunately this allocates.... (we could pool the owned array objects though) 19 | return new OwnedArray(_pool.Rent(size)); 20 | } 21 | 22 | public void Dispose() 23 | { 24 | // Nothing to do here 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Channels/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0-beta-*", 3 | "description": "An abstraction for doing efficient asynchronous IO", 4 | "packOptions": { 5 | "projectUrl": "https://github.com/davidfowl/Channels" 6 | }, 7 | "buildOptions": { 8 | "allowUnsafe": true, 9 | "keyFile": "../../tools/Key.snk", 10 | "xmlDoc": true 11 | }, 12 | 13 | "dependencies": { 14 | "NETStandard.Library": "1.6.0", 15 | "System.Slices": "0.1.0-*", 16 | "System.Binary": "0.1.0-*", 17 | "System.Buffers": "4.0.0", 18 | "System.Runtime.CompilerServices.Unsafe": "4.0.0", 19 | "System.Numerics.Vectors": "4.1.1", 20 | "System.Threading.Tasks.Extensions": "4.0.0", 21 | "System.Collections.Sequences": "0.1.0-*" 22 | }, 23 | 24 | "frameworks": { 25 | "net451": {}, 26 | "netstandard1.3": {} 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Channels.Networking.Windows.RIO/Internal/Winsock/WindowsSocketsData.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | using System.Runtime.InteropServices; 6 | 7 | namespace Channels.Networking.Windows.RIO.Internal.Winsock 8 | { 9 | [StructLayout(LayoutKind.Sequential)] 10 | internal struct WindowsSocketsData 11 | { 12 | internal short Version; 13 | internal short HighVersion; 14 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)] 15 | internal string Description; 16 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 129)] 17 | internal string SystemStatus; 18 | internal short MaxSockets; 19 | internal short MaxDatagramSize; 20 | internal IntPtr VendorInfo; 21 | } 22 | } -------------------------------------------------------------------------------- /src/Channels.Networking.Windows.RIO/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0-beta-*", 3 | "description": "Networking implementation of Channels based on Windows RIO", 4 | "packOptions": { 5 | "projectUrl": "https://github.com/davidfowl/Channels" 6 | }, 7 | "buildOptions": { 8 | "allowUnsafe": true, 9 | "keyFile": "../../tools/Key.snk" 10 | }, 11 | 12 | "dependencies": { 13 | "Channels": { 14 | "target": "project" 15 | } 16 | }, 17 | 18 | "frameworks": { 19 | "net451": {}, 20 | "netstandard1.3": { 21 | "dependencies": { 22 | "NETStandard.Library": "1.6.0", 23 | "System.Threading.Thread": "4.0.0", 24 | "System.Threading.Overlapped": "4.0.1", 25 | "System.Threading.ThreadPool": "4.0.10", 26 | "System.Diagnostics.Process": "4.1.0", 27 | "System.Runtime.CompilerServices.Unsafe": "4.0.0" 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/Channels.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyProduct("Channels.Tests")] 11 | [assembly: AssemblyTrademark("")] 12 | 13 | // Setting ComVisible to false makes the types in this assembly not visible 14 | // to COM components. If you need to access a type in this assembly from 15 | // COM, set the ComVisible attribute to true on that type. 16 | [assembly: ComVisible(false)] 17 | 18 | // The following GUID is for the ID of the typelib if this project is exposed to COM 19 | [assembly: Guid("b9967782-565b-4b0b-97b9-043e35022674")] 20 | -------------------------------------------------------------------------------- /samples/Channels.Samples/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyProduct("Channels.Samples")] 11 | [assembly: AssemblyTrademark("")] 12 | 13 | // Setting ComVisible to false makes the types in this assembly not visible 14 | // to COM components. If you need to access a type in this assembly from 15 | // COM, set the ComVisible attribute to true on that type. 16 | [assembly: ComVisible(false)] 17 | 18 | // The following GUID is for the ID of the typelib if this project is exposed to COM 19 | [assembly: Guid("3dcb0f92-51ae-493e-a1b6-5b42449ea2bd")] 20 | -------------------------------------------------------------------------------- /src/Channels.Networking.Libuv/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyProduct("Channels.Networking.Libuv")] 11 | [assembly: AssemblyTrademark("")] 12 | 13 | // Setting ComVisible to false makes the types in this assembly not visible 14 | // to COM components. If you need to access a type in this assembly from 15 | // COM, set the ComVisible attribute to true on that type. 16 | [assembly: ComVisible(false)] 17 | 18 | // The following GUID is for the ID of the typelib if this project is exposed to COM 19 | [assembly: Guid("11d6b886-a303-4d13-bca8-a917d5740fb6")] 20 | -------------------------------------------------------------------------------- /src/Channels.Text.Primitives/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyProduct("Channels.Text.Primitives")] 11 | [assembly: AssemblyTrademark("")] 12 | 13 | // Setting ComVisible to false makes the types in this assembly not visible 14 | // to COM components. If you need to access a type in this assembly from 15 | // COM, set the ComVisible attribute to true on that type. 16 | [assembly: ComVisible(false)] 17 | 18 | // The following GUID is for the ID of the typelib if this project is exposed to COM 19 | [assembly: Guid("a98068ae-c895-4b1f-adcb-60c70c64f118")] 20 | -------------------------------------------------------------------------------- /src/Channels.Networking.Windows.RIO/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyProduct("Channels.Networking.Windows.RIO")] 11 | [assembly: AssemblyTrademark("")] 12 | 13 | // Setting ComVisible to false makes the types in this assembly not visible 14 | // to COM components. If you need to access a type in this assembly from 15 | // COM, set the ComVisible attribute to true on that type. 16 | [assembly: ComVisible(false)] 17 | 18 | // The following GUID is for the ID of the typelib if this project is exposed to COM 19 | [assembly: Guid("86a0b10d-8c7a-4b20-a033-d6f679454e30")] 20 | -------------------------------------------------------------------------------- /src/Channels/UnownedBuffer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | 4 | namespace Channels 5 | { 6 | /// 7 | /// Represents a buffer that is owned by an external component. 8 | /// 9 | public class UnownedBuffer : OwnedMemory 10 | { 11 | private ArraySegment _buffer; 12 | 13 | public UnownedBuffer(ArraySegment buffer) : base(buffer.Array, buffer.Offset, buffer.Count) 14 | { 15 | _buffer = buffer; 16 | } 17 | 18 | public OwnedMemory MakeCopy(int offset, int length, out int newStart, out int newEnd) 19 | { 20 | // Copy to a new Owned Buffer. 21 | var buffer = new byte[length]; 22 | Buffer.BlockCopy(_buffer.Array, _buffer.Offset + offset, buffer, 0, length); 23 | newStart = 0; 24 | newEnd = length; 25 | return new OwnedArray(buffer); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Channels/ReadableChannelAwaitable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace Channels 5 | { 6 | /// 7 | /// An awaitable object that represents an asynchronous read operation 8 | /// 9 | public struct ReadableChannelAwaitable : ICriticalNotifyCompletion 10 | { 11 | private readonly IReadableBufferAwaiter _awaiter; 12 | 13 | public ReadableChannelAwaitable(IReadableBufferAwaiter awaiter) 14 | { 15 | _awaiter = awaiter; 16 | } 17 | 18 | public bool IsCompleted => _awaiter.IsCompleted; 19 | 20 | public ChannelReadResult GetResult() => _awaiter.GetResult(); 21 | 22 | public ReadableChannelAwaitable GetAwaiter() => this; 23 | 24 | public void UnsafeOnCompleted(Action continuation) => _awaiter.OnCompleted(continuation); 25 | 26 | public void OnCompleted(Action continuation) => _awaiter.OnCompleted(continuation); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Channels.Networking.Libuv/Interop/UvPipeHandle.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | 6 | namespace Channels.Networking.Libuv.Interop 7 | { 8 | public class UvPipeHandle : UvStreamHandle 9 | { 10 | public UvPipeHandle() : base() 11 | { 12 | } 13 | 14 | public void Init(UvLoopHandle loop, Action, IntPtr> queueCloseHandle, bool ipc = false) 15 | { 16 | CreateHandle( 17 | loop.Libuv, 18 | loop.ThreadId, 19 | loop.Libuv.handle_size(Uv.HandleType.NAMED_PIPE), queueCloseHandle); 20 | 21 | _uv.pipe_init(loop, this, ipc); 22 | } 23 | 24 | public void Bind(string name) 25 | { 26 | _uv.pipe_bind(this, name); 27 | } 28 | 29 | public int PendingCount() 30 | { 31 | return _uv.pipe_pending_count(this); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Channels.Networking.Windows.RIO/Internal/Winsock/RioExtensionFunctionTable.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | using System.Runtime.InteropServices; 6 | 7 | namespace Channels.Networking.Windows.RIO.Internal.Winsock 8 | { 9 | [StructLayout(LayoutKind.Sequential)] 10 | public struct RioExtensionFunctionTable 11 | { 12 | public UInt32 Size; 13 | 14 | public IntPtr RIOReceive; 15 | public IntPtr RIOReceiveEx; 16 | public IntPtr RIOSend; 17 | public IntPtr RIOSendEx; 18 | public IntPtr RIOCloseCompletionQueue; 19 | public IntPtr RIOCreateCompletionQueue; 20 | public IntPtr RIOCreateRequestQueue; 21 | public IntPtr RIODequeueCompletion; 22 | public IntPtr RIODeregisterBuffer; 23 | public IntPtr RIONotify; 24 | public IntPtr RIORegisterBuffer; 25 | public IntPtr RIOResizeCompletionQueue; 26 | public IntPtr RIOResizeRequestQueue; 27 | } 28 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.doc diff=astextplain 2 | *.DOC diff=astextplain 3 | *.docx diff=astextplain 4 | *.DOCX diff=astextplain 5 | *.dot diff=astextplain 6 | *.DOT diff=astextplain 7 | *.pdf diff=astextplain 8 | *.PDF diff=astextplain 9 | *.rtf diff=astextplain 10 | *.RTF diff=astextplain 11 | 12 | *.jpg binary 13 | *.png binary 14 | *.gif binary 15 | 16 | *.cs text=auto diff=csharp 17 | *.vb text=auto 18 | *.resx text=auto 19 | *.c text=auto 20 | *.cpp text=auto 21 | *.cxx text=auto 22 | *.h text=auto 23 | *.hxx text=auto 24 | *.py text=auto 25 | *.rb text=auto 26 | *.java text=auto 27 | *.html text=auto 28 | *.htm text=auto 29 | *.css text=auto 30 | *.scss text=auto 31 | *.sass text=auto 32 | *.less text=auto 33 | *.js text=auto 34 | *.lisp text=auto 35 | *.clj text=auto 36 | *.sql text=auto 37 | *.php text=auto 38 | *.lua text=auto 39 | *.m text=auto 40 | *.asm text=auto 41 | *.erl text=auto 42 | *.fs text=auto 43 | *.fsx text=auto 44 | *.hs text=auto 45 | 46 | *.csproj text=auto 47 | *.vbproj text=auto 48 | *.fsproj text=auto 49 | *.dbproj text=auto 50 | *.sln text=auto eol=crlf 51 | 52 | *.sh eol=lf -------------------------------------------------------------------------------- /samples/Channels.Samples/Models/Pet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace Channels.Samples.Models 7 | { 8 | public class Pet 9 | { 10 | public int Id { get; set; } 11 | 12 | public int Age { get; set; } 13 | 14 | public Category Category { get; set; } 15 | 16 | public bool HasVaccinations { get; set; } 17 | 18 | public string Name { get; set; } 19 | 20 | public List Images { get; set; } 21 | 22 | public List Tags { get; set; } 23 | 24 | public string Status { get; set; } 25 | } 26 | 27 | public class Image 28 | { 29 | public int Id { get; set; } 30 | 31 | public string Url { get; set; } 32 | } 33 | 34 | public class Tag 35 | { 36 | public int Id { get; set; } 37 | 38 | public string Name { get; set; } 39 | } 40 | 41 | public class Category 42 | { 43 | public int Id { get; set; } 44 | 45 | public string Name { get; set; } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | os: Visual Studio 2015 2 | build: off 3 | 4 | before_test: 5 | - dotnet --info 6 | - dotnet restore 7 | 8 | test_script: 9 | # Build sample 10 | - dotnet build samples/Channels.Samples 11 | - dotnet test test/Channels.Tests 12 | 13 | after_test: 14 | # Build and pack source 15 | - dotnet pack -c Release src/Channels --version-suffix %APPVEYOR_BUILD_NUMBER% 16 | - dotnet pack -c Release src/Channels.Networking.Libuv --version-suffix %APPVEYOR_BUILD_NUMBER% 17 | - dotnet pack -c Release src/Channels.Networking.Windows.RIO --version-suffix %APPVEYOR_BUILD_NUMBER% 18 | - dotnet pack -c Release src/Channels.Networking.Sockets --version-suffix %APPVEYOR_BUILD_NUMBER% 19 | - dotnet pack -c Release src/Channels.Text.Primitives --version-suffix %APPVEYOR_BUILD_NUMBER% 20 | 21 | 22 | artifacts: 23 | path: '**/*.nupkg' 24 | 25 | deploy: 26 | - provider: NuGet 27 | server: https://www.myget.org/F/channels/api/v2/package 28 | api_key: 29 | secure: OvE2o5re489/Dr0nt3p9UzpcIcLhSt7FoHyvRUuniUbj5Wkx9+i9fBYuwmDGtxnw 30 | skip_symbols: true 31 | on: 32 | branch: master 33 | -------------------------------------------------------------------------------- /src/Channels.Networking.Windows.RIO/Internal/Winsock/RegisteredIO.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | namespace Channels.Networking.Windows.RIO.Internal.Winsock 5 | { 6 | public sealed class RegisteredIO 7 | { 8 | public RioRegisterBuffer RioRegisterBuffer; 9 | 10 | public RioCreateCompletionQueue RioCreateCompletionQueue; 11 | public RioCreateRequestQueue RioCreateRequestQueue; 12 | 13 | 14 | public RioReceive RioReceive; 15 | public RioSend Send; 16 | 17 | public RioNotify Notify; 18 | 19 | public RioCloseCompletionQueue CloseCompletionQueue; 20 | public RioDequeueCompletion DequeueCompletion; 21 | public RioDeregisterBuffer DeregisterBuffer; 22 | public RioResizeCompletionQueue ResizeCompletionQueue; 23 | public RioResizeRequestQueue ResizeRequestQueue; 24 | 25 | 26 | public const long CachedValue = long.MinValue; 27 | 28 | public RegisteredIO() 29 | { 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/Channels.Networking.Windows.RIO/Internal/Winsock/Version.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | namespace Channels.Networking.Windows.RIO.Internal.Winsock 5 | { 6 | public struct Version 7 | { 8 | public ushort Raw; 9 | 10 | public Version(byte major, byte minor) 11 | { 12 | Raw = major; 13 | Raw <<= 8; 14 | Raw += minor; 15 | } 16 | 17 | public byte Major 18 | { 19 | get 20 | { 21 | ushort result = Raw; 22 | result >>= 8; 23 | return (byte)result; 24 | } 25 | } 26 | 27 | public byte Minor 28 | { 29 | get 30 | { 31 | ushort result = Raw; 32 | result &= 0x00FF; 33 | return (byte)result; 34 | } 35 | } 36 | 37 | public override string ToString() 38 | { 39 | return string.Format("{0}.{1}", Major, Minor); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /test/Channels.Tests/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "buildOptions": { 3 | "warningsAsErrors": true, 4 | "keyFile": "../../tools/Key.snk", 5 | "allowUnsafe": true 6 | }, 7 | "dependencies": { 8 | "dotnet-test-xunit": "2.2.0-preview2-build1029", 9 | "Channels": { 10 | "target": "project" 11 | }, 12 | "Channels.Text.Primitives": { 13 | "target": "project" 14 | }, 15 | "Channels.Networking.Sockets": { 16 | "target": "project" 17 | }, 18 | "Channels.Networking.Libuv": { 19 | "target": "project" 20 | }, 21 | "Channels.Networking.Windows.RIO": { 22 | "target": "project" 23 | }, 24 | "xunit": "2.2.0-beta2-build3300", 25 | "System.Collections.Sequences": "0.1.0-*" 26 | }, 27 | "frameworks": { 28 | "netcoreapp1.0": { 29 | "dependencies": { 30 | "Microsoft.NETCore.App": { 31 | "version": "1.0.0-*", 32 | "type": "platform" 33 | }, 34 | "Microsoft.CodeCoverage": { 35 | "type": "build", 36 | "version": "1.0.1" 37 | } 38 | }, 39 | "imports": "dnxcore50" 40 | } 41 | }, 42 | "testRunner": "xunit" 43 | } -------------------------------------------------------------------------------- /src/Channels/Channels.xproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | 10 | d6b28fc7-8d6d-4e9e-962f-9505d7c0e958 11 | Channels 12 | .\obj 13 | .\bin\ 14 | v4.5.2 15 | 16 | 17 | 18 | 2.0 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/Channels.File/Channels.File.xproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | 10 | 8de69699-bae7-4186-b452-27923928a0b1 11 | Channels.File 12 | .\obj 13 | .\bin\ 14 | v4.6.2 15 | 16 | 17 | 18 | 2.0 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /samples/Channels.Samples/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0-*", 3 | "buildOptions": { 4 | "allowUnsafe": true, 5 | "emitEntryPoint": true 6 | }, 7 | 8 | "dependencies": { 9 | "Channels": { 10 | "target": "project" 11 | }, 12 | "Channels.Text.Primitives": { 13 | "target": "project" 14 | }, 15 | "Channels.Networking.Libuv": { 16 | "target": "project" 17 | }, 18 | "Channels.Networking.Windows.RIO": { 19 | "target": "project" 20 | }, 21 | "Channels.File": { 22 | "target": "project" 23 | }, 24 | "Channels.Compression": { 25 | "target": "project" 26 | }, 27 | "Newtonsoft.Json": "9.0.1", 28 | "System.Buffers": "4.0.0", 29 | "System.Slices": "0.1.0-*", 30 | "System.Text.Primitives": "0.1.0-*", 31 | "System.Text.Formatting": "0.1.0-*", 32 | "System.Text.Utf8": "0.1.0-*", 33 | "Microsoft.AspNetCore.Server.Kestrel": "1.0.0", 34 | "Microsoft.NETCore.App": { 35 | "type": "platform", 36 | "version": "1.0.0" 37 | } 38 | }, 39 | 40 | "frameworks": { 41 | "netcoreapp1.0": { } 42 | }, 43 | 44 | "runtimeOptions": { 45 | "configProperties": { 46 | "System.GC.Server": true 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /samples/Channels.Samples/Channels.Samples.xproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | 10 | 3dcb0f92-51ae-493e-a1b6-5b42449ea2bd 11 | Channels.Samples 12 | .\obj 13 | .\bin\ 14 | v4.5.2 15 | 16 | 17 | 18 | 2.0 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/Channels.Compression/Channels.Compression.xproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | 10 | 4fd538a6-423f-4554-af10-427a959ca9d3 11 | Channels.Compression 12 | .\obj 13 | .\bin\ 14 | v4.6.2 15 | 16 | 17 | 18 | 2.0 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/Channels.Networking.Sockets/Channels.Networking.Sockets.xproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | 1932e4b5-a40e-4eef-ad8f-012dc01f5e5f 10 | Channels.Networking.Sockets 11 | .\obj 12 | .\bin\ 13 | v4.5.2 14 | 15 | 16 | 2.0 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Channels.Text.Primitives/Channels.Text.Primitives.xproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | 10 | a98068ae-c895-4b1f-adcb-60c70c64f118 11 | Channels.Text.Primitives 12 | .\obj 13 | .\bin\ 14 | v4.5.2 15 | 16 | 17 | 18 | 2.0 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/Channels.Networking.Libuv/Channels.Networking.Libuv.xproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | 10 | 11d6b886-a303-4d13-bca8-a917d5740fb6 11 | Channels.Networking.Libuv 12 | .\obj 13 | .\bin\ 14 | v4.5.2 15 | 16 | 17 | 18 | 2.0 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/Channels.Networking.Windows.RIO/Channels.Networking.Windows.RIO.xproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | 10 | 86a0b10d-8c7a-4b20-a033-d6f679454e30 11 | Channels.Networking.Windows.RIO 12 | .\obj 13 | .\bin\ 14 | v4.5.2 15 | 16 | 17 | 18 | 2.0 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/Channels/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyProduct("Channels")] 11 | [assembly: AssemblyTrademark("")] 12 | 13 | // Setting ComVisible to false makes the types in this assembly not visible 14 | // to COM components. If you need to access a type in this assembly from 15 | // COM, set the ComVisible attribute to true on that type. 16 | [assembly: ComVisible(false)] 17 | 18 | // The following GUID is for the ID of the typelib if this project is exposed to COM 19 | [assembly: Guid("d6b28fc7-8d6d-4e9e-962f-9505d7c0e958")] 20 | 21 | [assembly: InternalsVisibleTo("Channels.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100F33A29044FA9D740C9B3213A93E57C84B472C84E0B8A0E1AE48E67A9F8F6DE9D5F7F3D52AC23E48AC51801F1DC950ABE901DA34D2A9E3BAADB141A17C77EF3C565DD5EE5054B91CF63BB3C6AB83F72AB3AAFE93D0FC3C2348B764FAFB0B1C0733DE51459AEAB46580384BF9D74C4E28164B7CDE247F891BA07891C9D872AD2BB")] -------------------------------------------------------------------------------- /test/Channels.Tests.Performance/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "buildOptions": { 3 | "warningsAsErrors": true, 4 | "keyFile": "../../tools/Key.snk", 5 | "allowUnsafe": true, 6 | "emitEntryPoint": true, 7 | "copyToOutput": "random.json.gz" 8 | }, 9 | 10 | "publishOptions": { 11 | "includeFiles": [ "random.json.gz" ] 12 | }, 13 | 14 | "dependencies": { 15 | "Channels": { 16 | "target": "project" 17 | }, 18 | "Channels.Text.Primitives": { 19 | "target": "project" 20 | }, 21 | "Channels.Networking.Sockets": { 22 | "target": "project" 23 | }, 24 | "Channels.Networking.Libuv": { 25 | "target": "project" 26 | }, 27 | "Channels.Networking.Windows.RIO": { 28 | "target": "project" 29 | }, 30 | "Channels.File": { 31 | "target": "project" 32 | }, 33 | "Channels.Compression": { 34 | "target": "project" 35 | }, 36 | "Newtonsoft.Json": "9.0.1", 37 | "System.Text.Json": "0.1.0-*" 38 | }, 39 | "frameworks": { 40 | "net46": { 41 | "imports": "dotnet", 42 | "dependencies": { 43 | "BenchmarkDotNet": "0.9.9", 44 | "BenchmarkDotNet.Diagnostics.Windows": "0.9.9" 45 | } 46 | } 47 | }, 48 | 49 | "contentExclude": [ "BenchmarkDotNet.Artifacts**" ] 50 | } -------------------------------------------------------------------------------- /src/Channels.Networking.Sockets/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyProduct("Channels.Networking.Sockets")] 11 | [assembly: AssemblyTrademark("")] 12 | 13 | // Setting ComVisible to false makes the types in this assembly not visible 14 | // to COM components. If you need to access a type in this assembly from 15 | // COM, set the ComVisible attribute to true on that type. 16 | [assembly: ComVisible(false)] 17 | 18 | // The following GUID is for the ID of the typelib if this project is exposed to COM 19 | [assembly: Guid("1932e4b5-a40e-4eef-ad8f-012dc01f5e5f")] 20 | 21 | [assembly: InternalsVisibleTo("Channels.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100F33A29044FA9D740C9B3213A93E57C84B472C84E0B8A0E1AE48E67A9F8F6DE9D5F7F3D52AC23E48AC51801F1DC950ABE901DA34D2A9E3BAADB141A17C77EF3C565DD5EE5054B91CF63BB3C6AB83F72AB3AAFE93D0FC3C2348B764FAFB0B1C0733DE51459AEAB46580384BF9D74C4E28164B7CDE247F891BA07891C9D872AD2BB")] -------------------------------------------------------------------------------- /test/Channels.Tests/Channels.Tests.xproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | b9967782-565b-4b0b-97b9-043e35022674 10 | Channels.Tests 11 | .\obj 12 | .\bin\ 13 | v4.5.2 14 | 15 | 16 | 2.0 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /test/Channels.Tests.Performance/Channels.Tests.Performance.xproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | fd7d92ee-542c-424b-b66a-9ac2a214ec90 10 | Channels.Tests.Performance 11 | .\obj 12 | .\bin\ 13 | v4.6.2 14 | 15 | 16 | 2.0 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/Channels/IWritableChannel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace Channels 5 | { 6 | /// 7 | /// Defines a class that provides a channel to which data can be written. 8 | /// 9 | public interface IWritableChannel 10 | { 11 | /// 12 | /// Gets a task that completes when no more data will be read from the channel. 13 | /// 14 | /// 15 | /// This task indicates the consumer has completed and will not read anymore data. 16 | /// When this task is triggered, the producer should stop producing data. 17 | /// 18 | Task Writing { get; } 19 | 20 | /// 21 | /// Allocates memory from the channel to write into. 22 | /// 23 | /// The minimum size buffer to allocate 24 | /// A that can be written to. 25 | WritableBuffer Alloc(int minimumSize = 0); 26 | 27 | /// 28 | /// Marks the channel as being complete, meaning no more items will be written to it. 29 | /// 30 | /// Optional Exception indicating a failure that's causing the channel to complete. 31 | void Complete(Exception exception = null); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Channels.Networking.Libuv/Interop/UvLoopHandle.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Threading; 6 | 7 | namespace Channels.Networking.Libuv.Interop 8 | { 9 | public class UvLoopHandle : UvMemory 10 | { 11 | public UvLoopHandle() : base() 12 | { 13 | } 14 | 15 | public void Init(Uv uv) 16 | { 17 | CreateMemory( 18 | uv, 19 | Thread.CurrentThread.ManagedThreadId, 20 | uv.loop_size()); 21 | 22 | _uv.loop_init(this); 23 | } 24 | 25 | public void Run(int mode = 0) 26 | { 27 | _uv.run(this, mode); 28 | } 29 | 30 | public void Stop() 31 | { 32 | _uv.stop(this); 33 | } 34 | 35 | unsafe protected override bool ReleaseHandle() 36 | { 37 | var memory = handle; 38 | if (memory != IntPtr.Zero) 39 | { 40 | // loop_close clears the gcHandlePtr 41 | var gcHandlePtr = *(IntPtr*)memory; 42 | 43 | _uv.loop_close(this); 44 | handle = IntPtr.Zero; 45 | 46 | DestroyMemory(memory, gcHandlePtr); 47 | } 48 | 49 | return true; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Channels.Networking.Sockets/Internal/SocketExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Sockets; 2 | 3 | namespace Channels.Networking.Sockets.Internal 4 | { 5 | internal static class SocketExtensions 6 | { 7 | /// 8 | /// Note that this presumes that args.UserToken is already a Signal, and that args.Completed 9 | /// knows to call .Set on the Signal 10 | /// 11 | public static Signal ReceiveSignalAsync(this Socket socket, SocketAsyncEventArgs args) 12 | { 13 | var signal = (Signal)args.UserToken; 14 | signal.Reset(); 15 | if (!socket.ReceiveAsync(args)) 16 | { // mark it as already complete (probably an error) 17 | signal.Set(); 18 | } 19 | return signal; 20 | } 21 | 22 | /// 23 | /// Note that this presumes that args.UserToken is already a Signal, and that args.Completed 24 | /// knows to call .Set on the Signal 25 | /// 26 | public static Signal SendSignalAsync(this Socket socket, SocketAsyncEventArgs args) 27 | { 28 | var signal = (Signal)args.UserToken; 29 | signal.Reset(); 30 | if (!socket.SendAsync(args)) 31 | { // mark it as already complete (probably an error) 32 | signal.Set(); 33 | } 34 | return signal; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": ".NET Core Launch (web)", 6 | "type": "coreclr", 7 | "request": "launch", 8 | "preLaunchTask": "build", 9 | "program": "${workspaceRoot}/samples/Channels.Samples/bin/Debug/netcoreapp1.0/Channels.Samples.dll", 10 | "args": [], 11 | "cwd": "${workspaceRoot}", 12 | "stopAtEntry": false, 13 | "internalConsoleOptions": "openOnSessionStart", 14 | "launchBrowser": { 15 | "enabled": true, 16 | "args": "${auto-detect-url}", 17 | "windows": { 18 | "command": "cmd.exe", 19 | "args": "/C start ${auto-detect-url}" 20 | }, 21 | "osx": { 22 | "command": "open" 23 | }, 24 | "linux": { 25 | "command": "xdg-open" 26 | } 27 | }, 28 | "env": { 29 | "ASPNETCORE_ENVIRONMENT": "Development" 30 | }, 31 | "sourceFileMap": { 32 | "/Views": "${workspaceRoot}/Views" 33 | } 34 | }, 35 | { 36 | "name": ".NET Core Attach", 37 | "type": "coreclr", 38 | "request": "attach", 39 | "processId": "${command.pickProcess}" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /src/Channels/WritableChannel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace Channels 5 | { 6 | public abstract class WritableChannel : IWritableChannel 7 | { 8 | private readonly Channel _channel; 9 | 10 | public WritableChannel(IBufferPool pool) 11 | { 12 | _channel = new Channel(pool); 13 | 14 | Consume(_channel); 15 | } 16 | 17 | protected abstract Task WriteAsync(ReadableBuffer buffer); 18 | 19 | public Task Writing => _channel.Writing; 20 | 21 | public WritableBuffer Alloc(int minimumSize = 0) => _channel.Alloc(minimumSize); 22 | 23 | public void Complete(Exception exception = null) => _channel.CompleteWriter(exception); 24 | 25 | private async void Consume(IReadableChannel channel) 26 | { 27 | while (true) 28 | { 29 | var result = await channel.ReadAsync(); 30 | var buffer = result.Buffer; 31 | 32 | try 33 | { 34 | if (buffer.IsEmpty && result.IsCompleted) 35 | { 36 | break; 37 | } 38 | 39 | await WriteAsync(buffer); 40 | } 41 | finally 42 | { 43 | channel.Advance(buffer.End); 44 | } 45 | } 46 | 47 | channel.Complete(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Channels/PreservedBuffer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | 6 | namespace Channels 7 | { 8 | /// 9 | /// Represents a buffer that can read a sequential series of bytes. 10 | /// 11 | public struct PreservedBuffer : IDisposable 12 | { 13 | private ReadableBuffer _buffer; 14 | 15 | internal PreservedBuffer(ref ReadableBuffer buffer) 16 | { 17 | _buffer = buffer; 18 | } 19 | 20 | /// 21 | /// Returns the preserved . 22 | /// 23 | public ReadableBuffer Buffer => _buffer; 24 | 25 | /// 26 | /// Dispose the preserved buffer. 27 | /// 28 | public void Dispose() 29 | { 30 | var returnStart = _buffer.Start.Segment; 31 | var returnEnd = _buffer.End.Segment; 32 | 33 | while (true) 34 | { 35 | var returnSegment = returnStart; 36 | returnStart = returnStart?.Next; 37 | returnSegment?.Dispose(); 38 | 39 | if (returnSegment == returnEnd) 40 | { 41 | break; 42 | } 43 | } 44 | 45 | _buffer.ClearCursors(); 46 | } 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: generic 2 | 3 | addons: 4 | apt: 5 | packages: 6 | - gettext 7 | - libcurl4-openssl-dev 8 | - libicu-dev 9 | - libssl-dev 10 | - libunwind8 11 | - zlib1g 12 | 13 | env: 14 | global: 15 | - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true 16 | - DOTNET_CLI_TELEMETRY_OPTOUT: 1 17 | 18 | matrix: 19 | include: 20 | - os: linux 21 | dist: trusty 22 | sudo: required 23 | - os: osx 24 | osx_image: xcode7.2 25 | 26 | before_install: 27 | # Install OpenSSL 28 | - if test "$TRAVIS_OS_NAME" == "osx"; then 29 | brew install openssl; 30 | brew link --force openssl; 31 | export DOTNET_SDK_URL="https://go.microsoft.com/fwlink/?LinkID=809128"; 32 | else 33 | export DOTNET_SDK_URL="https://go.microsoft.com/fwlink/?LinkID=809129"; 34 | fi 35 | 36 | - export DOTNET_INSTALL_DIR="$PWD/.dotnetcli" 37 | 38 | # Install .NET CLI 39 | - mkdir $DOTNET_INSTALL_DIR 40 | - curl -L $DOTNET_SDK_URL -o dotnet_package 41 | - tar -xvzf dotnet_package -C $DOTNET_INSTALL_DIR 42 | 43 | # Add dotnet to PATH 44 | - export PATH="$DOTNET_INSTALL_DIR:$PATH" 45 | 46 | install: 47 | # Display dotnet version info 48 | - which dotnet; 49 | if [ $? -eq 0 ]; then 50 | echo "Using dotnet:"; 51 | dotnet --info; 52 | else 53 | echo "dotnet.exe not found" 54 | exit 1; 55 | fi 56 | 57 | # Restore dependencies 58 | - dotnet restore 59 | 60 | script: 61 | # Build sample 62 | - dotnet build samples/Channels.Samples 63 | - dotnet test test/Channels.Tests 64 | -------------------------------------------------------------------------------- /src/Channels/IReadableChannel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Channels 4 | { 5 | /// 6 | /// Defines a class that provides a channel from which data can be read. 7 | /// 8 | public interface IReadableChannel 9 | { 10 | /// 11 | /// Asynchronously reads a sequence of bytes from the current . 12 | /// 13 | /// A representing the asynchronous read operation. 14 | ReadableChannelAwaitable ReadAsync(); 15 | 16 | /// 17 | /// Moves forward the channel's read cursor to after the consumed data. 18 | /// 19 | /// Marks the extent of the data that has been succesfully proceesed. 20 | /// Marks the extent of the data that has been read and examined. 21 | /// 22 | /// The memory for the consumed data will be released and no longer available. 23 | /// The examined data communicates to the channel when it should signal more data is available. 24 | /// 25 | void Advance(ReadCursor consumed, ReadCursor examined); 26 | 27 | /// 28 | /// Signal to the producer that the consumer is done reading. 29 | /// 30 | /// Optional Exception indicating a failure that's causing the channel to complete. 31 | void Complete(Exception exception = null); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /samples/Channels.Samples/CompressionSample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.IO.Compression; 4 | using Channels.Compression; 5 | using Channels.File; 6 | 7 | namespace Channels.Samples 8 | { 9 | public class CompressionSample 10 | { 11 | public static void Run() 12 | { 13 | using (var cf = new ChannelFactory()) 14 | { 15 | var filePath = Path.GetFullPath("Program.cs"); 16 | 17 | // This is what Stream looks like 18 | //var fs = File.OpenRead(filePath); 19 | //var compressed = new MemoryStream(); 20 | //var compressStream = new DeflateStream(compressed, CompressionMode.Compress); 21 | //fs.CopyTo(compressStream); 22 | //compressStream.Flush(); 23 | //compressed.Seek(0, SeekOrigin.Begin); 24 | // var input = channelFactory.MakeReadableChannel(compressed); 25 | 26 | var input = cf.ReadFile(filePath) 27 | .DeflateCompress(cf, CompressionLevel.Optimal) 28 | .DeflateDecompress(cf); 29 | 30 | // Wrap the console in a writable channel 31 | var output = cf.MakeWriteableChannel(Console.OpenStandardOutput()); 32 | 33 | // Copy from the file channel to the console channel 34 | input.CopyToAsync(output).GetAwaiter().GetResult(); 35 | 36 | input.Complete(); 37 | 38 | output.Complete(); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Channels.Networking.Libuv/Interop/UvShutdownReq.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | 6 | namespace Channels.Networking.Libuv.Interop 7 | { 8 | /// 9 | /// Summary description for UvShutdownRequest 10 | /// 11 | public class UvShutdownReq : UvRequest 12 | { 13 | private readonly static Uv.uv_shutdown_cb _uv_shutdown_cb = UvShutdownCb; 14 | 15 | private Action _callback; 16 | private object _state; 17 | 18 | public UvShutdownReq() : base () 19 | { 20 | } 21 | 22 | public void Init(UvLoopHandle loop) 23 | { 24 | CreateMemory( 25 | loop.Libuv, 26 | loop.ThreadId, 27 | loop.Libuv.req_size(Uv.RequestType.SHUTDOWN)); 28 | } 29 | 30 | public void Shutdown(UvStreamHandle handle, Action callback, object state) 31 | { 32 | _callback = callback; 33 | _state = state; 34 | Pin(); 35 | _uv.shutdown(this, handle, _uv_shutdown_cb); 36 | } 37 | 38 | private static void UvShutdownCb(IntPtr ptr, int status) 39 | { 40 | var req = FromIntPtr(ptr); 41 | req.Unpin(); 42 | req._callback(req, status, req._state); 43 | req._callback = null; 44 | req._state = null; 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /src/Channels.Text.Primitives/WritableChannelFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Text.Formatting; 6 | using System.Text; 7 | 8 | namespace Channels.Text.Primitives 9 | { 10 | public class WritableChannelFormatter : ITextOutput 11 | { 12 | private readonly IWritableChannel _channel; 13 | private WritableBuffer _writableBuffer; 14 | private bool _needAlloc = true; 15 | 16 | public WritableChannelFormatter(IWritableChannel channel, EncodingData encoding) 17 | { 18 | _channel = channel; 19 | Encoding = encoding; 20 | } 21 | 22 | public EncodingData Encoding { get; } 23 | 24 | public Span Buffer 25 | { 26 | get 27 | { 28 | EnsureBuffer(); 29 | 30 | return _writableBuffer.Memory.Span; 31 | } 32 | } 33 | 34 | public void Advance(int bytes) 35 | { 36 | _writableBuffer.Advance(bytes); 37 | } 38 | 39 | public void Enlarge(int desiredFreeBytesHint = 0) 40 | { 41 | _writableBuffer.Ensure(desiredFreeBytesHint == 0 ? 2048 : desiredFreeBytesHint); 42 | } 43 | 44 | public void Write(Span data) 45 | { 46 | EnsureBuffer(); 47 | _writableBuffer.Write(data); 48 | } 49 | 50 | public async Task FlushAsync() 51 | { 52 | await _writableBuffer.FlushAsync(); 53 | _needAlloc = true; 54 | } 55 | 56 | private void EnsureBuffer() 57 | { 58 | if (_needAlloc) 59 | { 60 | _writableBuffer = _channel.Alloc(); 61 | _needAlloc = false; 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Channels.Text.Primitives/SplitEnumerable.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | 4 | namespace Channels.Text.Primitives 5 | { 6 | /// 7 | /// Exposes the enumerator, which supports a simple iteration over a collection of a specified type. 8 | /// 9 | public struct SplitEnumerable : IEnumerable 10 | { 11 | private ReadableBuffer _buffer; 12 | 13 | private int _count; 14 | 15 | private byte _delimiter; 16 | 17 | internal SplitEnumerable(ReadableBuffer buffer, byte delimiter) 18 | { 19 | _buffer = buffer; 20 | _delimiter = delimiter; 21 | _count = buffer.IsEmpty ? 0 : -1; 22 | } 23 | 24 | /// 25 | /// Count the number of elemnts in this sequence 26 | /// 27 | public int Count() 28 | { 29 | if (_count >= 0) 30 | { 31 | return _count; 32 | } 33 | 34 | int count = 1; 35 | var current = _buffer; 36 | ReadableBuffer ignore; 37 | ReadCursor cursor; 38 | while (current.TrySliceTo(_delimiter, out ignore, out cursor)) 39 | { 40 | current = current.Slice(cursor).Slice(1); 41 | count++; 42 | } 43 | return _count = count; 44 | } 45 | /// 46 | /// Returns an enumerator that iterates through the collection. 47 | /// 48 | public SplitEnumerator GetEnumerator() 49 | => new SplitEnumerator(_buffer, _delimiter); 50 | 51 | IEnumerator IEnumerable.GetEnumerator() 52 | => GetEnumerator(); 53 | 54 | IEnumerator IEnumerable.GetEnumerator() 55 | => GetEnumerator(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /samples/Channels.Samples/AspNetHttpServerSample.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using Channels.Samples.Http; 3 | using Microsoft.AspNetCore.Builder; 4 | using Microsoft.AspNetCore.Hosting; 5 | 6 | namespace Channels.Samples 7 | { 8 | public class AspNetHttpServerSample 9 | { 10 | private static readonly UTF8Encoding _utf8Encoding = new UTF8Encoding(false); 11 | private static readonly byte[] _helloWorldPayload = Encoding.UTF8.GetBytes("Hello, World!"); 12 | 13 | public static void Run() 14 | { 15 | using (var httpServer = new HttpServer()) 16 | { 17 | var host = new WebHostBuilder() 18 | .UseUrls("http://*:5000") 19 | .UseServer(httpServer) 20 | // .UseKestrel() 21 | .Configure(app => 22 | { 23 | app.Run(context => 24 | { 25 | context.Response.StatusCode = 200; 26 | context.Response.ContentType = "text/plain"; 27 | // HACK: Setting the Content-Length header manually avoids the cost of serializing the int to a string. 28 | // This is instead of: httpContext.Response.ContentLength = _helloWorldPayload.Length; 29 | context.Response.Headers["Content-Length"] = "13"; 30 | return context.Response.Body.WriteAsync(_helloWorldPayload, 0, _helloWorldPayload.Length); 31 | }); 32 | }) 33 | .Build(); 34 | host.Run(); 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/Channels.Networking.Libuv/Internal/WriteReqPool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Channels.Networking.Libuv.Interop; 4 | 5 | namespace Channels.Networking.Libuv.Internal 6 | { 7 | public class WriteReqPool 8 | { 9 | private const int _maxPooledWriteReqs = 1024; 10 | 11 | private readonly UvThread _thread; 12 | private readonly Queue _pool = new Queue(_maxPooledWriteReqs); 13 | private bool _disposed; 14 | 15 | public WriteReqPool(UvThread thread) 16 | { 17 | _thread = thread; 18 | } 19 | 20 | public UvWriteReq Allocate() 21 | { 22 | if (_disposed) 23 | { 24 | throw new ObjectDisposedException(GetType().Name); 25 | } 26 | 27 | UvWriteReq req; 28 | if (_pool.Count > 0) 29 | { 30 | req = _pool.Dequeue(); 31 | } 32 | else 33 | { 34 | req = new UvWriteReq(); 35 | req.Init(_thread.Loop); 36 | } 37 | 38 | return req; 39 | } 40 | 41 | public void Return(UvWriteReq req) 42 | { 43 | if (_disposed) 44 | { 45 | throw new ObjectDisposedException(GetType().Name); 46 | } 47 | 48 | if (_pool.Count < _maxPooledWriteReqs) 49 | { 50 | _pool.Enqueue(req); 51 | } 52 | else 53 | { 54 | req.Dispose(); 55 | } 56 | } 57 | 58 | public void Dispose() 59 | { 60 | if (!_disposed) 61 | { 62 | _disposed = true; 63 | 64 | while (_pool.Count > 0) 65 | { 66 | _pool.Dequeue().Dispose(); 67 | } 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /test/Channels.Tests.Performance/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using BenchmarkDotNet.Configs; 3 | using BenchmarkDotNet.Diagnostics.Windows; 4 | using BenchmarkDotNet.Jobs; 5 | using BenchmarkDotNet.Running; 6 | 7 | namespace Channels.Tests.Performance 8 | { 9 | public class Program 10 | { 11 | public static void Main(string[] args) 12 | { 13 | var options = (uint[])Enum.GetValues(typeof(BenchmarkType)); 14 | BenchmarkType type; 15 | if (args.Length != 1 || !Enum.TryParse(args[0], out type)) 16 | { 17 | Console.WriteLine($"Please add benchmark to run as parameter:"); 18 | for (var i = 0; i < options.Length; i++) 19 | { 20 | Console.WriteLine($" {((BenchmarkType)options[i]).ToString()}" ); 21 | } 22 | 23 | return; 24 | } 25 | 26 | RunSelectedBenchmarks(type); 27 | } 28 | 29 | private static void RunSelectedBenchmarks(BenchmarkType type) 30 | { 31 | if (type.HasFlag(BenchmarkType.Streams)) 32 | { 33 | BenchmarkRunner.Run(); 34 | } 35 | } 36 | } 37 | 38 | [Flags] 39 | public enum BenchmarkType : uint 40 | { 41 | Streams = 1, 42 | // add new ones in powers of two - e.g. 2,4,8,16... 43 | 44 | All = uint.MaxValue 45 | } 46 | 47 | public class DefaultConfig : ManualConfig 48 | { 49 | public DefaultConfig() 50 | { 51 | Add(Job.Default. 52 | With(Platform.X64). 53 | With(Jit.RyuJit). 54 | With(Runtime.Clr). 55 | WithLaunchCount(3). 56 | WithIterationTime(200). // 200ms per iteration 57 | WithWarmupCount(5). 58 | WithTargetCount(10)); 59 | 60 | Add(new MemoryDiagnoser()); 61 | } 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /test/Channels.Tests/BufferPoolFacts.cs: -------------------------------------------------------------------------------- 1 | using Channels.Networking.Sockets.Internal; 2 | using System; 3 | using Xunit; 4 | 5 | namespace Channels.Tests 6 | { 7 | public class BufferPoolFacts 8 | { 9 | [Fact] 10 | public void BufferPoolBasicUsage() 11 | { 12 | var pool = new MicroBufferPool(8, 4); 13 | 14 | ArraySegment[] segments = new ArraySegment[5]; 15 | 16 | Assert.Equal(0, pool.InUse); 17 | Assert.Equal(4, pool.Available); 18 | Assert.True(pool.TryTake(out segments[0])); 19 | Assert.True(pool.TryTake(out segments[1])); 20 | Assert.Equal(2, pool.InUse); 21 | Assert.Equal(2, pool.Available); 22 | Assert.True(pool.TryTake(out segments[2])); 23 | Assert.True(pool.TryTake(out segments[3])); 24 | Assert.False(pool.TryTake(out segments[4])); 25 | Assert.Equal(4, pool.InUse); 26 | Assert.Equal(0, pool.Available); 27 | for (int i = 0; i < 4; i++) 28 | { 29 | Assert.Equal(i * 8, segments[i].Offset); 30 | Assert.Equal(8, segments[i].Count); 31 | } 32 | 33 | pool.Recycle(segments[3]); 34 | pool.Recycle(segments[1]); 35 | Assert.Equal(2, pool.InUse); 36 | Assert.Equal(2, pool.Available); 37 | Assert.True(pool.TryTake(out segments[1])); 38 | Assert.True(pool.TryTake(out segments[3])); 39 | Assert.False(pool.TryTake(out segments[4])); 40 | Assert.Equal(4, pool.InUse); 41 | Assert.Equal(0, pool.Available); 42 | 43 | Assert.Equal(24, segments[1].Offset); 44 | Assert.Equal(8, segments[3].Offset); 45 | for(int i = 0; i < 4; i++) 46 | { 47 | pool.Recycle(segments[i]); 48 | } 49 | Assert.Equal(0, pool.InUse); 50 | Assert.Equal(4, pool.Available); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Channels.Networking.Libuv/UvTcpClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading.Tasks; 4 | using Channels.Networking.Libuv.Interop; 5 | 6 | namespace Channels.Networking.Libuv 7 | { 8 | public class UvTcpClient 9 | { 10 | private static readonly Action _connectCallback = OnConnection; 11 | private static readonly Action _startConnect = state => ((UvTcpClient)state).DoConnect(); 12 | 13 | private readonly TaskCompletionSource _connectTcs = new TaskCompletionSource(); 14 | private readonly IPEndPoint _ipEndPoint; 15 | private readonly UvThread _thread; 16 | 17 | private UvTcpHandle _connectSocket; 18 | 19 | public UvTcpClient(UvThread thread, IPEndPoint endPoint) 20 | { 21 | _thread = thread; 22 | _ipEndPoint = endPoint; 23 | } 24 | 25 | public async Task ConnectAsync() 26 | { 27 | _thread.Post(_startConnect, this); 28 | 29 | var connection = await _connectTcs.Task; 30 | 31 | // Get back onto the current context 32 | await Task.Yield(); 33 | 34 | return connection; 35 | } 36 | 37 | private void DoConnect() 38 | { 39 | _connectSocket = new UvTcpHandle(); 40 | _connectSocket.Init(_thread.Loop, null); 41 | 42 | var connectReq = new UvConnectRequest(); 43 | connectReq.Init(_thread.Loop); 44 | connectReq.Connect(_connectSocket, _ipEndPoint, _connectCallback, this); 45 | } 46 | 47 | private static void OnConnection(UvConnectRequest req, int status, Exception exception, object state) 48 | { 49 | var client = (UvTcpClient)state; 50 | 51 | var connection = new UvTcpConnection(client._thread, client._connectSocket); 52 | 53 | client._connectTcs.TrySetResult(connection); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Channels.Text.Primitives/SplitEnumerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace Channels.Text.Primitives 6 | { 7 | /// 8 | /// Supports a simple iteration over a sequence of buffers from a Split operation. 9 | /// 10 | public struct SplitEnumerator : IEnumerator 11 | { 12 | private readonly byte _delimiter; 13 | private ReadableBuffer _current, _remainder; 14 | internal SplitEnumerator(ReadableBuffer remainder, byte delimiter) 15 | { 16 | _current = default(ReadableBuffer); 17 | _remainder = remainder; 18 | _delimiter = delimiter; 19 | } 20 | 21 | /// 22 | /// Gets the element in the collection at the current position of the enumerator. 23 | /// 24 | public ReadableBuffer Current => _current; 25 | 26 | object IEnumerator.Current => _current; 27 | 28 | /// 29 | /// Releases all resources owned by the instance 30 | /// 31 | public void Dispose() { } 32 | 33 | void IEnumerator.Reset() 34 | { 35 | throw new NotSupportedException(); 36 | } 37 | 38 | /// 39 | /// Advances the enumerator to the next element of the collection. 40 | /// 41 | public bool MoveNext() 42 | { 43 | ReadCursor cursor; 44 | if (_remainder.TrySliceTo(_delimiter, out _current, out cursor)) 45 | { 46 | _remainder = _remainder.Slice(cursor).Slice(1); 47 | return true; 48 | } 49 | // once we're out of splits, yield whatever is left 50 | if (_remainder.IsEmpty) 51 | { 52 | return false; 53 | } 54 | _current = _remainder; 55 | _remainder = default(ReadableBuffer); 56 | return true; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Channels.Networking.Libuv/Interop/UvHandle.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Diagnostics; 6 | using System.Threading; 7 | 8 | namespace Channels.Networking.Libuv.Interop 9 | { 10 | public abstract class UvHandle : UvMemory 11 | { 12 | private static readonly Uv.uv_close_cb _destroyMemory = (handle) => DestroyMemory(handle); 13 | private Action, IntPtr> _queueCloseHandle; 14 | 15 | protected UvHandle() : base () 16 | { 17 | } 18 | 19 | protected void CreateHandle( 20 | Uv uv, 21 | int threadId, 22 | int size, 23 | Action, IntPtr> queueCloseHandle) 24 | { 25 | _queueCloseHandle = queueCloseHandle; 26 | CreateMemory(uv, threadId, size); 27 | } 28 | 29 | protected override bool ReleaseHandle() 30 | { 31 | var memory = handle; 32 | if (memory != IntPtr.Zero) 33 | { 34 | handle = IntPtr.Zero; 35 | 36 | if (Thread.CurrentThread.ManagedThreadId == ThreadId) 37 | { 38 | _uv.close(memory, _destroyMemory); 39 | } 40 | else if (_queueCloseHandle != null) 41 | { 42 | // This can be called from the finalizer. 43 | // Ensure the closure doesn't reference "this". 44 | var uv = _uv; 45 | _queueCloseHandle(memory2 => uv.close(memory2, _destroyMemory), memory); 46 | } 47 | else 48 | { 49 | Debug.Assert(false, "UvHandle not initialized with queueCloseHandle action"); 50 | return false; 51 | } 52 | } 53 | return true; 54 | } 55 | 56 | public void Reference() 57 | { 58 | _uv.@ref(this); 59 | } 60 | 61 | public void Unreference() 62 | { 63 | _uv.unref(this); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /samples/Channels.Samples/HttpServer/FormReader.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Numerics; 3 | using Channels.Text.Primitives; 4 | using Microsoft.Extensions.Primitives; 5 | 6 | namespace Channels.Samples.Http 7 | { 8 | public class FormReader 9 | { 10 | private Dictionary _data = new Dictionary(); 11 | private long? _contentLength; 12 | 13 | public FormReader(long? contentLength) 14 | { 15 | _contentLength = contentLength; 16 | } 17 | 18 | public Dictionary FormValues => _data; 19 | 20 | public bool TryParse(ref ReadableBuffer buffer) 21 | { 22 | if (buffer.IsEmpty || !_contentLength.HasValue) 23 | { 24 | return true; 25 | } 26 | 27 | while (!buffer.IsEmpty && _contentLength > 0) 28 | { 29 | var next = buffer; 30 | ReadCursor delim; 31 | ReadableBuffer key; 32 | if (!next.TrySliceTo((byte)'=', out key, out delim)) 33 | { 34 | break; 35 | } 36 | 37 | next = next.Slice(delim).Slice(1); 38 | 39 | ReadableBuffer value; 40 | if (next.TrySliceTo((byte)'&', out value, out delim)) 41 | { 42 | next = next.Slice(delim).Slice(1); 43 | } 44 | else 45 | { 46 | 47 | var remaining = _contentLength - buffer.Length; 48 | 49 | if (remaining == 0) 50 | { 51 | value = next; 52 | next = next.Slice(next.End); 53 | } 54 | else 55 | { 56 | break; 57 | } 58 | } 59 | 60 | // TODO: Combine multi value keys 61 | _data[key.GetUtf8String()] = value.GetUtf8String(); 62 | _contentLength -= (buffer.Length - next.Length); 63 | buffer = next; 64 | } 65 | 66 | return _contentLength == 0; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Channels/ReadableBufferReader.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Buffers; 6 | using System.Runtime.CompilerServices; 7 | 8 | namespace Channels 9 | { 10 | public struct ReadableBufferReader 11 | { 12 | private Span _currentMemory; 13 | private int _index; 14 | private MemoryEnumerator _enumerator; 15 | private int _overallIndex; 16 | private bool _end; 17 | 18 | public ReadableBufferReader(ReadableBuffer buffer) 19 | { 20 | _end = false; 21 | _index = 0; 22 | _overallIndex = 0; 23 | _enumerator = buffer.GetEnumerator(); 24 | _currentMemory = default(Span); 25 | while (_enumerator.MoveNext()) 26 | { 27 | if (!_enumerator.Current.IsEmpty) 28 | { 29 | _currentMemory = _enumerator.Current.Span; 30 | return; 31 | } 32 | } 33 | _end = true; 34 | } 35 | 36 | public bool End => _end; 37 | 38 | public int Index => _overallIndex; 39 | 40 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 41 | public int Peek() 42 | { 43 | if (_end) 44 | { 45 | return -1; 46 | } 47 | return _currentMemory[_index]; 48 | } 49 | 50 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 51 | public int Take() 52 | { 53 | var value = Peek(); 54 | _index++; 55 | _overallIndex++; 56 | 57 | if (_index >= _currentMemory.Length) 58 | { 59 | MoveNext(); 60 | } 61 | 62 | return value; 63 | } 64 | 65 | private void MoveNext() 66 | { 67 | if (_enumerator.MoveNext()) 68 | { 69 | _currentMemory = _enumerator.Current.Span; 70 | _index = 0; 71 | } 72 | else 73 | { 74 | _end = true; 75 | } 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /src/Channels.Compression/Interop/Interop.zlib.Unix.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.IO.Compression; 6 | using System.Runtime.InteropServices; 7 | 8 | #if UNIX 9 | internal static partial class Interop 10 | { 11 | internal static partial class zlib 12 | { 13 | [DllImport(Libraries.CompressionNative, EntryPoint = "CompressionNative_DeflateInit2_")] 14 | internal static extern ZLibNative.ErrorCode DeflateInit2_( 15 | ref ZLibNative.ZStream stream, 16 | ZLibNative.CompressionLevel level, 17 | ZLibNative.CompressionMethod method, 18 | int windowBits, 19 | int memLevel, 20 | ZLibNative.CompressionStrategy strategy); 21 | 22 | [DllImport(Libraries.CompressionNative, EntryPoint = "CompressionNative_Deflate")] 23 | internal static extern ZLibNative.ErrorCode Deflate(ref ZLibNative.ZStream stream, ZLibNative.FlushCode flush); 24 | 25 | [DllImport(Libraries.CompressionNative, EntryPoint = "CompressionNative_DeflateEnd")] 26 | internal static extern ZLibNative.ErrorCode DeflateEnd(ref ZLibNative.ZStream stream); 27 | 28 | [DllImport(Libraries.CompressionNative, EntryPoint = "CompressionNative_InflateInit2_")] 29 | internal static extern ZLibNative.ErrorCode InflateInit2_(ref ZLibNative.ZStream stream, int windowBits); 30 | 31 | [DllImport(Libraries.CompressionNative, EntryPoint = "CompressionNative_Inflate")] 32 | internal static extern ZLibNative.ErrorCode Inflate(ref ZLibNative.ZStream stream, ZLibNative.FlushCode flush); 33 | 34 | [DllImport(Libraries.CompressionNative, EntryPoint = "CompressionNative_InflateEnd")] 35 | internal static extern ZLibNative.ErrorCode InflateEnd(ref ZLibNative.ZStream stream); 36 | 37 | internal static unsafe uint crc32(uint crc, byte[] buffer, int offset, int len) 38 | { 39 | fixed (byte* buf = &buffer[offset]) 40 | return Crc32(crc, buf, len); 41 | } 42 | 43 | [DllImport(Libraries.CompressionNative, EntryPoint = "CompressionNative_Crc32")] 44 | private static unsafe extern uint Crc32(uint crc, byte* buffer, int len); 45 | } 46 | } 47 | #endif -------------------------------------------------------------------------------- /src/Channels.Networking.Libuv/Interop/UvTcpHandle.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Net; 6 | using System.Runtime.InteropServices; 7 | 8 | namespace Channels.Networking.Libuv.Interop 9 | { 10 | public class UvTcpHandle : UvStreamHandle 11 | { 12 | public UvTcpHandle() : base() 13 | { 14 | } 15 | 16 | public void Init(UvLoopHandle loop, Action, IntPtr> queueCloseHandle) 17 | { 18 | CreateHandle( 19 | loop.Libuv, 20 | loop.ThreadId, 21 | loop.Libuv.handle_size(Uv.HandleType.TCP), queueCloseHandle); 22 | 23 | _uv.tcp_init(loop, this); 24 | } 25 | 26 | public void Bind(IPEndPoint endpoint) 27 | { 28 | SockAddr addr; 29 | var addressText = endpoint.Address.ToString(); 30 | 31 | Exception error1; 32 | _uv.ip4_addr(addressText, endpoint.Port, out addr, out error1); 33 | 34 | if (error1 != null) 35 | { 36 | Exception error2; 37 | _uv.ip6_addr(addressText, endpoint.Port, out addr, out error2); 38 | if (error2 != null) 39 | { 40 | throw error1; 41 | } 42 | } 43 | 44 | _uv.tcp_bind(this, ref addr, 0); 45 | } 46 | 47 | public IPEndPoint GetPeerIPEndPoint() 48 | { 49 | SockAddr socketAddress; 50 | int namelen = Marshal.SizeOf(); 51 | _uv.tcp_getpeername(this, out socketAddress, ref namelen); 52 | 53 | return socketAddress.GetIPEndPoint(); 54 | } 55 | 56 | public IPEndPoint GetSockIPEndPoint() 57 | { 58 | SockAddr socketAddress; 59 | int namelen = Marshal.SizeOf(); 60 | _uv.tcp_getsockname(this, out socketAddress, ref namelen); 61 | 62 | return socketAddress.GetIPEndPoint(); 63 | } 64 | 65 | public void Open(IntPtr hSocket) 66 | { 67 | _uv.tcp_open(this, hSocket); 68 | } 69 | 70 | public void NoDelay(bool enable) 71 | { 72 | _uv.tcp_nodelay(this, enable); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /samples/Channels.Samples/RawLibuvHttpClientSample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Threading.Tasks; 6 | using Channels.Networking.Libuv; 7 | using Channels.Text.Primitives; 8 | 9 | namespace Channels.Samples 10 | { 11 | public class RawLibuvHttpClientSample 12 | { 13 | public static async Task Run() 14 | { 15 | var thread = new UvThread(); 16 | var client = new UvTcpClient(thread, new IPEndPoint(IPAddress.Loopback, 5000)); 17 | 18 | var consoleOutput = thread.ChannelFactory.MakeWriteableChannel(Console.OpenStandardOutput()); 19 | 20 | var connection = await client.ConnectAsync(); 21 | 22 | while (true) 23 | { 24 | var buffer = connection.Output.Alloc(); 25 | 26 | buffer.WriteAsciiString("GET / HTTP/1.1"); 27 | buffer.WriteAsciiString("\r\n\r\n"); 28 | 29 | await buffer.FlushAsync(); 30 | 31 | // Write the client output to the console 32 | await CopyCompletedAsync(connection.Input, consoleOutput); 33 | 34 | await Task.Delay(1000); 35 | } 36 | } 37 | private static async Task CopyCompletedAsync(IReadableChannel input, IWritableChannel channel) 38 | { 39 | var result = await input.ReadAsync(); 40 | var inputBuffer = result.Buffer; 41 | 42 | while (true) 43 | { 44 | try 45 | { 46 | if (inputBuffer.IsEmpty && result.IsCompleted) 47 | { 48 | return; 49 | } 50 | 51 | var buffer = channel.Alloc(); 52 | 53 | buffer.Append(inputBuffer); 54 | 55 | await buffer.FlushAsync(); 56 | } 57 | finally 58 | { 59 | input.Advance(inputBuffer.End); 60 | } 61 | 62 | var awaiter = input.ReadAsync(); 63 | 64 | if (!awaiter.IsCompleted) 65 | { 66 | // No more data 67 | break; 68 | } 69 | 70 | result = await input.ReadAsync(); 71 | inputBuffer = result.Buffer; 72 | } 73 | } 74 | 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Channels/MemoryEnumerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Channels 4 | { 5 | /// 6 | /// An enumerator over the 7 | /// 8 | public struct MemoryEnumerator 9 | { 10 | private BufferSegment _segment; 11 | private Memory _current; 12 | private int _startIndex; 13 | private readonly int _endIndex; 14 | private readonly BufferSegment _endSegment; 15 | 16 | /// 17 | /// 18 | /// 19 | public MemoryEnumerator(ReadCursor start, ReadCursor end) 20 | { 21 | _startIndex = start.Index; 22 | _segment = start.Segment; 23 | _endSegment = end.Segment; 24 | _endIndex = end.Index; 25 | _current = Memory.Empty; 26 | } 27 | 28 | /// 29 | /// The current 30 | /// 31 | public Memory Current => _current; 32 | 33 | /// 34 | /// 35 | /// 36 | public void Dispose() 37 | { 38 | 39 | } 40 | 41 | /// 42 | /// Moves to the next in the 43 | /// 44 | /// 45 | public bool MoveNext() 46 | { 47 | if (_segment == null) 48 | { 49 | return false; 50 | } 51 | 52 | int start = _segment.Start; 53 | int end = _segment.End; 54 | 55 | if (_startIndex != 0) 56 | { 57 | start = _startIndex; 58 | _startIndex = 0; 59 | } 60 | 61 | if (_segment == _endSegment) 62 | { 63 | end = _endIndex; 64 | } 65 | 66 | _current = _segment.Memory.Slice(start, end - start); 67 | 68 | if (_segment == _endSegment) 69 | { 70 | _segment = null; 71 | } 72 | else 73 | { 74 | _segment = _segment.Next; 75 | } 76 | 77 | return true; 78 | } 79 | 80 | /// 81 | /// 82 | /// 83 | public void Reset() 84 | { 85 | ThrowHelper.ThrowNotSupportedException(); 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /samples/Channels.Samples/HttpClient/ChannelHttpContent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Net.Http; 7 | using System.Threading.Tasks; 8 | 9 | namespace Channels.Samples 10 | { 11 | public class ChannelHttpContent : HttpContent 12 | { 13 | private readonly IReadableChannel _output; 14 | 15 | public ChannelHttpContent(IReadableChannel output) 16 | { 17 | _output = output; 18 | } 19 | 20 | public int ContentLength { get; set; } 21 | 22 | protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context) 23 | { 24 | int remaining = ContentLength; 25 | 26 | while (remaining > 0) 27 | { 28 | var result = await _output.ReadAsync(); 29 | var inputBuffer = result.Buffer; 30 | 31 | var fin = result.IsCompleted; 32 | 33 | var consumed = inputBuffer.Start; 34 | 35 | try 36 | { 37 | if (inputBuffer.IsEmpty && fin) 38 | { 39 | return; 40 | } 41 | 42 | var data = inputBuffer.Slice(0, remaining); 43 | 44 | foreach (var memory in data) 45 | { 46 | ArraySegment buffer; 47 | 48 | unsafe 49 | { 50 | if (!memory.TryGetArray(out buffer)) 51 | { 52 | // Fall back to copies if this was native memory and we were unable to get 53 | // something we could write 54 | buffer = new ArraySegment(memory.Span.ToArray()); 55 | } 56 | } 57 | 58 | await stream.WriteAsync(buffer.Array, buffer.Offset, buffer.Count); 59 | } 60 | 61 | consumed = data.End; 62 | remaining -= data.Length; 63 | } 64 | finally 65 | { 66 | _output.Advance(consumed); 67 | } 68 | } 69 | } 70 | 71 | protected override bool TryComputeLength(out long length) 72 | { 73 | length = ContentLength; 74 | return true; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Channels.Networking.Libuv/Interop/UvAsyncHandle.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Diagnostics; 6 | using System.Threading; 7 | 8 | namespace Channels.Networking.Libuv.Interop 9 | { 10 | public class UvAsyncHandle : UvHandle 11 | { 12 | private static readonly Uv.uv_close_cb _destroyMemory = (handle) => DestroyMemory(handle); 13 | 14 | private static readonly Uv.uv_async_cb _uv_async_cb = (handle) => AsyncCb(handle); 15 | private Action _callback; 16 | private Action, IntPtr> _queueCloseHandle; 17 | 18 | public UvAsyncHandle() : base() 19 | { 20 | } 21 | 22 | public void Init(UvLoopHandle loop, Action callback, Action, IntPtr> queueCloseHandle) 23 | { 24 | CreateMemory( 25 | loop.Libuv, 26 | loop.ThreadId, 27 | loop.Libuv.handle_size(Uv.HandleType.ASYNC)); 28 | 29 | _callback = callback; 30 | _queueCloseHandle = queueCloseHandle; 31 | _uv.async_init(loop, this, _uv_async_cb); 32 | } 33 | 34 | public void Send() 35 | { 36 | _uv.async_send(this); 37 | } 38 | 39 | private static void AsyncCb(IntPtr handle) 40 | { 41 | FromIntPtr(handle)._callback.Invoke(); 42 | } 43 | 44 | protected override bool ReleaseHandle() 45 | { 46 | var memory = handle; 47 | if (memory != IntPtr.Zero) 48 | { 49 | handle = IntPtr.Zero; 50 | 51 | if (Thread.CurrentThread.ManagedThreadId == ThreadId) 52 | { 53 | _uv.close(memory, _destroyMemory); 54 | } 55 | else if (_queueCloseHandle != null) 56 | { 57 | // This can be called from the finalizer. 58 | // Ensure the closure doesn't reference "this". 59 | var uv = _uv; 60 | _queueCloseHandle(memory2 => uv.close(memory2, _destroyMemory), memory); 61 | uv.unsafe_async_send(memory); 62 | } 63 | else 64 | { 65 | Debug.Assert(false, "UvAsyncHandle not initialized with queueCloseHandle action"); 66 | return false; 67 | } 68 | } 69 | return true; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Channels/ReadableChannel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace Channels 5 | { 6 | /// 7 | /// Represents a channel from which data can be read. 8 | /// 9 | public abstract class ReadableChannel : IReadableChannel 10 | { 11 | /// 12 | /// The underlying the ReadableChannel communicates over. 13 | /// 14 | protected readonly Channel _channel; 15 | 16 | /// 17 | /// Creates a base . 18 | /// 19 | /// The that buffers will be allocated from. 20 | protected ReadableChannel(IBufferPool pool) 21 | { 22 | _channel = new Channel(pool); 23 | } 24 | 25 | /// 26 | /// Creates a base . 27 | /// 28 | /// The the ReadableChannel communicates over. 29 | protected ReadableChannel(Channel channel) 30 | { 31 | _channel = channel; 32 | } 33 | 34 | /// 35 | /// Moves forward the channels read cursor to after the consumed data. 36 | /// 37 | /// Marks the extent of the data that has been succesfully proceesed. 38 | /// Marks the extent of the data that has been read and examined. 39 | /// 40 | /// The memory for the consumed data will be released and no longer available. 41 | /// The examined data communicates to the channel when it should signal more data is available. 42 | /// 43 | public void Advance(ReadCursor consumed, ReadCursor examined) => _channel.AdvanceReader(consumed, examined); 44 | 45 | /// 46 | /// Signal to the producer that the consumer is done reading. 47 | /// 48 | /// Optional Exception indicating a failure that's causing the channel to complete. 49 | public void Complete(Exception exception = null) => _channel.CompleteReader(exception); 50 | 51 | /// 52 | /// Asynchronously reads a sequence of bytes from the current . 53 | /// 54 | /// A representing the asynchronous read operation. 55 | public ReadableChannelAwaitable ReadAsync() => _channel.ReadAsync(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Channels.Compression/ZLibNative.Windows.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System; 6 | using System.Runtime.InteropServices; 7 | 8 | namespace Channels.Compression 9 | { 10 | internal static partial class ZLibNative 11 | { 12 | /// 13 | /// ZLib stream descriptor data structure 14 | /// Do not construct instances of ZStream explicitly. 15 | /// Always use ZLibNative.DeflateInit2_ or ZLibNative.InflateInit2_ instead. 16 | /// Those methods will wrap this structure into a SafeHandle and thus make sure that it is always disposed correctly. 17 | /// 18 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 19 | internal struct ZStream 20 | { 21 | internal void Init() 22 | { 23 | zalloc = ZNullPtr; 24 | zfree = ZNullPtr; 25 | opaque = ZNullPtr; 26 | } 27 | 28 | internal IntPtr nextIn; //Bytef *next_in; /* next input byte */ 29 | internal uint availIn; //uInt avail_in; /* number of bytes available at next_in */ 30 | internal uint totalIn; //uLong total_in; /* total nb of input bytes read so far */ 31 | 32 | internal IntPtr nextOut; //Bytef *next_out; /* next output byte should be put there */ 33 | internal uint availOut; //uInt avail_out; /* remaining free space at next_out */ 34 | internal uint totalOut; //uLong total_out; /* total nb of bytes output so far */ 35 | 36 | internal IntPtr msg; //char *msg; /* last error message, NULL if no error */ 37 | 38 | internal IntPtr state; //struct internal_state FAR *state; /* not visible by applications */ 39 | 40 | internal IntPtr zalloc; //alloc_func zalloc; /* used to allocate the internal state */ 41 | internal IntPtr zfree; //free_func zfree; /* used to free the internal state */ 42 | internal IntPtr opaque; //voidpf opaque; /* private data object passed to zalloc and zfree */ 43 | 44 | internal int dataType; //int data_type; /* best guess about the data type: binary or text */ 45 | internal uint adler; //uLong adler; /* adler32 value of the uncompressed data */ 46 | internal uint reserved; //uLong reserved; /* reserved for future use */ 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Channels.Networking.Libuv/Interop/UvMemory.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | using System; 4 | using System.Diagnostics; 5 | using System.Runtime.InteropServices; 6 | using System.Threading; 7 | 8 | namespace Channels.Networking.Libuv.Interop 9 | { 10 | /// 11 | /// Summary description for UvMemory 12 | /// 13 | public abstract class UvMemory : SafeHandle 14 | { 15 | protected Uv _uv; 16 | protected int _threadId; 17 | 18 | protected UvMemory() : base(IntPtr.Zero, true) 19 | { 20 | } 21 | 22 | public Uv Libuv => _uv; 23 | 24 | public override bool IsInvalid 25 | { 26 | get 27 | { 28 | return handle == IntPtr.Zero; 29 | } 30 | } 31 | 32 | public int ThreadId 33 | { 34 | get 35 | { 36 | return _threadId; 37 | } 38 | private set 39 | { 40 | _threadId = value; 41 | } 42 | } 43 | 44 | unsafe protected void CreateMemory(Uv uv, int threadId, int size) 45 | { 46 | _uv = uv; 47 | ThreadId = threadId; 48 | 49 | handle = Marshal.AllocCoTaskMem(size); 50 | *(IntPtr*)handle = GCHandle.ToIntPtr(GCHandle.Alloc(this, GCHandleType.Weak)); 51 | } 52 | 53 | unsafe protected static void DestroyMemory(IntPtr memory) 54 | { 55 | var gcHandlePtr = *(IntPtr*)memory; 56 | DestroyMemory(memory, gcHandlePtr); 57 | } 58 | 59 | protected static void DestroyMemory(IntPtr memory, IntPtr gcHandlePtr) 60 | { 61 | if (gcHandlePtr != IntPtr.Zero) 62 | { 63 | var gcHandle = GCHandle.FromIntPtr(gcHandlePtr); 64 | gcHandle.Free(); 65 | } 66 | Marshal.FreeCoTaskMem(memory); 67 | } 68 | 69 | internal IntPtr InternalGetHandle() 70 | { 71 | return handle; 72 | } 73 | 74 | public void Validate(bool closed = false) 75 | { 76 | Debug.Assert(closed || !IsClosed, "Handle is closed"); 77 | Debug.Assert(!IsInvalid, "Handle is invalid"); 78 | Debug.Assert(_threadId == Thread.CurrentThread.ManagedThreadId, "ThreadId is incorrect"); 79 | } 80 | 81 | unsafe public static THandle FromIntPtr(IntPtr handle) 82 | { 83 | GCHandle gcHandle = GCHandle.FromIntPtr(*(IntPtr*)handle); 84 | return (THandle)gcHandle.Target; 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /src/Channels/DefaultWritableBufferExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Binary; 3 | using System.Runtime.CompilerServices; 4 | 5 | namespace Channels 6 | { 7 | /// 8 | /// Common extension methods against writable buffers 9 | /// 10 | public static class DefaultWritableBufferExtensions 11 | { 12 | /// 13 | /// Writes the source to the . 14 | /// 15 | /// The 16 | /// The to write 17 | public static void Write(this WritableBuffer buffer, Span source) 18 | { 19 | if (buffer.Memory.IsEmpty) 20 | { 21 | buffer.Ensure(); 22 | } 23 | 24 | // Fast path, try copying to the available memory directly 25 | if (source.Length <= buffer.Memory.Length) 26 | { 27 | source.CopyTo(buffer.Memory.Span); 28 | buffer.Advance(source.Length); 29 | return; 30 | } 31 | 32 | var remaining = source.Length; 33 | var offset = 0; 34 | 35 | while (remaining > 0) 36 | { 37 | var writable = Math.Min(remaining, buffer.Memory.Length); 38 | 39 | buffer.Ensure(writable); 40 | 41 | if (writable == 0) 42 | { 43 | continue; 44 | } 45 | 46 | source.Slice(offset, writable).CopyTo(buffer.Memory.Span); 47 | 48 | remaining -= writable; 49 | offset += writable; 50 | 51 | buffer.Advance(writable); 52 | } 53 | } 54 | 55 | /// 56 | /// Reads a structure of type T out of a buffer of bytes. 57 | /// 58 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 59 | public static void WriteBigEndian<[Primitive]T>(this WritableBuffer buffer, T value) where T : struct 60 | { 61 | int len = Unsafe.SizeOf(); 62 | buffer.Ensure(len); 63 | buffer.Memory.Span.WriteBigEndian(value); 64 | buffer.Advance(len); 65 | } 66 | 67 | /// 68 | /// Reads a structure of type T out of a buffer of bytes. 69 | /// 70 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 71 | public static void WriteLittleEndian<[Primitive]T>(this WritableBuffer buffer, T value) where T : struct 72 | { 73 | int len = Unsafe.SizeOf(); 74 | buffer.Ensure(len); 75 | buffer.Memory.Span.WriteLittleEndian(value); 76 | buffer.Advance(len); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Channels.Networking.Libuv/Interop/UvConnectRequest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Net; 6 | 7 | namespace Channels.Networking.Libuv.Interop 8 | { 9 | /// 10 | /// Summary description for UvWriteRequest 11 | /// 12 | public class UvConnectRequest : UvRequest 13 | { 14 | private readonly static Uv.uv_connect_cb _uv_connect_cb = (req, status) => UvConnectCb(req, status); 15 | 16 | private Action _callback; 17 | private object _state; 18 | 19 | public UvConnectRequest() : base() 20 | { 21 | } 22 | 23 | public void Init(UvLoopHandle loop) 24 | { 25 | var requestSize = loop.Libuv.req_size(Uv.RequestType.CONNECT); 26 | CreateMemory( 27 | loop.Libuv, 28 | loop.ThreadId, 29 | requestSize); 30 | } 31 | 32 | public void Connect( 33 | UvTcpHandle socket, 34 | IPEndPoint endpoint, 35 | Action callback, 36 | object state) 37 | { 38 | _callback = callback; 39 | _state = state; 40 | 41 | SockAddr addr; 42 | var addressText = endpoint.Address.ToString(); 43 | 44 | Exception error1; 45 | _uv.ip4_addr(addressText, endpoint.Port, out addr, out error1); 46 | 47 | if (error1 != null) 48 | { 49 | Exception error2; 50 | _uv.ip6_addr(addressText, endpoint.Port, out addr, out error2); 51 | if (error2 != null) 52 | { 53 | throw error1; 54 | } 55 | } 56 | 57 | Pin(); 58 | Libuv.tcp_connect(this, socket, ref addr, _uv_connect_cb); 59 | } 60 | 61 | public void Connect( 62 | UvPipeHandle pipe, 63 | string name, 64 | Action callback, 65 | object state) 66 | { 67 | _callback = callback; 68 | _state = state; 69 | 70 | Pin(); 71 | Libuv.pipe_connect(this, pipe, name, _uv_connect_cb); 72 | } 73 | 74 | private static void UvConnectCb(IntPtr ptr, int status) 75 | { 76 | var req = FromIntPtr(ptr); 77 | req.Unpin(); 78 | 79 | var callback = req._callback; 80 | req._callback = null; 81 | 82 | var state = req._state; 83 | req._state = null; 84 | 85 | Exception error = null; 86 | if (status < 0) 87 | { 88 | req.Libuv.Check(status, out error); 89 | } 90 | 91 | callback(req, status, error, state); 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /src/Channels/ChannelFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace Channels 8 | { 9 | /// 10 | /// Factory used to creaet instances of various channels. 11 | /// 12 | public class ChannelFactory : IDisposable 13 | { 14 | private readonly IBufferPool _pool; 15 | 16 | public ChannelFactory() : this(new MemoryPool()) 17 | { 18 | } 19 | 20 | public ChannelFactory(IBufferPool pool) 21 | { 22 | _pool = pool; 23 | } 24 | 25 | public Channel CreateChannel() => new Channel(_pool); 26 | 27 | public IReadableChannel MakeReadableChannel(Stream stream) 28 | { 29 | if (!stream.CanRead) 30 | { 31 | ThrowHelper.ThrowNotSupportedException(); 32 | } 33 | 34 | var channel = new Channel(_pool); 35 | ExecuteCopyToAsync(channel, stream); 36 | return channel; 37 | } 38 | 39 | private async void ExecuteCopyToAsync(Channel channel, Stream stream) 40 | { 41 | await channel.ReadingStarted; 42 | 43 | await stream.CopyToAsync(channel); 44 | } 45 | 46 | public IChannel MakeChannel(Stream stream) 47 | { 48 | return new StreamChannel(this, stream); 49 | } 50 | 51 | public IWritableChannel MakeWriteableChannel(Stream stream) 52 | { 53 | if (!stream.CanWrite) 54 | { 55 | ThrowHelper.ThrowNotSupportedException(); 56 | } 57 | 58 | var channel = new Channel(_pool); 59 | 60 | channel.CopyToAsync(stream).ContinueWith((task) => 61 | { 62 | if (task.IsFaulted) 63 | { 64 | channel.CompleteReader(task.Exception); 65 | } 66 | else 67 | { 68 | channel.CompleteReader(); 69 | } 70 | }); 71 | 72 | return channel; 73 | } 74 | 75 | public IWritableChannel MakeWriteableChannel(IWritableChannel channel, Func consume) 76 | { 77 | var newChannel = new Channel(_pool); 78 | 79 | consume(newChannel, channel).ContinueWith(t => 80 | { 81 | }); 82 | 83 | return newChannel; 84 | } 85 | 86 | public IReadableChannel MakeReadableChannel(IReadableChannel channel, Func produce) 87 | { 88 | var newChannel = new Channel(_pool); 89 | Execute(channel, newChannel, produce); 90 | return newChannel; 91 | } 92 | 93 | private async void Execute(IReadableChannel channel, Channel newChannel, Func produce) 94 | { 95 | await newChannel.ReadingStarted; 96 | 97 | await produce(channel, newChannel); 98 | } 99 | 100 | public void Dispose() => _pool.Dispose(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/Channels.Networking.Sockets/Internal/Signal.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using System.Threading; 4 | 5 | namespace Channels.Networking.Sockets.Internal 6 | { 7 | /// 8 | /// Very lightweight awaitable gate - intended for use in high-volume single-producer/single-consumer 9 | /// scenario, in particular targeting the bridge between async IO operations 10 | /// and the async method that is pumping the read/write queue. A key consideration is that 11 | /// no objects (in particular Task/TaskCompletionSource) are allocated even in the await case. Instead, 12 | /// a custom awaiter is provided. Works like a - the method must 13 | /// be called between operations. 14 | /// 15 | internal class Signal : INotifyCompletion 16 | { 17 | private readonly ContinuationMode _continuationMode; 18 | 19 | private Action _continuation; 20 | private static readonly Action _completedSentinel = delegate { }; 21 | 22 | public Signal(ContinuationMode continuationMode = ContinuationMode.Synchronous) 23 | { 24 | _continuationMode = continuationMode; 25 | } 26 | 27 | public bool IsCompleted => ReferenceEquals(_completedSentinel, Volatile.Read(ref _continuation)); 28 | 29 | private object SyncLock => this; 30 | 31 | public Signal GetAwaiter() => this; 32 | 33 | public void GetResult() { } 34 | 35 | public void OnCompleted(Action continuation) 36 | { 37 | if (continuation != null) 38 | { 39 | var oldValue = Interlocked.CompareExchange(ref _continuation, continuation, null); 40 | 41 | if (ReferenceEquals(oldValue, _completedSentinel)) 42 | { 43 | // already complete; calback sync 44 | continuation.Invoke(); 45 | } 46 | else if (oldValue != null) 47 | { 48 | ThrowMultipleCallbacksNotSupported(); 49 | } 50 | } 51 | } 52 | private static void ThrowMultipleCallbacksNotSupported() 53 | { 54 | throw new NotSupportedException("Multiple callbacks via Signal.OnCompleted are not supported"); 55 | } 56 | 57 | 58 | public void Reset() 59 | { 60 | Volatile.Write(ref _continuation, null); 61 | } 62 | 63 | public void Set() 64 | { 65 | Action continuation = Interlocked.Exchange(ref _continuation, _completedSentinel); 66 | 67 | if (continuation != null && !ReferenceEquals(continuation, _completedSentinel)) 68 | { 69 | switch (_continuationMode) 70 | { 71 | case ContinuationMode.Synchronous: 72 | continuation.Invoke(); 73 | break; 74 | case ContinuationMode.ThreadPool: 75 | ThreadPool.QueueUserWorkItem(state => ((Action)state).Invoke(), continuation); 76 | break; 77 | } 78 | } 79 | } 80 | 81 | // utility method for people who don't feel comfortable with `await obj;` and prefer `await obj.WaitAsync();` 82 | internal Signal WaitAsync() => this; 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/Channels.Networking.Sockets/Internal/MicroBufferPool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Channels.Networking.Sockets.Internal 5 | { 6 | /// 7 | /// Simple pool over a byte[] that returns segments, using a queue to 8 | /// handle recycling of segments. 9 | /// 10 | internal class MicroBufferPool 11 | { 12 | private readonly byte[] _buffer; 13 | private readonly Queue _recycled; 14 | private ushort _next; 15 | private readonly ushort _count; 16 | private readonly int _bytesPerItem; 17 | 18 | public int BytesPerItem => _bytesPerItem; 19 | 20 | public int Available 21 | { 22 | get 23 | { 24 | lock (_recycled) 25 | { 26 | return (_count - _next) + _recycled.Count; 27 | } 28 | } 29 | } 30 | 31 | public int InUse 32 | { 33 | get 34 | { 35 | lock (_recycled) 36 | { 37 | return _next - _recycled.Count; 38 | } 39 | } 40 | } 41 | 42 | public MicroBufferPool(int bytesPerItem, int count) 43 | { 44 | if (count <= 0 || count > ushort.MaxValue) 45 | { 46 | throw new ArgumentOutOfRangeException(nameof(count)); 47 | } 48 | if (bytesPerItem <= 0) 49 | { 50 | throw new ArgumentOutOfRangeException(nameof(bytesPerItem)); 51 | } 52 | _buffer = new byte[(ulong)bytesPerItem * (ulong)count]; 53 | _next = 0; 54 | _count = (ushort)count; 55 | _bytesPerItem = bytesPerItem; 56 | _recycled = new Queue(); 57 | } 58 | 59 | public bool TryTake(out ArraySegment segment) 60 | { 61 | int index; 62 | lock (_recycled) 63 | { 64 | if (_recycled.Count != 0) 65 | { 66 | index = _recycled.Dequeue(); 67 | } 68 | else if (_next < _count) 69 | { 70 | index = _next++; 71 | } 72 | else 73 | { 74 | segment = default(ArraySegment); 75 | return false; 76 | } 77 | } 78 | 79 | segment = new ArraySegment(_buffer, index * _bytesPerItem, _bytesPerItem); 80 | return true; 81 | } 82 | 83 | public void Recycle(ArraySegment segment) 84 | { 85 | // only put it back if it is a buffer we might have issued - 86 | // needs same array and count, aligned by count, and not out-of-range 87 | int index; 88 | if (segment.Array == _buffer && segment.Count == _bytesPerItem 89 | && (segment.Offset % _bytesPerItem) == 0 90 | && (index = segment.Offset / _bytesPerItem) >= 0 91 | && index < _count) 92 | { 93 | lock (_recycled) 94 | { 95 | _recycled.Enqueue((ushort)index); 96 | } 97 | } 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/Channels.Text.Primitives/AsciiUtilities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace Channels.Text.Primitives 7 | { 8 | internal class AsciiUtilities 9 | { 10 | public static unsafe bool TryGetAsciiString(byte* input, char* output, int count) 11 | { 12 | var i = 0; 13 | sbyte* signedInput = (sbyte*)input; 14 | 15 | bool isValid = true; 16 | while (i < count - 11) 17 | { 18 | isValid = isValid && *signedInput > 0 && *(signedInput + 1) > 0 && *(signedInput + 2) > 0 && 19 | *(signedInput + 3) > 0 && *(signedInput + 4) > 0 && *(signedInput + 5) > 0 && *(signedInput + 6) > 0 && 20 | *(signedInput + 7) > 0 && *(signedInput + 8) > 0 && *(signedInput + 9) > 0 && *(signedInput + 10) > 0 && 21 | *(signedInput + 11) > 0; 22 | 23 | i += 12; 24 | *(output) = (char)*(signedInput); 25 | *(output + 1) = (char)*(signedInput + 1); 26 | *(output + 2) = (char)*(signedInput + 2); 27 | *(output + 3) = (char)*(signedInput + 3); 28 | *(output + 4) = (char)*(signedInput + 4); 29 | *(output + 5) = (char)*(signedInput + 5); 30 | *(output + 6) = (char)*(signedInput + 6); 31 | *(output + 7) = (char)*(signedInput + 7); 32 | *(output + 8) = (char)*(signedInput + 8); 33 | *(output + 9) = (char)*(signedInput + 9); 34 | *(output + 10) = (char)*(signedInput + 10); 35 | *(output + 11) = (char)*(signedInput + 11); 36 | output += 12; 37 | signedInput += 12; 38 | } 39 | if (i < count - 5) 40 | { 41 | isValid = isValid && *signedInput > 0 && *(signedInput + 1) > 0 && *(signedInput + 2) > 0 && 42 | *(signedInput + 3) > 0 && *(signedInput + 4) > 0 && *(signedInput + 5) > 0; 43 | 44 | i += 6; 45 | *(output) = (char)*(signedInput); 46 | *(output + 1) = (char)*(signedInput + 1); 47 | *(output + 2) = (char)*(signedInput + 2); 48 | *(output + 3) = (char)*(signedInput + 3); 49 | *(output + 4) = (char)*(signedInput + 4); 50 | *(output + 5) = (char)*(signedInput + 5); 51 | output += 6; 52 | signedInput += 6; 53 | } 54 | if (i < count - 3) 55 | { 56 | isValid = isValid && *signedInput > 0 && *(signedInput + 1) > 0 && *(signedInput + 2) > 0 && 57 | *(signedInput + 3) > 0; 58 | 59 | i += 4; 60 | *(output) = (char)*(signedInput); 61 | *(output + 1) = (char)*(signedInput + 1); 62 | *(output + 2) = (char)*(signedInput + 2); 63 | *(output + 3) = (char)*(signedInput + 3); 64 | output += 4; 65 | signedInput += 4; 66 | } 67 | 68 | while (i < count) 69 | { 70 | isValid = isValid && *signedInput > 0; 71 | 72 | i++; 73 | *output = (char)*signedInput; 74 | output++; 75 | signedInput++; 76 | } 77 | 78 | return isValid; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Channels/Gate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace Channels 7 | { 8 | /// 9 | /// Simple awaitable gate - intended to synchronize a single producer with a single consumer to ensure the producer doesn't 10 | /// produce until the consumer is ready. Similar to a but reusable so we don't have 11 | /// to keep allocating new ones every time. 12 | /// 13 | /// 14 | /// The gate can be in one of two states: "Open", indicating that an await will immediately return and "Closed", meaning that an await 15 | /// will block until the gate is opened. The gate is initially "Closed" and can be opened by a call to . Upon the completion 16 | /// of an await, it will automatically return to the "Closed" state (this is done in the call that is injected by the 17 | /// compiler's async/await logic). 18 | /// 19 | internal class Gate : ICriticalNotifyCompletion 20 | { 21 | private static readonly Action _gateIsOpen = () => {}; 22 | 23 | private volatile Action _gateState; 24 | 25 | /// 26 | /// Returns a boolean indicating if the gate is "open" 27 | /// 28 | public bool IsCompleted => _gateState == _gateIsOpen; 29 | 30 | public void UnsafeOnCompleted(Action continuation) => OnCompleted(continuation); 31 | 32 | public void OnCompleted(Action continuation) 33 | { 34 | // If we're already completed, call the continuation immediately 35 | if (_gateState == _gateIsOpen) 36 | { 37 | continuation(); 38 | } 39 | else 40 | { 41 | // Otherwise, if the current continuation is null, atomically store the new continuation in the field and return the old value 42 | var previous = Interlocked.CompareExchange(ref _gateState, continuation, null); 43 | if (previous == _gateIsOpen) 44 | { 45 | // It got completed in the time between the previous the method and the cmpexch. 46 | // So call the continuation (the value of _continuation will remain _completed because cmpexch is atomic, 47 | // so we didn't accidentally replace it). 48 | continuation(); 49 | } 50 | } 51 | } 52 | 53 | /// 54 | /// Resets the gate to continue blocking the waiter. This is called immediately after awaiting the signal. 55 | /// 56 | public void GetResult() 57 | { 58 | // Clear the active continuation to "reset" the state of this event 59 | Interlocked.Exchange(ref _gateState, null); 60 | } 61 | 62 | /// 63 | /// Set the gate to allow the waiter to continue. 64 | /// 65 | public void Open() 66 | { 67 | // Set the stored continuation value to a sentinel that indicates the state is completed, then call the previous value. 68 | var completion = Interlocked.Exchange(ref _gateState, _gateIsOpen); 69 | if (completion != _gateIsOpen) 70 | { 71 | completion?.Invoke(); 72 | } 73 | } 74 | 75 | public Gate GetAwaiter() => this; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Channels.Networking.Windows.RIO/Internal/Winsock/RioDelegates.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | using System.Runtime.InteropServices; 6 | using System.Security; 7 | 8 | namespace Channels.Networking.Windows.RIO.Internal.Winsock 9 | { 10 | [SuppressUnmanagedCodeSecurity] 11 | [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true)] 12 | public delegate IntPtr RioRegisterBuffer([In] IntPtr dataBuffer, [In] UInt32 dataLength); 13 | 14 | [SuppressUnmanagedCodeSecurity] 15 | [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true)] 16 | public delegate void RioDeregisterBuffer([In] IntPtr bufferId); 17 | 18 | [SuppressUnmanagedCodeSecurity] 19 | [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true)] 20 | public delegate bool RioSend([In] IntPtr socketQueue, [In] ref RioBufferSegment rioBuffer, [In] UInt32 dataBufferCount, [In] RioSendFlags flags, [In] long requestCorrelation); 21 | 22 | [SuppressUnmanagedCodeSecurity] 23 | [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true)] 24 | public delegate bool RioReceive([In] IntPtr socketQueue, [In] ref RioBufferSegment rioBuffer, [In] UInt32 dataBufferCount, [In] RioReceiveFlags flags, [In] long requestCorrelation); 25 | 26 | [SuppressUnmanagedCodeSecurity] 27 | [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true)] 28 | public delegate IntPtr RioCreateCompletionQueue([In] uint queueSize, [In] NotificationCompletion notificationCompletion); 29 | 30 | [SuppressUnmanagedCodeSecurity] 31 | [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true)] 32 | public delegate void RioCloseCompletionQueue([In] IntPtr cq); 33 | 34 | [SuppressUnmanagedCodeSecurity] 35 | [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true)] 36 | public delegate IntPtr RioCreateRequestQueue( 37 | [In] IntPtr socket, 38 | [In] UInt32 maxOutstandingReceive, 39 | [In] UInt32 maxReceiveDataBuffers, 40 | [In] UInt32 maxOutstandingSend, 41 | [In] UInt32 maxSendDataBuffers, 42 | [In] IntPtr receiveCq, 43 | [In] IntPtr sendCq, 44 | [In] long connectionCorrelation 45 | ); 46 | 47 | [SuppressUnmanagedCodeSecurity] 48 | [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true)] 49 | public delegate uint RioDequeueCompletion([In] IntPtr cq, [In] IntPtr resultArray, [In] uint resultArrayLength); 50 | 51 | [SuppressUnmanagedCodeSecurity] 52 | [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true)] 53 | public delegate Int32 RioNotify([In] IntPtr cq); 54 | 55 | [SuppressUnmanagedCodeSecurity] 56 | [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true)] 57 | public delegate bool RioResizeCompletionQueue([In] IntPtr cq, [In] UInt32 queueSize); 58 | 59 | [SuppressUnmanagedCodeSecurity] 60 | [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true)] 61 | public delegate bool RioResizeRequestQueue([In] IntPtr rq, [In] UInt32 maxOutstandingReceive, [In] UInt32 maxOutstandingSend); 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/Channels.Networking.Libuv/Internal/WorkQueue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Threading; 5 | 6 | namespace Channels.Networking.Libuv.Internal 7 | { 8 | // Lock free linked list that for multi producers and a single consumer 9 | internal class WorkQueue 10 | { 11 | private Node _head; 12 | 13 | public void Add(T value) 14 | { 15 | Node node = new Node(), oldHead; 16 | node.Value = value; 17 | 18 | do 19 | { 20 | oldHead = _head; 21 | node.Next = _head; 22 | node.Count = 1 + (oldHead?.Count ?? 0); 23 | } while (Interlocked.CompareExchange(ref _head, node, oldHead) != oldHead); 24 | } 25 | 26 | 27 | public Enumerable DequeAll() 28 | { 29 | // swap out the head 30 | var node = Interlocked.Exchange(ref _head, null); 31 | 32 | // we now have a detatched head, but we're backwards 33 | // note: 0/1 are a trivial case 34 | if (node == null || node.Count == 1) 35 | { 36 | return new Enumerable(node); 37 | } 38 | // otherwise, we need to reverse the linked-list 39 | // note: use the iterative method to avoid a stack-dive 40 | Node prev = null; 41 | int count = 1; // rebuild the counts 42 | while (node != null) 43 | { 44 | var next = node.Next; 45 | node.Next = prev; 46 | node.Count = count++; 47 | prev = node; 48 | node = next; 49 | } 50 | return new Enumerable(prev); 51 | } 52 | 53 | public struct Enumerable : IEnumerable 54 | { 55 | private Node _node; 56 | public int Count => _node?.Count ?? 0; 57 | internal Enumerable(Node node) 58 | { 59 | _node = node; 60 | } 61 | public Enumerator GetEnumerator() => new Enumerator(_node); 62 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 63 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 64 | 65 | } 66 | 67 | public struct Enumerator : IEnumerator 68 | { 69 | private Node _next; 70 | private T _current; 71 | internal Enumerator(Node node) 72 | { 73 | _current = default(T); 74 | _next = node; 75 | } 76 | object IEnumerator.Current => _current; 77 | public T Current => _current; 78 | 79 | void IDisposable.Dispose() { } 80 | 81 | public bool MoveNext() 82 | { 83 | if (_next == null) 84 | { 85 | _current = default(T); 86 | return false; 87 | } 88 | _current = _next.Value; 89 | _next = _next.Next; 90 | return true; 91 | } 92 | public void Reset() { throw new NotSupportedException(); } 93 | } 94 | 95 | internal class Node // need internal for Enumerator / Enumerable 96 | { 97 | public T Value; 98 | public Node Next; 99 | public int Count; 100 | } 101 | 102 | } 103 | } -------------------------------------------------------------------------------- /samples/Channels.Samples/RawLibuvHttpServerSample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | using System.Threading; 5 | using Channels.Networking.Libuv; 6 | using Channels.Text.Primitives; 7 | 8 | namespace Channels.Samples 9 | { 10 | public class RawLibuvHttpServerSample 11 | { 12 | public static void Run() 13 | { 14 | var ip = IPAddress.Any; 15 | int port = 5000; 16 | var thread = new UvThread(); 17 | var listener = new UvTcpListener(thread, new IPEndPoint(ip, port)); 18 | listener.OnConnection(async connection => 19 | { 20 | var httpParser = new HttpRequestParser(); 21 | 22 | while (true) 23 | { 24 | // Wait for data 25 | var result = await connection.Input.ReadAsync(); 26 | var input = result.Buffer; 27 | 28 | try 29 | { 30 | if (input.IsEmpty && result.IsCompleted) 31 | { 32 | // No more data 33 | break; 34 | } 35 | 36 | // Parse the input http request 37 | var parseResult = httpParser.ParseRequest(ref input); 38 | 39 | switch (parseResult) 40 | { 41 | case HttpRequestParser.ParseResult.Incomplete: 42 | if (result.IsCompleted) 43 | { 44 | // Didn't get the whole request and the connection ended 45 | throw new EndOfStreamException(); 46 | } 47 | // Need more data 48 | continue; 49 | case HttpRequestParser.ParseResult.Complete: 50 | break; 51 | case HttpRequestParser.ParseResult.BadRequest: 52 | throw new Exception(); 53 | default: 54 | break; 55 | } 56 | 57 | // Writing directly to pooled buffers 58 | var output = connection.Output.Alloc(); 59 | output.WriteUtf8String("HTTP/1.1 200 OK"); 60 | output.WriteUtf8String("\r\nContent-Length: 13"); 61 | output.WriteUtf8String("\r\nContent-Type: text/plain"); 62 | output.WriteUtf8String("\r\n\r\n"); 63 | output.WriteUtf8String("Hello, World!"); 64 | await output.FlushAsync(); 65 | 66 | httpParser.Reset(); 67 | } 68 | finally 69 | { 70 | // Consume the input 71 | connection.Input.Advance(input.Start, input.End); 72 | } 73 | } 74 | }); 75 | 76 | listener.StartAsync().GetAwaiter().GetResult(); 77 | 78 | Console.WriteLine($"Listening on {ip} on port {port}"); 79 | var wh = new ManualResetEventSlim(); 80 | Console.CancelKeyPress += (sender, e) => 81 | { 82 | wh.Set(); 83 | }; 84 | 85 | wh.Wait(); 86 | 87 | listener.Dispose(); 88 | thread.Dispose(); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Channels.Networking.Windows.RIO/Internal/RioThreadPool.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System; 5 | using System.Runtime.InteropServices; 6 | using System.Threading; 7 | using Channels.Networking.Windows.RIO.Internal.Winsock; 8 | 9 | namespace Channels.Networking.Windows.RIO.Internal 10 | { 11 | internal class RioThreadPool 12 | { 13 | const string Kernel_32 = "Kernel32"; 14 | const long INVALID_HANDLE_VALUE = -1; 15 | 16 | private RegisteredIO _rio; 17 | private CancellationToken _token; 18 | private int _maxThreads; 19 | 20 | private IntPtr _socket; 21 | private RioThread[] _rioThreads; 22 | 23 | public unsafe RioThreadPool(RegisteredIO rio, IntPtr socket, CancellationToken token) 24 | { 25 | _socket = socket; 26 | _rio = rio; 27 | _token = token; 28 | 29 | // Count non-HT cores only 30 | var procCount = CpuInfo.PhysicalCoreCount; 31 | // RSS only supports up to 16 cores 32 | _maxThreads = procCount > 16 ? 16 : procCount; 33 | 34 | _rioThreads = new RioThread[_maxThreads]; 35 | for (var i = 0; i < _rioThreads.Length; i++) 36 | { 37 | IntPtr completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, IntPtr.Zero, 0, 0); 38 | 39 | if (completionPort == IntPtr.Zero) 40 | { 41 | var error = GetLastError(); 42 | RioImports.WSACleanup(); 43 | throw new Exception($"ERROR: CreateIoCompletionPort returned {error}"); 44 | } 45 | 46 | var completionMethod = new NotificationCompletion 47 | { 48 | Type = NotificationCompletionType.IocpCompletion, 49 | Iocp = new NotificationCompletionIocp 50 | { 51 | IocpHandle = completionPort, 52 | QueueCorrelation = (ulong) i, 53 | Overlapped = (NativeOverlapped*) (-1) // nativeOverlapped 54 | } 55 | }; 56 | 57 | IntPtr completionQueue = _rio.RioCreateCompletionQueue(RioTcpServer.MaxOutsandingCompletionsPerThread, 58 | completionMethod); 59 | 60 | if (completionQueue == IntPtr.Zero) 61 | { 62 | var error = RioImports.WSAGetLastError(); 63 | RioImports.WSACleanup(); 64 | throw new Exception($"ERROR: RioCreateCompletionQueue returned {error}"); 65 | } 66 | 67 | var thread = new RioThread(i, _token, completionPort, completionQueue, rio); 68 | _rioThreads[i] = thread; 69 | } 70 | 71 | for (var i = 0; i < _rioThreads.Length; i++) 72 | { 73 | var thread = _rioThreads[i]; 74 | thread.Start(); 75 | } 76 | } 77 | 78 | internal RioThread GetThread(long connetionId) 79 | { 80 | return _rioThreads[(connetionId % _maxThreads)]; 81 | } 82 | 83 | [DllImport(Kernel_32, SetLastError = true)] 84 | private static extern IntPtr CreateIoCompletionPort(long handle, IntPtr hExistingCompletionPort, 85 | int puiCompletionKey, uint uiNumberOfConcurrentThreads); 86 | 87 | [DllImport(Kernel_32, SetLastError = true)] 88 | private static extern long GetLastError(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Channels.Networking.Libuv/UvTcpListener.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading.Tasks; 4 | using Channels.Networking.Libuv.Interop; 5 | 6 | namespace Channels.Networking.Libuv 7 | { 8 | public class UvTcpListener : IDisposable 9 | { 10 | private static Action _onConnectionCallback = OnConnectionCallback; 11 | private static Action _startListeningCallback = state => ((UvTcpListener)state).Listen(); 12 | private static Action _stopListeningCallback = state => ((UvTcpListener)state).Shutdown(); 13 | 14 | private readonly IPEndPoint _endpoint; 15 | private readonly UvThread _thread; 16 | 17 | private UvTcpHandle _listenSocket; 18 | private Func _callback; 19 | 20 | private TaskCompletionSource _startedTcs = new TaskCompletionSource(); 21 | 22 | public UvTcpListener(UvThread thread, IPEndPoint endpoint) 23 | { 24 | _thread = thread; 25 | _endpoint = endpoint; 26 | } 27 | 28 | public void OnConnection(Func callback) 29 | { 30 | _callback = callback; 31 | } 32 | 33 | public Task StartAsync() 34 | { 35 | // TODO: Make idempotent 36 | _thread.Post(_startListeningCallback, this); 37 | 38 | return _startedTcs.Task; 39 | } 40 | 41 | public void Dispose() 42 | { 43 | // TODO: Make idempotent 44 | _thread.Post(_stopListeningCallback, this); 45 | } 46 | 47 | private void Shutdown() 48 | { 49 | _listenSocket.Dispose(); 50 | } 51 | 52 | private void Listen() 53 | { 54 | // TODO: Error handling 55 | _listenSocket = new UvTcpHandle(); 56 | _listenSocket.Init(_thread.Loop, null); 57 | _listenSocket.NoDelay(true); 58 | _listenSocket.Bind(_endpoint); 59 | _listenSocket.Listen(10, _onConnectionCallback, this); 60 | 61 | // Don't complete the task on the UV thread 62 | Task.Run(() => _startedTcs.TrySetResult(null)); 63 | } 64 | 65 | private static void OnConnectionCallback(UvStreamHandle listenSocket, int status, Exception error, object state) 66 | { 67 | var listener = (UvTcpListener)state; 68 | 69 | var acceptSocket = new UvTcpHandle(); 70 | 71 | try 72 | { 73 | acceptSocket.Init(listener._thread.Loop, null); 74 | acceptSocket.NoDelay(true); 75 | listenSocket.Accept(acceptSocket); 76 | var connection = new UvTcpConnection(listener._thread, acceptSocket); 77 | ExecuteCallback(listener, connection); 78 | } 79 | catch (UvException) 80 | { 81 | acceptSocket.Dispose(); 82 | } 83 | } 84 | 85 | private static async void ExecuteCallback(UvTcpListener listener, UvTcpConnection connection) 86 | { 87 | try 88 | { 89 | await listener._callback?.Invoke(connection); 90 | } 91 | catch 92 | { 93 | // Swallow exceptions 94 | } 95 | finally 96 | { 97 | // Dispose the connection on task completion 98 | connection.Dispose(); 99 | } 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /src/Channels/MemoryPoolBlock.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | using System.Diagnostics; 4 | using System.Text; 5 | 6 | namespace Channels 7 | { 8 | /// 9 | /// Block tracking object used by the byte buffer memory pool. A slab is a large allocation which is divided into smaller blocks. The 10 | /// individual blocks are then treated as independent array segments. 11 | /// 12 | public class MemoryPoolBlock : OwnedMemory 13 | { 14 | private readonly int _offset; 15 | private readonly int _length; 16 | 17 | /// 18 | /// This object cannot be instantiated outside of the static Create method 19 | /// 20 | protected unsafe MemoryPoolBlock(MemoryPool pool, MemoryPoolSlab slab, int offset, int length) : base(slab.Array, offset, length, slab.NativePointer + offset) 21 | { 22 | _offset = offset; 23 | _length = length; 24 | 25 | Pool = pool; 26 | Slab = slab; 27 | } 28 | 29 | /// 30 | /// Back-reference to the memory pool which this block was allocated from. It may only be returned to this pool. 31 | /// 32 | public MemoryPool Pool { get; } 33 | 34 | /// 35 | /// Back-reference to the slab from which this block was taken, or null if it is one-time-use memory. 36 | /// 37 | public MemoryPoolSlab Slab { get; } 38 | 39 | #if DEBUG 40 | public bool IsLeased { get; set; } 41 | public string Leaser { get; set; } 42 | #endif 43 | 44 | ~MemoryPoolBlock() 45 | { 46 | #if DEBUG 47 | Debug.Assert(Slab == null || !Slab.IsActive, $"{Environment.NewLine}{Environment.NewLine}*** Block being garbage collected instead of returned to pool: {Leaser} ***{Environment.NewLine}"); 48 | #endif 49 | if (Slab != null && Slab.IsActive) 50 | { 51 | // Need to make a new object because this one is being finalized 52 | Pool.Return(new MemoryPoolBlock(Pool, Slab, _offset, _length)); 53 | } 54 | } 55 | 56 | internal void Initialize() 57 | { 58 | if (IsDisposed) 59 | { 60 | Initialize(Slab.Array, _offset, _length, Slab.NativePointer + _offset); 61 | } 62 | } 63 | 64 | internal static MemoryPoolBlock Create( 65 | int offset, 66 | int length, 67 | MemoryPool pool, 68 | MemoryPoolSlab slab) 69 | { 70 | return new MemoryPoolBlock(pool, slab, offset, length) 71 | { 72 | #if DEBUG 73 | Leaser = Environment.StackTrace, 74 | #endif 75 | }; 76 | } 77 | 78 | /// 79 | /// ToString overridden for debugger convenience. This displays the "active" byte information in this block as ASCII characters. 80 | /// ToString overridden for debugger convenience. This displays the byte information in this block as ASCII characters. 81 | /// 82 | /// 83 | public override string ToString() 84 | { 85 | var builder = new StringBuilder(); 86 | var data = Memory.Span; 87 | 88 | for (int i = 0; i < data.Length; i++) 89 | { 90 | builder.Append((char)data[i]); 91 | } 92 | return builder.ToString(); 93 | } 94 | 95 | protected override void Dispose(bool disposing) 96 | { 97 | Pool.Return(this); 98 | 99 | base.Dispose(disposing); 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /src/Channels/DefaultReadableBufferExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Binary; 3 | using System.IO; 4 | using System.Runtime.CompilerServices; 5 | using System.Threading.Tasks; 6 | 7 | namespace Channels 8 | { 9 | /// 10 | /// Common extension methods against readable buffers 11 | /// 12 | public static class DefaultReadableBufferExtensions 13 | { 14 | /// 15 | /// Copies a to a asynchronously 16 | /// 17 | /// The to copy 18 | /// The target 19 | /// 20 | public static async Task CopyToAsync(this ReadableBuffer buffer, Stream stream) 21 | { 22 | foreach (var memory in buffer) 23 | { 24 | ArraySegment data; 25 | if (memory.TryGetArray(out data)) 26 | { 27 | await stream.WriteAsync(data.Array, data.Offset, data.Count); 28 | } 29 | else 30 | { 31 | // Copy required 32 | var array = memory.Span.ToArray(); 33 | await stream.WriteAsync(array, 0, array.Length); 34 | } 35 | } 36 | } 37 | 38 | public static async Task ReadToEndAsync(this IReadableChannel input) 39 | { 40 | while (true) 41 | { 42 | // Wait for more data 43 | var result = await input.ReadAsync(); 44 | 45 | if (result.IsCompleted) 46 | { 47 | // Read all the data, return it 48 | return result.Buffer; 49 | } 50 | 51 | // Don't advance the buffer so remains in buffer 52 | input.Advance(result.Buffer.Start, result.Buffer.End); 53 | } 54 | } 55 | 56 | /// 57 | /// Reads a structure of type out of a buffer of bytes. 58 | /// 59 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 60 | public static T ReadBigEndian<[Primitive]T>(this ReadableBuffer buffer) where T : struct 61 | { 62 | var memory = buffer.First; 63 | int len = Unsafe.SizeOf(); 64 | var value = memory.Length >= len ? memory.Span.ReadBigEndian() : ReadMultiBig(buffer, len); 65 | return value; 66 | } 67 | 68 | /// 69 | /// Reads a structure of type out of a buffer of bytes. 70 | /// 71 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 72 | public static T ReadLittleEndian<[Primitive]T>(this ReadableBuffer buffer) where T : struct 73 | { 74 | var memory = buffer.First; 75 | int len = Unsafe.SizeOf(); 76 | var value = memory.Length >= len ? memory.Span.ReadLittleEndian() : ReadMultiLittle(buffer, len); 77 | return value; 78 | } 79 | 80 | private static unsafe T ReadMultiBig<[Primitive]T>(ReadableBuffer buffer, int len) where T : struct 81 | { 82 | byte* local = stackalloc byte[len]; 83 | var localSpan = new Span(local, len); 84 | buffer.Slice(0, len).CopyTo(localSpan); 85 | return localSpan.ReadBigEndian(); 86 | } 87 | 88 | private static unsafe T ReadMultiLittle<[Primitive]T>(ReadableBuffer buffer, int len) where T : struct 89 | { 90 | byte* local = stackalloc byte[len]; 91 | var localSpan = new Span(local, len); 92 | buffer.Slice(0, len).CopyTo(localSpan); 93 | return localSpan.ReadLittleEndian(); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/Channels/MemoryPoolSlab.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Channels 5 | { 6 | /// 7 | /// Slab tracking object used by the byte buffer memory pool. A slab is a large allocation which is divided into smaller blocks. The 8 | /// individual blocks are then treated as independant array segments. 9 | /// 10 | public class MemoryPoolSlab : IDisposable 11 | { 12 | /// 13 | /// This handle pins the managed array in memory until the slab is disposed. This prevents it from being 14 | /// relocated and enables any subsections of the array to be used as native memory pointers to P/Invoked API calls. 15 | /// 16 | private readonly GCHandle _gcHandle; 17 | private readonly IntPtr _nativePointer; 18 | private byte[] _data; 19 | 20 | private bool _isActive; 21 | internal Action _deallocationCallback; 22 | private bool _disposedValue; 23 | 24 | public MemoryPoolSlab(byte[] data) 25 | { 26 | _data = data; 27 | _gcHandle = GCHandle.Alloc(data, GCHandleType.Pinned); 28 | _nativePointer = _gcHandle.AddrOfPinnedObject(); 29 | _isActive = true; 30 | } 31 | 32 | /// 33 | /// True as long as the blocks from this slab are to be considered returnable to the pool. In order to shrink the 34 | /// memory pool size an entire slab must be removed. That is done by (1) setting IsActive to false and removing the 35 | /// slab from the pool's _slabs collection, (2) as each block currently in use is Return()ed to the pool it will 36 | /// be allowed to be garbage collected rather than re-pooled, and (3) when all block tracking objects are garbage 37 | /// collected and the slab is no longer references the slab will be garbage collected and the memory unpinned will 38 | /// be unpinned by the slab's Dispose. 39 | /// 40 | public bool IsActive => _isActive; 41 | 42 | public IntPtr NativePointer => _nativePointer; 43 | 44 | public byte[] Array => _data; 45 | 46 | public int Length => _data.Length; 47 | 48 | public static MemoryPoolSlab Create(int length) 49 | { 50 | // allocate and pin requested memory length 51 | var array = new byte[length]; 52 | 53 | // allocate and return slab tracking object 54 | return new MemoryPoolSlab(array); 55 | } 56 | 57 | protected virtual void Dispose(bool disposing) 58 | { 59 | if (!_disposedValue) 60 | { 61 | if (disposing) 62 | { 63 | // N/A: dispose managed state (managed objects). 64 | } 65 | 66 | _isActive = false; 67 | 68 | _deallocationCallback?.Invoke(this); 69 | 70 | if (_gcHandle.IsAllocated) 71 | { 72 | _gcHandle.Free(); 73 | } 74 | 75 | // set large fields to null. 76 | _data = null; 77 | 78 | _disposedValue = true; 79 | } 80 | } 81 | 82 | // override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. 83 | ~MemoryPoolSlab() 84 | { 85 | // Do not change this code. Put cleanup code in Dispose(bool disposing) above. 86 | Dispose(false); 87 | } 88 | 89 | // This code added to correctly implement the disposable pattern. 90 | public void Dispose() 91 | { 92 | // Do not change this code. Put cleanup code in Dispose(bool disposing) above. 93 | Dispose(true); 94 | // uncomment the following line if the finalizer is overridden above. 95 | GC.SuppressFinalize(this); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /test/Channels.Tests/SignalFacts.cs: -------------------------------------------------------------------------------- 1 | using Channels.Networking.Sockets.Internal; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Xunit; 5 | 6 | namespace Channels.Tests 7 | { 8 | public class SignalFacts 9 | { 10 | [Fact] 11 | public void SignalIsNotCompletedByDefault() 12 | { 13 | Assert.False(new Signal().IsCompleted); 14 | } 15 | 16 | [Fact] 17 | public void SignalBecomesCompletedWhenSet() 18 | { 19 | var signal = new Signal(); 20 | signal.Set(); 21 | Assert.True(signal.IsCompleted); 22 | } 23 | 24 | [Fact] 25 | public void SignalDoesNotBecomeCompletedWhenResultFetched() 26 | { 27 | var signal = new Signal(); 28 | signal.Set(); 29 | signal.GetResult(); 30 | Assert.True(signal.IsCompleted); 31 | } 32 | 33 | [Fact] 34 | public void AlreadySetSignalManuallyAwaitableWithoutExternalCaller() 35 | { 36 | // here we're simulating the thread-race scenario: 37 | // thread A: checks IsCompleted, sees false 38 | // thread B: sets the status 39 | // thread A: asks for the awaiter and adds a continuation 40 | 41 | var signal = new Signal(); 42 | 43 | // A 44 | Assert.False(signal.IsCompleted); 45 | 46 | // B 47 | signal.Set(); 48 | 49 | int wasInvoked = 0; 50 | 51 | // A 52 | signal.OnCompleted(() => 53 | { 54 | signal.GetResult(); // compiler awaiter always does this 55 | Interlocked.Increment(ref wasInvoked); 56 | }); 57 | 58 | Assert.Equal(1, Volatile.Read(ref wasInvoked)); 59 | Assert.True(signal.IsCompleted); 60 | } 61 | 62 | [Fact] 63 | public async Task AlreadySetSignalCompilerAwaitableWithoutExternalCaller() 64 | { 65 | var signal = new Signal(); 66 | signal.Set(); 67 | await signal; 68 | Assert.True(signal.IsCompleted); 69 | signal.Reset(); 70 | Assert.False(signal.IsCompleted); 71 | } 72 | 73 | [Fact] 74 | public async Task SignalCompilerAwaitableWithExternalCaller() 75 | { 76 | var signal = new Signal(); 77 | ThreadPool.QueueUserWorkItem(_ => 78 | { 79 | Thread.Sleep(100); 80 | signal.Set(); 81 | }); 82 | await signal; 83 | Assert.True(signal.IsCompleted); 84 | } 85 | 86 | [Fact] 87 | public void ResetClearsContinuation() 88 | { 89 | var signal = new Signal(); 90 | bool wasInvoked = false; 91 | signal.OnCompleted(() => 92 | { 93 | signal.GetResult(); 94 | wasInvoked = true; 95 | }); 96 | signal.Reset(); 97 | signal.Set(); 98 | Assert.False(wasInvoked); 99 | } 100 | 101 | [Fact] 102 | public void CallingSetTwiceHasNoBacklog() 103 | { 104 | var signal = new Signal(); 105 | signal.Set(); 106 | signal.Set(); 107 | Assert.True(signal.IsCompleted); 108 | signal.GetResult(); 109 | Assert.True(signal.IsCompleted); 110 | signal.Reset(); 111 | Assert.False(signal.IsCompleted); // only set "once" 112 | } 113 | 114 | [Fact] 115 | public void CallingSetTwiceOnlyInvokesContinuationOnce() 116 | { 117 | var signal = new Signal(); 118 | int count = 0; 119 | 120 | signal.OnCompleted(() => Interlocked.Increment(ref count)); 121 | signal.Set(); 122 | signal.GetResult(); 123 | signal.Set(); 124 | signal.GetResult(); 125 | 126 | Assert.Equal(1, Volatile.Read(ref count)); 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /samples/Channels.Samples/HttpServer/HttpResponseStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace Channels.Samples.Http 7 | { 8 | public class HttpResponseStream : Stream 9 | { 10 | private readonly static Task _initialCachedTask = Task.FromResult(0); 11 | private Task _cachedTask = _initialCachedTask; 12 | 13 | private readonly HttpConnection _connection; 14 | 15 | public HttpResponseStream(HttpConnection connection) 16 | { 17 | _connection = connection; 18 | } 19 | 20 | public override bool CanRead => false; 21 | 22 | public override bool CanSeek => false; 23 | 24 | public override bool CanWrite => true; 25 | 26 | public override long Length 27 | { 28 | get 29 | { 30 | throw new NotSupportedException(); 31 | } 32 | } 33 | 34 | public override long Position 35 | { 36 | get 37 | { 38 | throw new NotSupportedException(); 39 | } 40 | set 41 | { 42 | throw new NotSupportedException(); 43 | } 44 | } 45 | 46 | public override long Seek(long offset, SeekOrigin origin) 47 | { 48 | throw new NotSupportedException(); 49 | } 50 | 51 | public override void SetLength(long value) 52 | { 53 | throw new NotSupportedException(); 54 | } 55 | public override int Read(byte[] buffer, int offset, int count) 56 | { 57 | throw new NotSupportedException(); 58 | } 59 | 60 | public override void Write(byte[] buffer, int offset, int count) 61 | { 62 | WriteAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult(); 63 | } 64 | 65 | public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken token) 66 | { 67 | return _connection.WriteAsync(new Span(buffer, offset, count)); 68 | } 69 | 70 | public override void Flush() 71 | { 72 | // No-op since writes are immediate. 73 | } 74 | 75 | public override Task FlushAsync(CancellationToken cancellationToken) 76 | { 77 | // No-op since writes are immediate. 78 | return Task.FromResult(0); 79 | } 80 | 81 | #if NET451 82 | public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) 83 | { 84 | var task = WriteAsync(buffer, offset, count, default(CancellationToken), state); 85 | if (callback != null) 86 | { 87 | task.ContinueWith(t => callback.Invoke(t)); 88 | } 89 | return task; 90 | } 91 | 92 | public override void EndWrite(IAsyncResult asyncResult) 93 | { 94 | ((Task)asyncResult).GetAwaiter().GetResult(); 95 | } 96 | 97 | private Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken, object state) 98 | { 99 | var tcs = new TaskCompletionSource(state); 100 | var task = WriteAsync(buffer, offset, count, cancellationToken); 101 | task.ContinueWith((task2, state2) => 102 | { 103 | var tcs2 = (TaskCompletionSource)state2; 104 | if (task2.IsCanceled) 105 | { 106 | tcs2.SetCanceled(); 107 | } 108 | else if (task2.IsFaulted) 109 | { 110 | tcs2.SetException(task2.Exception); 111 | } 112 | else 113 | { 114 | tcs2.SetResult(null); 115 | } 116 | }, tcs, cancellationToken); 117 | return tcs.Task; 118 | } 119 | #endif 120 | } 121 | } -------------------------------------------------------------------------------- /samples/Channels.Samples/HttpServer/ResponseHeaderDictionary.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using System.Text.Formatting; 6 | using Channels; 7 | using Channels.Text.Primitives; 8 | using Microsoft.AspNetCore.Http; 9 | using Microsoft.AspNetCore.Server.Kestrel.Internal.Http; 10 | using Microsoft.Extensions.Primitives; 11 | 12 | namespace Channels.Samples.Http 13 | { 14 | public class ResponseHeaderDictionary : IHeaderDictionary 15 | { 16 | private static readonly DateHeaderValueManager _dateHeaderValueManager = new DateHeaderValueManager(); 17 | private static readonly byte[] _serverHeaderBytes = Encoding.UTF8.GetBytes("\r\nServer: Channels"); 18 | private static readonly byte[] _chunkedHeaderBytes = Encoding.UTF8.GetBytes("\r\nTransfer-Encoding: chunked"); 19 | 20 | private static readonly byte[] _headersStartBytes = Encoding.UTF8.GetBytes("\r\n"); 21 | private static readonly byte[] _headersSeperatorBytes = Encoding.UTF8.GetBytes(": "); 22 | private static readonly byte[] _headersEndBytes = Encoding.UTF8.GetBytes("\r\n\r\n"); 23 | 24 | private readonly HeaderDictionary _headers = new HeaderDictionary(); 25 | 26 | public StringValues this[string key] 27 | { 28 | get 29 | { 30 | return _headers[key]; 31 | } 32 | 33 | set 34 | { 35 | _headers[key] = value; 36 | } 37 | } 38 | 39 | public int Count => _headers.Count; 40 | 41 | public bool IsReadOnly => false; 42 | 43 | public ICollection Keys => _headers.Keys; 44 | 45 | public ICollection Values => _headers.Values; 46 | 47 | public void Add(KeyValuePair item) => _headers.Add(item); 48 | 49 | public void Add(string key, StringValues value) => _headers.Add(key, value); 50 | 51 | public void Clear() 52 | { 53 | _headers.Clear(); 54 | } 55 | 56 | public bool Contains(KeyValuePair item) 57 | { 58 | return _headers.Contains(item); 59 | } 60 | 61 | public bool ContainsKey(string key) 62 | { 63 | return _headers.ContainsKey(key); 64 | } 65 | 66 | public void CopyTo(KeyValuePair[] array, int arrayIndex) 67 | { 68 | _headers.CopyTo(array, arrayIndex); 69 | } 70 | 71 | public IEnumerator> GetEnumerator() 72 | { 73 | return _headers.GetEnumerator(); 74 | } 75 | 76 | public bool Remove(KeyValuePair item) 77 | { 78 | return _headers.Remove(item); 79 | } 80 | 81 | public bool Remove(string key) 82 | { 83 | return _headers.Remove(key); 84 | } 85 | 86 | public bool TryGetValue(string key, out StringValues value) 87 | { 88 | return _headers.TryGetValue(key, out value); 89 | } 90 | 91 | IEnumerator IEnumerable.GetEnumerator() 92 | { 93 | return GetEnumerator(); 94 | } 95 | 96 | public void CopyTo(bool chunk, WritableBuffer buffer) 97 | { 98 | foreach (var header in _headers) 99 | { 100 | buffer.Write(_headersStartBytes); 101 | buffer.WriteUtf8String(header.Key); 102 | buffer.Write(_headersSeperatorBytes); 103 | buffer.WriteUtf8String(header.Value.ToString()); 104 | } 105 | 106 | if (chunk) 107 | { 108 | buffer.Write(_chunkedHeaderBytes); 109 | } 110 | 111 | buffer.Write(_serverHeaderBytes); 112 | var date = _dateHeaderValueManager.GetDateHeaderValues().Bytes; 113 | buffer.Write(date); 114 | 115 | buffer.Write(_headersEndBytes); 116 | } 117 | 118 | public void Reset() => _headers.Clear(); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/Channels.Networking.Libuv/UvThread.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.CompilerServices; 4 | using System.Threading; 5 | using Channels.Networking.Libuv.Interop; 6 | using Channels.Networking.Libuv.Internal; 7 | 8 | namespace Channels.Networking.Libuv 9 | { 10 | // This class needs a bunch of work to make sure it's thread safe 11 | public class UvThread : ICriticalNotifyCompletion, IDisposable 12 | { 13 | private readonly Thread _thread = new Thread(OnStart) 14 | { 15 | Name = "Libuv event loop" 16 | }; 17 | private readonly ManualResetEventSlim _running = new ManualResetEventSlim(); 18 | private readonly WorkQueue _workQueue = new WorkQueue(); 19 | 20 | private bool _stopping; 21 | private UvAsyncHandle _postHandle; 22 | 23 | public UvThread() 24 | { 25 | WriteReqPool = new WriteReqPool(this); 26 | } 27 | 28 | public Uv Uv { get; private set; } 29 | 30 | public UvLoopHandle Loop { get; private set; } 31 | 32 | public ChannelFactory ChannelFactory { get; } = new ChannelFactory(); 33 | 34 | public WriteReqPool WriteReqPool { get; } 35 | 36 | public void Post(Action callback, object state) 37 | { 38 | if (_stopping) 39 | { 40 | return; 41 | } 42 | 43 | EnsureStarted(); 44 | 45 | var work = new Work 46 | { 47 | Callback = callback, 48 | State = state 49 | }; 50 | 51 | _workQueue.Add(work); 52 | 53 | _postHandle.Send(); 54 | } 55 | 56 | // Awaiter impl 57 | public bool IsCompleted => Thread.CurrentThread.ManagedThreadId == _thread.ManagedThreadId; 58 | 59 | public UvThread GetAwaiter() => this; 60 | 61 | public void GetResult() 62 | { 63 | 64 | } 65 | 66 | private static void OnStart(object state) 67 | { 68 | ((UvThread)state).RunLoop(); 69 | } 70 | 71 | private void RunLoop() 72 | { 73 | Uv = new Uv(); 74 | 75 | Loop = new UvLoopHandle(); 76 | Loop.Init(Uv); 77 | 78 | _postHandle = new UvAsyncHandle(); 79 | _postHandle.Init(Loop, OnPost, null); 80 | 81 | _running.Set(); 82 | 83 | Uv.run(Loop, 0); 84 | 85 | _postHandle.Reference(); 86 | _postHandle.Dispose(); 87 | 88 | Uv.run(Loop, 0); 89 | 90 | Loop.Dispose(); 91 | } 92 | 93 | private void OnPost() 94 | { 95 | foreach (var work in _workQueue.DequeAll()) 96 | { 97 | work.Callback(work.State); 98 | } 99 | 100 | if (_stopping) 101 | { 102 | WriteReqPool.Dispose(); 103 | 104 | _postHandle.Unreference(); 105 | } 106 | } 107 | 108 | private void EnsureStarted() 109 | { 110 | if (!_running.IsSet) 111 | { 112 | _thread.Start(this); 113 | 114 | _running.Wait(); 115 | } 116 | } 117 | 118 | private void Stop() 119 | { 120 | if (!_stopping) 121 | { 122 | _stopping = true; 123 | 124 | _postHandle.Send(); 125 | 126 | _thread.Join(); 127 | 128 | // REVIEW: Can you restart the thread? 129 | } 130 | } 131 | 132 | public void UnsafeOnCompleted(Action continuation) 133 | { 134 | OnCompleted(continuation); 135 | } 136 | 137 | public void OnCompleted(Action continuation) 138 | { 139 | Post(state => ((Action)state)(), continuation); 140 | } 141 | 142 | public void Dispose() 143 | { 144 | Stop(); 145 | 146 | ChannelFactory.Dispose(); 147 | } 148 | 149 | private struct Work 150 | { 151 | public object State; 152 | public Action Callback; 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/Channels/WritableBuffer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Buffers; 6 | using System.Threading.Tasks; 7 | 8 | namespace Channels 9 | { 10 | /// 11 | /// Represents a buffer that can write a sequential series of bytes. 12 | /// 13 | public struct WritableBuffer : IOutput 14 | { 15 | private Channel _channel; 16 | 17 | internal WritableBuffer(Channel channel) 18 | { 19 | _channel = channel; 20 | } 21 | 22 | /// 23 | /// Available memory. 24 | /// 25 | public Memory Memory => _channel.Memory; 26 | 27 | /// 28 | /// Returns the number of bytes currently written and uncommitted. 29 | /// 30 | public int BytesWritten => AsReadableBuffer().Length; 31 | 32 | Span IOutput.Buffer => Memory.Span; 33 | 34 | void IOutput.Enlarge(int desiredBufferLength) => Ensure(desiredBufferLength); 35 | 36 | /// 37 | /// Obtain a readable buffer over the data written but uncommitted to this buffer. 38 | /// 39 | public ReadableBuffer AsReadableBuffer() 40 | { 41 | return _channel.AsReadableBuffer(); 42 | } 43 | 44 | /// 45 | /// Ensures the specified number of bytes are available. 46 | /// Will assign more memory to the if requested amount not currently available. 47 | /// 48 | /// number of bytes 49 | /// 50 | /// Used when writing to directly. 51 | /// 52 | /// 53 | /// More requested than underlying can allocate in a contiguous block. 54 | /// 55 | public void Ensure(int count = 1) 56 | { 57 | _channel.Ensure(count); 58 | } 59 | 60 | /// 61 | /// Appends the to the in-place without copies. 62 | /// 63 | /// The to append 64 | public void Append(ReadableBuffer buffer) 65 | { 66 | _channel.Append(buffer); 67 | } 68 | 69 | /// 70 | /// Moves forward the underlying 's write cursor but does not commit the data. 71 | /// 72 | /// number of bytes to be marked as written. 73 | /// Forwards the start of available by . 74 | /// is larger than the current data available data. 75 | /// is negative. 76 | public void Advance(int bytesWritten) 77 | { 78 | _channel.AdvanceWriter(bytesWritten); 79 | } 80 | 81 | /// 82 | /// Commits all outstanding written data to the underlying so they can be read 83 | /// and seals the so no more data can be committed. 84 | /// 85 | /// 86 | /// While an on-going conncurent read may pick up the data, should be called to signal the reader. 87 | /// 88 | public void Commit() 89 | { 90 | _channel.Commit(); 91 | } 92 | 93 | /// 94 | /// Signals the data is available. 95 | /// Will if necessary. 96 | /// 97 | /// A task that completes when the data is fully flushed. 98 | public Task FlushAsync() 99 | { 100 | return _channel.FlushAsync(); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /samples/Channels.Samples/HttpServer/HttpRequestParser.cs: -------------------------------------------------------------------------------- 1 | using Channels.Samples.Http; 2 | using Channels.Text.Primitives; 3 | 4 | namespace Channels.Samples 5 | { 6 | public class HttpRequestParser 7 | { 8 | private ParsingState _state; 9 | 10 | private PreservedBuffer _httpVersion; 11 | private PreservedBuffer _path; 12 | private PreservedBuffer _method; 13 | 14 | public ReadableBuffer HttpVersion => _httpVersion.Buffer; 15 | public ReadableBuffer Path => _path.Buffer; 16 | public ReadableBuffer Method => _method.Buffer; 17 | 18 | public RequestHeaderDictionary RequestHeaders = new RequestHeaderDictionary(); 19 | 20 | public ParseResult ParseRequest(ref ReadableBuffer buffer) 21 | { 22 | if (_state == ParsingState.StartLine) 23 | { 24 | // Find \n 25 | ReadCursor delim; 26 | ReadableBuffer startLine; 27 | if (!buffer.TrySliceTo((byte)'\r', (byte)'\n', out startLine, out delim)) 28 | { 29 | return ParseResult.Incomplete; 30 | } 31 | 32 | // Move the buffer to the rest 33 | buffer = buffer.Slice(delim).Slice(2); 34 | 35 | ReadableBuffer method; 36 | if (!startLine.TrySliceTo((byte)' ', out method, out delim)) 37 | { 38 | return ParseResult.BadRequest; 39 | } 40 | 41 | _method = method.Preserve(); 42 | 43 | // Skip ' ' 44 | startLine = startLine.Slice(delim).Slice(1); 45 | 46 | ReadableBuffer path; 47 | if (!startLine.TrySliceTo((byte)' ', out path, out delim)) 48 | { 49 | return ParseResult.BadRequest; 50 | } 51 | 52 | _path = path.Preserve(); 53 | 54 | // Skip ' ' 55 | startLine = startLine.Slice(delim).Slice(1); 56 | 57 | var httpVersion = startLine; 58 | if (httpVersion.IsEmpty) 59 | { 60 | return ParseResult.BadRequest; 61 | } 62 | 63 | _httpVersion = httpVersion.Preserve(); 64 | 65 | _state = ParsingState.Headers; 66 | } 67 | 68 | // Parse headers 69 | // key: value\r\n 70 | 71 | while (!buffer.IsEmpty) 72 | { 73 | var headerName = default(ReadableBuffer); 74 | var headerValue = default(ReadableBuffer); 75 | 76 | // End of the header 77 | // \n 78 | ReadCursor delim; 79 | ReadableBuffer headerPair; 80 | if (!buffer.TrySliceTo((byte)'\r', (byte)'\n', out headerPair, out delim)) 81 | { 82 | return ParseResult.Incomplete; 83 | } 84 | 85 | buffer = buffer.Slice(delim).Slice(2); 86 | 87 | // End of headers 88 | if (headerPair.IsEmpty) 89 | { 90 | return ParseResult.Complete; 91 | } 92 | 93 | // : 94 | if (!headerPair.TrySliceTo((byte)':', out headerName, out delim)) 95 | { 96 | return ParseResult.BadRequest; 97 | } 98 | 99 | headerName = headerName.TrimStart(); 100 | headerPair = headerPair.Slice(delim).Slice(1); 101 | 102 | headerValue = headerPair.TrimStart(); 103 | RequestHeaders.SetHeader(ref headerName, ref headerValue); 104 | } 105 | 106 | return ParseResult.Incomplete; 107 | } 108 | 109 | public void Reset() 110 | { 111 | _state = ParsingState.StartLine; 112 | 113 | _method.Dispose(); 114 | _path.Dispose(); 115 | _httpVersion.Dispose(); 116 | 117 | RequestHeaders.Reset(); 118 | } 119 | 120 | public enum ParseResult 121 | { 122 | Incomplete, 123 | Complete, 124 | BadRequest, 125 | } 126 | 127 | private enum ParsingState 128 | { 129 | StartLine, 130 | Headers 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /test/Channels.Tests/ReadableBufferReaderFacts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Xunit; 6 | 7 | namespace Channels.Tests 8 | { 9 | public class ReadableBufferReaderFacts 10 | { 11 | [Fact] 12 | public void PeekReturnsByteWithoutMoving() 13 | { 14 | var reader = new ReadableBufferReader(ReadableBuffer.Create(new byte[] { 1, 2 }, 0, 2)); 15 | Assert.Equal(1, reader.Peek()); 16 | Assert.Equal(1, reader.Peek()); 17 | } 18 | 19 | [Fact] 20 | public void TakeReturnsByteAndMoves() 21 | { 22 | var reader = new ReadableBufferReader(ReadableBuffer.Create(new byte[] { 1, 2 }, 0, 2)); 23 | Assert.Equal(1, reader.Take()); 24 | Assert.Equal(2, reader.Take()); 25 | Assert.Equal(-1, reader.Take()); 26 | } 27 | 28 | [Fact] 29 | public void PeekReturnsMinuOneByteInTheEnd() 30 | { 31 | var reader = new ReadableBufferReader(ReadableBuffer.Create(new byte[] { 1, 2 }, 0, 2)); 32 | Assert.Equal(1, reader.Take()); 33 | Assert.Equal(2, reader.Take()); 34 | Assert.Equal(-1, reader.Peek()); 35 | } 36 | 37 | [Fact] 38 | public async Task TakeTraversesSegments() 39 | { 40 | using (var channelFactory = new ChannelFactory()) 41 | { 42 | var channel = channelFactory.CreateChannel(); 43 | var w = channel.Alloc(); 44 | w.Append(ReadableBuffer.Create(new byte[] { 1 }, 0, 1)); 45 | w.Append(ReadableBuffer.Create(new byte[] { 2 }, 0, 1)); 46 | w.Append(ReadableBuffer.Create(new byte[] { 3 }, 0, 1)); 47 | await w.FlushAsync(); 48 | 49 | var result = await channel.ReadAsync(); 50 | var buffer = result.Buffer; 51 | var reader = new ReadableBufferReader(buffer); 52 | 53 | Assert.Equal(1, reader.Take()); 54 | Assert.Equal(2, reader.Take()); 55 | Assert.Equal(3, reader.Take()); 56 | Assert.Equal(-1, reader.Take()); 57 | } 58 | } 59 | 60 | [Fact] 61 | public async Task PeekTraversesSegments() 62 | { 63 | using (var channelFactory = new ChannelFactory()) 64 | { 65 | var channel = channelFactory.CreateChannel(); 66 | var w = channel.Alloc(); 67 | w.Append(ReadableBuffer.Create(new byte[] { 1 }, 0, 1)); 68 | w.Append(ReadableBuffer.Create(new byte[] { 2 }, 0, 1)); 69 | await w.FlushAsync(); 70 | 71 | var result = await channel.ReadAsync(); 72 | var buffer = result.Buffer; 73 | var reader = new ReadableBufferReader(buffer); 74 | 75 | Assert.Equal(1, reader.Take()); 76 | Assert.Equal(2, reader.Peek()); 77 | Assert.Equal(2, reader.Take()); 78 | Assert.Equal(-1, reader.Peek()); 79 | Assert.Equal(-1, reader.Take()); 80 | } 81 | } 82 | 83 | [Fact] 84 | public async Task PeekWorkesWithEmptySegments() 85 | { 86 | using (var channelFactory = new ChannelFactory()) 87 | { 88 | var channel = channelFactory.CreateChannel(); 89 | var w = channel.Alloc(); 90 | w.Append(ReadableBuffer.Create(new byte[] { 0 }, 0, 0)); 91 | w.Append(ReadableBuffer.Create(new byte[] { 1 }, 0, 1)); 92 | await w.FlushAsync(); 93 | 94 | var result = await channel.ReadAsync(); 95 | var buffer = result.Buffer; 96 | var reader = new ReadableBufferReader(buffer); 97 | 98 | Assert.Equal(1, reader.Peek()); 99 | Assert.Equal(1, reader.Take()); 100 | Assert.Equal(-1, reader.Peek()); 101 | Assert.Equal(-1, reader.Take()); 102 | } 103 | } 104 | 105 | [Fact] 106 | public void WorkesWithEmptyBuffer() 107 | { 108 | var reader = new ReadableBufferReader(ReadableBuffer.Create(new byte[] { 0 }, 0, 0)); 109 | 110 | Assert.Equal(-1, reader.Peek()); 111 | Assert.Equal(-1, reader.Take()); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/Channels.Compression/ZLibException.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System; 6 | using System.IO; 7 | using System.Security; 8 | using ZErrorCode = Channels.Compression.ZLibNative.ErrorCode; 9 | 10 | namespace Channels.Compression 11 | { 12 | /// 13 | /// This is the exception that is thrown when a ZLib returns an error code indicating an unrecoverable error. 14 | /// 15 | internal class ZLibException : IOException 16 | { 17 | private string _zlibErrorContext = null; 18 | private string _zlibErrorMessage = null; 19 | private ZErrorCode _zlibErrorCode = ZErrorCode.Ok; 20 | 21 | 22 | 23 | /// 24 | /// This is the preferred constructor to use. 25 | /// The other constructors are provided for compliance to Fx design guidelines. 26 | /// 27 | /// A (localised) human readable error description. 28 | /// A description of the context within zlib where the error occurred (e.g. the function name). 29 | /// The error code returned by a ZLib function that caused this exception. 30 | /// The string provided by ZLib as error information (unlocalised). 31 | public ZLibException(string message, string zlibErrorContext, int zlibErrorCode, string zlibErrorMessage) : 32 | base(message) 33 | { 34 | Init(zlibErrorContext, (ZErrorCode)zlibErrorCode, zlibErrorMessage); 35 | } 36 | 37 | 38 | /// 39 | /// This constructor is provided in compliance with common NetFx design patterns; 40 | /// developers should prefer using the constructor 41 | /// public ZLibException(string message, string zlibErrorContext, ZLibNative.ErrorCode zlibErrorCode, string zlibErrorMessage). 42 | /// 43 | public ZLibException() 44 | : base() 45 | { 46 | Init(); 47 | } 48 | 49 | 50 | /// 51 | /// This constructor is provided in compliance with common NetFx design patterns; 52 | /// developers should prefer using the constructor 53 | /// public ZLibException(string message, string zlibErrorContext, ZLibNative.ErrorCode zlibErrorCode, string zlibErrorMessage). 54 | /// 55 | /// The error message that explains the reason for the exception. 56 | public ZLibException(string message) 57 | : base(message) 58 | { 59 | Init(); 60 | } 61 | 62 | 63 | /// 64 | /// This constructor is provided in compliance with common NetFx design patterns; 65 | /// developers should prefer using the constructor 66 | /// public ZLibException(string message, string zlibErrorContext, ZLibNative.ErrorCode zlibErrorCode, string zlibErrorMessage). 67 | /// 68 | /// The error message that explains the reason for the exception. 69 | /// The exception that is the cause of the current exception, or a null. 70 | public ZLibException(string message, Exception inner) 71 | : base(message, inner) 72 | { 73 | Init(); 74 | } 75 | 76 | private void Init() 77 | { 78 | Init("", ZErrorCode.Ok, ""); 79 | } 80 | 81 | private void Init(string zlibErrorContext, ZErrorCode zlibErrorCode, string zlibErrorMessage) 82 | { 83 | _zlibErrorContext = zlibErrorContext; 84 | _zlibErrorCode = zlibErrorCode; 85 | _zlibErrorMessage = zlibErrorMessage; 86 | } 87 | 88 | 89 | public string ZLibContext 90 | { 91 | [SecurityCritical] 92 | get 93 | { return _zlibErrorContext; } 94 | } 95 | 96 | public int ZLibErrorCode 97 | { 98 | [SecurityCritical] 99 | get 100 | { return (int)_zlibErrorCode; } 101 | } 102 | 103 | public string ZLibErrorMessage 104 | { 105 | [SecurityCritical] 106 | get { return _zlibErrorMessage; } 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /samples/Channels.Samples/HttpServer/HttpConnection.Features.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Threading.Tasks; 6 | using Channels.Text.Primitives; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.AspNetCore.Http.Features; 9 | 10 | namespace Channels.Samples.Http 11 | { 12 | public partial class HttpConnection : IHttpRequestFeature, IHttpResponseFeature, IFeatureCollection 13 | { 14 | private FeatureCollection _features = new FeatureCollection(); 15 | 16 | public object this[Type key] 17 | { 18 | get 19 | { 20 | return GetFeature(key); 21 | } 22 | 23 | set 24 | { 25 | SetFeature(key, value); 26 | } 27 | } 28 | 29 | private object GetFeature(Type key) 30 | { 31 | if (key == typeof(IHttpRequestFeature)) 32 | { 33 | return this; 34 | } 35 | 36 | if (key == typeof(IHttpResponseFeature)) 37 | { 38 | return this; 39 | } 40 | 41 | return _features[key]; 42 | } 43 | 44 | private void SetFeature(Type key, object value) 45 | { 46 | _features[key] = value; 47 | } 48 | 49 | public bool HasStarted { get; set; } 50 | 51 | Stream IHttpRequestFeature.Body 52 | { 53 | get 54 | { 55 | return _requestBody; 56 | } 57 | set 58 | { 59 | 60 | } 61 | } 62 | 63 | Stream IHttpResponseFeature.Body 64 | { 65 | get 66 | { 67 | return _responseBody; 68 | } 69 | set 70 | { 71 | 72 | } 73 | } 74 | 75 | IHeaderDictionary IHttpResponseFeature.Headers 76 | { 77 | get 78 | { 79 | return ResponseHeaders; 80 | } 81 | 82 | set 83 | { 84 | throw new NotSupportedException(); 85 | } 86 | } 87 | 88 | IHeaderDictionary IHttpRequestFeature.Headers 89 | { 90 | get 91 | { 92 | return RequestHeaders; 93 | } 94 | 95 | set 96 | { 97 | throw new NotSupportedException(); 98 | } 99 | } 100 | 101 | public bool IsReadOnly => false; 102 | 103 | private string _method; 104 | string IHttpRequestFeature.Method 105 | { 106 | get 107 | { 108 | if (_method == null) 109 | { 110 | _method = Method.GetAsciiString(); 111 | } 112 | 113 | return _method; 114 | } 115 | set 116 | { 117 | _method = value; 118 | } 119 | } 120 | 121 | private string _path; 122 | string IHttpRequestFeature.Path 123 | { 124 | get 125 | { 126 | if (_path == null) 127 | { 128 | _path = Path.GetAsciiString(); 129 | } 130 | return _path; 131 | } 132 | set 133 | { 134 | _path = value; 135 | } 136 | } 137 | 138 | public string PathBase { get; set; } 139 | 140 | public string Protocol { get; set; } 141 | 142 | public string QueryString { get; set; } 143 | 144 | public string RawTarget { get; set; } 145 | 146 | public string ReasonPhrase { get; set; } 147 | 148 | public int Revision { get; set; } 149 | 150 | public string Scheme { get; set; } = "http"; 151 | 152 | public int StatusCode { get; set; } 153 | 154 | public TFeature Get() 155 | { 156 | return (TFeature)this[typeof(TFeature)]; 157 | } 158 | 159 | public IEnumerator> GetEnumerator() 160 | { 161 | throw new NotImplementedException(); 162 | } 163 | 164 | public void OnCompleted(Func callback, object state) 165 | { 166 | throw new NotImplementedException(); 167 | } 168 | 169 | public void OnStarting(Func callback, object state) 170 | { 171 | throw new NotImplementedException(); 172 | } 173 | 174 | public void Set(TFeature instance) 175 | { 176 | this[typeof(TFeature)] = instance; 177 | } 178 | 179 | IEnumerator IEnumerable.GetEnumerator() 180 | { 181 | throw new NotSupportedException(); 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /samples/Channels.Samples/Framing/Codec.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | using System.Text; 5 | using System.Text.Formatting; 6 | using System.Threading.Tasks; 7 | using Channels.Networking.Libuv; 8 | using Channels.Text.Primitives; 9 | 10 | namespace Channels.Samples.Framing 11 | { 12 | public static class ProtocolHandling 13 | { 14 | public static void Run() 15 | { 16 | var ip = IPAddress.Any; 17 | int port = 5000; 18 | var thread = new UvThread(); 19 | var listener = new UvTcpListener(thread, new IPEndPoint(ip, port)); 20 | listener.OnConnection(async connection => 21 | { 22 | var channel = MakePipeline(connection); 23 | 24 | var decoder = new LineDecoder(); 25 | var handler = new LineHandler(); 26 | 27 | // Initialize the handler with the channel 28 | handler.Initialize(channel); 29 | 30 | try 31 | { 32 | while (true) 33 | { 34 | // Wait for data 35 | var result = await channel.Input.ReadAsync(); 36 | var input = result.Buffer; 37 | 38 | try 39 | { 40 | if (input.IsEmpty && result.IsCompleted) 41 | { 42 | // No more data 43 | break; 44 | } 45 | 46 | Line line; 47 | while (decoder.TryDecode(ref input, out line)) 48 | { 49 | await handler.HandleAsync(line); 50 | } 51 | 52 | if (!input.IsEmpty && result.IsCompleted) 53 | { 54 | // Didn't get the whole frame and the connection ended 55 | throw new EndOfStreamException(); 56 | } 57 | } 58 | finally 59 | { 60 | // Consume the input 61 | channel.Input.Advance(input.Start, input.End); 62 | } 63 | } 64 | } 65 | finally 66 | { 67 | // Close the input channel, which will tell the producer to stop producing 68 | channel.Input.Complete(); 69 | 70 | // Close the output channel, which will close the connection 71 | channel.Output.Complete(); 72 | } 73 | }); 74 | 75 | listener.StartAsync().GetAwaiter().GetResult(); 76 | 77 | Console.WriteLine($"Listening on {ip} on port {port}"); 78 | Console.ReadKey(); 79 | 80 | listener.Dispose(); 81 | thread.Dispose(); 82 | } 83 | 84 | public static IChannel MakePipeline(IChannel channel) 85 | { 86 | // Do something fancy here to wrap the channel, SSL etc 87 | return channel; 88 | } 89 | } 90 | 91 | public class Line 92 | { 93 | public string Data { get; set; } 94 | } 95 | 96 | public class LineHandler : IFrameHandler 97 | { 98 | private WritableChannelFormatter _formatter; 99 | 100 | public void Initialize(IChannel channel) 101 | { 102 | _formatter = new WritableChannelFormatter(channel.Output, EncodingData.InvariantUtf8); 103 | } 104 | 105 | public Task HandleAsync(Line message) 106 | { 107 | // Echo back to the caller 108 | _formatter.Append(message.Data); 109 | return _formatter.FlushAsync(); 110 | } 111 | } 112 | 113 | public class LineDecoder : IFrameDecoder 114 | { 115 | public bool TryDecode(ref ReadableBuffer input, out Line frame) 116 | { 117 | ReadableBuffer slice; 118 | ReadCursor cursor; 119 | if (input.TrySliceTo((byte)'\r', (byte)'\n', out slice, out cursor)) 120 | { 121 | frame = new Line { Data = slice.GetUtf8String() }; 122 | input = input.Slice(cursor).Slice(1); 123 | return true; 124 | } 125 | 126 | frame = null; 127 | return false; 128 | } 129 | } 130 | 131 | public interface IFrameDecoder 132 | { 133 | bool TryDecode(ref ReadableBuffer input, out TInput frame); 134 | } 135 | 136 | public interface IFrameHandler 137 | { 138 | void Initialize(IChannel channel); 139 | 140 | Task HandleAsync(TInput message); 141 | } 142 | } 143 | --------------------------------------------------------------------------------