

using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using Buttplug.Core;
using Buttplug.Core.Messages;
using vtortola.WebSockets;
using vtortola.WebSockets.Rfc6455;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("Nonpolynomial Labs, LLC")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright Nonpolynomial Labs, LLC")]
[assembly: AssemblyDescription("Websocket Connection Capabilities for Buttplug Clients. (.Net Standard 2.0+)")]
[assembly: AssemblyFileVersion("3.0.1.0")]
[assembly: AssemblyInformationalVersion("3.0.1")]
[assembly: AssemblyProduct("Buttplug.Client.Connectors.WebsocketConnector")]
[assembly: AssemblyTitle("Buttplug.Client.Connectors.WebsocketConnector")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/buttplugio/buttplug-csharp")]
[assembly: AssemblyVersion("3.0.1.0")]
namespace Buttplug.Client.Connectors.WebsocketConnector;
public class ButtplugWebsocketConnector : ButtplugRemoteJSONConnector, IButtplugClientConnector
{
private WebSocketClient _wsClient;
private WebSocket _ws;
private readonly SynchronizationContext _owningDispatcher = SynchronizationContext.Current ?? new SynchronizationContext();
private readonly Uri _uri;
private Channel<string> _channel = Channel.CreateBounded<string>(256);
private Task _readTask;
public bool Connected
{
get
{
WebSocket ws = _ws;
if (ws == null)
{
return false;
}
return ws.IsConnected;
}
}
public event EventHandler Disconnected;
public ButtplugWebsocketConnector(Uri uri)
{
_uri = uri;
}
public async Task ConnectAsync(CancellationToken token = default(CancellationToken))
{
if (_ws != null)
{
throw new ButtplugHandshakeException("Websocket connector is already connected.", 0u, (Exception)null);
}
WebSocketListenerOptions val = new WebSocketListenerOptions
{
SendBufferSize = 8192,
BufferManager = BufferManager.CreateBufferManager(819200L, 8192),
PingTimeout = Timeout.InfiniteTimeSpan,
PingMode = (PingMode)0
};
WebSocketFactoryCollectionExtensions.RegisterRfc6455(val.Standards, (Action<WebSocketFactoryRfc6455>)null);
_wsClient = new WebSocketClient(val);
try
{
_ws = await _wsClient.ConnectAsync(_uri, token).ConfigureAwait(continueOnCapturedContext: false);
}
catch (Exception ex)
{
throw new ButtplugClientConnectorException("Websocket Connection Exception! See Inner Exception", ex);
}
_readTask = Task.Run(async delegate
{
await RunClientLoop(token).ConfigureAwait(continueOnCapturedContext: false);
}, token);
}
public async Task DisconnectAsync(CancellationToken token = default(CancellationToken))
{
try
{
WebSocket ws = _ws;
if (ws != null && ws.IsConnected)
{
await _ws.CloseAsync().ConfigureAwait(continueOnCapturedContext: false);
}
}
catch
{
}
await _readTask.ConfigureAwait(continueOnCapturedContext: false);
}
public async Task<ButtplugMessage> SendAsync(ButtplugMessage msg, CancellationToken token)
{
var (item, msgPromise) = ((ButtplugRemoteJSONConnector)this).PrepareMessage(msg);
await _channel.Writer.WriteAsync(item);
return await msgPromise.ConfigureAwait(continueOnCapturedContext: false);
}
private async Task RunClientLoop(CancellationToken token)
{
_ = 4;
try
{
UTF8Encoding utf8NoBom = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: false);
Task<WebSocketMessageReadStream> readTask = _ws.ReadMessageAsync(token);
Task<string> writeTask = _channel.Reader.ReadAsync(token).AsTask();
while (_ws.IsConnected && !token.IsCancellationRequested)
{
await Task.WhenAny(new Task[2] { readTask, writeTask });
if (readTask.IsCompleted)
{
WebSocketMessageReadStream val = await readTask.ConfigureAwait(continueOnCapturedContext: false);
if (val == null)
{
break;
}
if ((int)val.MessageType == 1)
{
string text = string.Empty;
using (StreamReader reader = new StreamReader((Stream)(object)val, utf8NoBom))
{
text = await reader.ReadToEndAsync();
}
((ButtplugRemoteJSONConnector)this).ReceiveMessages(text);
}
readTask = _ws.ReadMessageAsync(token);
continue;
}
if (readTask.IsCanceled)
{
break;
}
if (writeTask.IsCompleted)
{
try
{
string text2 = await writeTask.ConfigureAwait(continueOnCapturedContext: false);
if (text2 != null)
{
WebSocket ws = _ws;
if (ws != null && ws.IsConnected)
{
await WebSocketStringExtensions.WriteStringAsync(_ws, text2, token).ConfigureAwait(continueOnCapturedContext: false);
writeTask = _channel.Reader.ReadAsync(token).AsTask();
continue;
}
}
}
catch (WebSocketException val2)
{
WebSocketException val3 = val2;
throw new ButtplugClientConnectorException("Websocket Client Read Error", (Exception)(object)val3);
}
break;
}
if (writeTask.IsCanceled)
{
Console.WriteLine("Write cancelled");
}
}
}
catch (Exception)
{
}
finally
{
_ws.CloseAsync().Dispose();
_ws = null;
_owningDispatcher.Send(delegate
{
((ButtplugRemoteJSONConnector)this).Dispose();
}, null);
_owningDispatcher.Send(delegate
{
this.Disconnected?.Invoke(this, EventArgs.Empty);
}, null);
}
}
void IButtplugClientConnector.add_MessageReceived(EventHandler<MessageReceivedEventArgs> value)
{
((ButtplugRemoteJSONConnector)this).MessageReceived += value;
}
void IButtplugClientConnector.remove_MessageReceived(EventHandler<MessageReceivedEventArgs> value)
{
((ButtplugRemoteJSONConnector)this).MessageReceived -= value;
}
void IButtplugClientConnector.add_InvalidMessageReceived(EventHandler<ButtplugExceptionEventArgs> value)
{
((ButtplugRemoteJSONConnector)this).InvalidMessageReceived += value;
}
void IButtplugClientConnector.remove_InvalidMessageReceived(EventHandler<ButtplugExceptionEventArgs> value)
{
((ButtplugRemoteJSONConnector)this).InvalidMessageReceived -= value;
}
}
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Threading;
using System.Threading.Tasks;
using Buttplug.Core;
using Buttplug.Core.Messages;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: InternalsVisibleTo("Buttplug.Test")]
[assembly: InternalsVisibleTo("Buttplug.Client.Test")]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("Nonpolynomial Labs, LLC")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright Nonpolynomial Labs, LLC")]
[assembly: AssemblyDescription("Buttplug Sex Toy Control Library. Contains Core (messages, errors, etc), and Client components. Server can be found in Buttplug.FFI.Server (.Net Standard 2.0+)")]
[assembly: AssemblyFileVersion("3.0.1.0")]
[assembly: AssemblyInformationalVersion("3.0.1")]
[assembly: AssemblyProduct("Buttplug")]
[assembly: AssemblyTitle("Buttplug")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/buttplugio/buttplug-csharp")]
[assembly: AssemblyVersion("3.0.1.0")]
namespace Buttplug.Core
{
public static class ButtplugConsts
{
public const uint SystemMsgId = 0u;
public const uint DefaultMsgId = 1u;
public const uint CurrentSpecVersion = 3u;
}
public class ButtplugDeviceException : ButtplugException
{
public ButtplugDeviceException(string message, uint id = 0u, Exception inner = null)
: base(message, Error.ErrorClass.ERROR_DEVICE, id, inner)
{
}
}
public class ButtplugException : Exception
{
public Error ButtplugErrorMessage { get; }
public static ButtplugException FromError(Error msg)
{
return msg.ErrorCode switch
{
Error.ErrorClass.ERROR_DEVICE => new ButtplugDeviceException(msg.ErrorMessage, msg.Id),
Error.ErrorClass.ERROR_INIT => new ButtplugHandshakeException(msg.ErrorMessage, msg.Id),
Error.ErrorClass.ERROR_MSG => new ButtplugMessageException(msg.ErrorMessage, msg.Id),
Error.ErrorClass.ERROR_PING => new ButtplugPingException(msg.ErrorMessage, msg.Id),
Error.ErrorClass.ERROR_UNKNOWN => new ButtplugException(msg.ErrorMessage, msg.Id),
_ => new ButtplugException(msg.ErrorMessage, msg.Id),
};
}
public ButtplugException(string message, uint id = 0u, Exception inner = null)
: this(message, Error.ErrorClass.ERROR_UNKNOWN, id, inner)
{
}
public ButtplugException(string message, Error.ErrorClass err = Error.ErrorClass.ERROR_UNKNOWN, uint id = 0u, Exception inner = null)
: base(message, inner)
{
ButtplugErrorMessage = new Error(message, err, id);
}
}
public class ButtplugExceptionEventArgs : EventArgs
{
public ButtplugException Exception { get; }
public ButtplugExceptionEventArgs(ButtplugException ex)
{
Exception = ex;
}
}
public class ButtplugHandshakeException : ButtplugException
{
public ButtplugHandshakeException(string message, uint id = 0u, Exception inner = null)
: base(message, Error.ErrorClass.ERROR_INIT, id, inner)
{
}
}
public class ButtplugJsonMessageParser
{
private readonly Dictionary<string, Type> _messageTypes;
private readonly JsonSerializer _serializer;
public ButtplugJsonMessageParser()
{
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Expected O, but got Unknown
_serializer = new JsonSerializer
{
MissingMemberHandling = (MissingMemberHandling)1
};
_messageTypes = new Dictionary<string, Type>();
foreach (Type allMessageType in ButtplugUtils.GetAllMessageTypes())
{
_messageTypes.Add(allMessageType.Name, allMessageType);
}
if (!_messageTypes.Any())
{
throw new ButtplugMessageException("No message types available.");
}
}
public IEnumerable<ButtplugMessage> Deserialize(string jsonMsg)
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_001a: Expected O, but got Unknown
//IL_0031: Expected O, but got Unknown
//IL_005b: Expected O, but got Unknown
//IL_007b: Unknown result type (might be due to invalid IL or missing references)
//IL_0080: Unknown result type (might be due to invalid IL or missing references)
JsonTextReader val = new JsonTextReader((TextReader)new StringReader(jsonMsg))
{
CloseInput = false,
SupportMultipleContent = true
};
List<ButtplugMessage> list = new List<ButtplugMessage>();
while (true)
{
try
{
if (!((JsonReader)val).Read())
{
return list;
}
}
catch (JsonReaderException val2)
{
JsonReaderException val3 = val2;
throw new ButtplugMessageException("Not valid JSON: " + jsonMsg + " - " + ((Exception)(object)val3).Message);
}
JArray val4;
try
{
val4 = JArray.Load((JsonReader)(object)val);
}
catch (JsonReaderException val5)
{
JsonReaderException val6 = val5;
throw new ButtplugMessageException("Not valid JSON: " + jsonMsg + " - " + ((Exception)(object)val6).Message);
}
foreach (JObject item in ((JToken)val4).Children<JObject>())
{
string name = item.Properties().First().Name;
if (!_messageTypes.ContainsKey(name))
{
throw new ButtplugMessageException(name + " is not a valid message class");
}
list.Add(DeserializeAs(item, _messageTypes[name]));
}
}
}
private ButtplugMessage DeserializeAs(JObject obj, Type msgType)
{
//IL_00c0: Expected O, but got Unknown
if (!msgType.IsSubclassOf(typeof(ButtplugMessage)))
{
throw new ButtplugMessageException("Type " + msgType.Name + " is not a subclass of ButtplugMessage");
}
if (msgType.Namespace != "Buttplug.Core.Messages")
{
throw new ButtplugMessageException("Type " + msgType.Name + " (" + msgType.Namespace + ") is not in the namespace of Buttplug.Core.Messages");
}
string name = ButtplugMessage.GetName(msgType);
try
{
return (ButtplugMessage)((JToken)Extensions.Value<JObject>((IEnumerable<JToken>)obj[name])).ToObject(msgType, _serializer);
}
catch (InvalidCastException ex)
{
throw new ButtplugMessageException($"Could not create message for JSON {obj}: {ex.Message}");
}
catch (JsonSerializationException val)
{
JsonSerializationException val2 = val;
throw new ButtplugMessageException($"Could not create message for JSON {obj}: {((Exception)(object)val2).Message}");
}
}
public string Serialize(ButtplugMessage msg)
{
//IL_007b: Unknown result type (might be due to invalid IL or missing references)
//IL_0080: Unknown result type (might be due to invalid IL or missing references)
if (msg.GetType().Namespace != "Buttplug.Core.Messages")
{
throw new ButtplugMessageException("Type " + msg.GetType().Name + " (" + msg.GetType().Namespace + ") is not in the namespace of Buttplug.Core.Messages");
}
JObject val = ButtplugMessageToJObject(msg);
if (val == null)
{
throw new ButtplugMessageException("Message cannot be converted to JSON.", msg.Id);
}
JArray val2 = new JArray();
val2.Add((JToken)(object)val);
return ((JToken)val2).ToString((Formatting)0, Array.Empty<JsonConverter>());
}
public string Serialize(IEnumerable<ButtplugMessage> msgs)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Expected O, but got Unknown
JArray val = new JArray();
foreach (ButtplugMessage msg in msgs)
{
JObject val2 = ButtplugMessageToJObject(msg);
if (val2 != null)
{
val.Add((JToken)(object)val2);
}
}
if (!((IEnumerable<JToken>)val).Any())
{
throw new ButtplugMessageException("No messages serialized.");
}
return ((JToken)val).ToString((Formatting)0, Array.Empty<JsonConverter>());
}
private JObject ButtplugMessageToJObject(ButtplugMessage msg)
{
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Expected O, but got Unknown
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: Expected O, but got Unknown
return new JObject((object)new JProperty(msg.Name, (object)JObject.FromObject((object)msg)));
}
}
public class ButtplugMessageException : ButtplugException
{
public ButtplugMessageException(string message, uint id = 0u, Exception inner = null)
: base(message, Error.ErrorClass.ERROR_MSG, id, inner)
{
}
}
public class ButtplugPingException : ButtplugException
{
public ButtplugPingException(string message, uint id = 0u, Exception inner = null)
: base(message, Error.ErrorClass.ERROR_PING, id, inner)
{
}
}
public static class ButtplugUtils
{
public static IEnumerable<Type> GetAllMessageTypes()
{
IEnumerable<Type> enumerable;
try
{
enumerable = Assembly.GetAssembly(typeof(ButtplugMessage))?.GetTypes();
}
catch (ReflectionTypeLoadException ex)
{
enumerable = ex.Types;
}
return (enumerable ?? throw new InvalidOperationException()).Where((Type type) => type != null && type.IsClass && type.IsSubclassOf(typeof(ButtplugMessage)) && type != typeof(ButtplugDeviceMessage));
}
[DebuggerStepThrough]
public static void ArgumentNotNull(object argument, string argumentName)
{
if (argument == null)
{
throw new ArgumentNullException(argumentName);
}
}
public static Type GetMessageType(string messageName)
{
return Type.GetType("Buttplug.Core.Messages." + messageName);
}
}
}
namespace Buttplug.Core.Messages
{
public class ButtplugDeviceMessage : ButtplugMessage
{
[JsonProperty(/*Could not decode attribute arguments.*/)]
public uint DeviceIndex { get; set; }
public ButtplugDeviceMessage(uint id = 1u, uint deviceIndex = uint.MaxValue)
: base(id)
{
DeviceIndex = deviceIndex;
}
}
public abstract class ButtplugMessage
{
private static readonly Dictionary<Type, ButtplugMessageMetadata> _metadataCache = new Dictionary<Type, ButtplugMessageMetadata>();
[JsonProperty(/*Could not decode attribute arguments.*/)]
public uint Id { get; set; }
[JsonIgnore]
public string Name => GetName(GetType());
protected ButtplugMessage(uint id)
{
Id = id;
}
private static T GetMessageAttribute<T>(Type msgType, Func<ButtplugMessageMetadata, T> func)
{
ButtplugUtils.ArgumentNotNull(msgType, "msgType");
ButtplugUtils.ArgumentNotNull(func, "func");
if (!msgType.IsSubclassOf(typeof(ButtplugMessage)))
{
throw new ArgumentException("Argument " + msgType.Name + " must be a subclass of ButtplugMessage");
}
if (_metadataCache.ContainsKey(msgType))
{
return func(_metadataCache[msgType]);
}
Attribute[] customAttributes = Attribute.GetCustomAttributes(msgType);
for (int i = 0; i < customAttributes.Length; i++)
{
if (customAttributes[i] is ButtplugMessageMetadata buttplugMessageMetadata)
{
_metadataCache[msgType] = buttplugMessageMetadata;
return func(buttplugMessageMetadata);
}
}
throw new ArgumentException($"Type {msgType} does not have ButtplugMessageMetadata Attributes");
}
public static string GetName(Type msgType)
{
return GetMessageAttribute(msgType, (ButtplugMessageMetadata md) => md.Name);
}
}
public interface IButtplugMessageOutgoingOnly
{
}
public interface IButtplugDeviceInfoMessage
{
string DeviceName { get; }
uint DeviceIndex { get; }
DeviceMessageAttributes DeviceMessages { get; }
string DeviceDisplayName { get; }
uint DeviceMessageTimingGap { get; }
}
[AttributeUsage(AttributeTargets.Class)]
public class ButtplugMessageMetadata : Attribute
{
public string Name { get; }
public ButtplugMessageMetadata(string name)
{
Name = name;
}
}
[JsonConverter(typeof(StringEnumConverter))]
public enum ActuatorType
{
[EnumMember(Value = "Unknown")]
Unknown,
[EnumMember(Value = "Vibrate")]
Vibrate,
[EnumMember(Value = "Rotate")]
Rotate,
[EnumMember(Value = "Oscillate")]
Oscillate,
[EnumMember(Value = "Constrict")]
Constrict,
[EnumMember(Value = "Inflate")]
Inflate,
[EnumMember(Value = "Position")]
Position
}
[JsonConverter(typeof(StringEnumConverter))]
public enum SensorType
{
[EnumMember(Value = "Unknown")]
Unknown,
[EnumMember(Value = "Battery")]
Battery,
[EnumMember(Value = "RSSI")]
RSSI,
[EnumMember(Value = "Button")]
Button,
[EnumMember(Value = "Pressure")]
Pressure
}
public class GenericDeviceMessageAttributes
{
[JsonIgnore]
internal uint _index;
[JsonProperty(/*Could not decode attribute arguments.*/)]
public readonly string FeatureDescriptor;
[JsonProperty(/*Could not decode attribute arguments.*/)]
[JsonConverter(typeof(StringEnumConverter))]
public readonly ActuatorType ActuatorType;
[JsonProperty(/*Could not decode attribute arguments.*/)]
public readonly uint StepCount;
[JsonIgnore]
public uint Index => _index;
}
public class SensorDeviceMessageAttributes
{
[JsonIgnore]
internal uint _index;
[JsonProperty(/*Could not decode attribute arguments.*/)]
public readonly string FeatureDescriptor;
[JsonProperty(/*Could not decode attribute arguments.*/)]
[JsonConverter(typeof(StringEnumConverter))]
public readonly SensorType SensorType;
[JsonProperty(/*Could not decode attribute arguments.*/)]
public readonly uint[][] SensorRange;
[JsonIgnore]
public uint Index => _index;
}
public class RawDeviceMessageAttributes
{
public readonly string[] Endpoints;
}
public class NullDeviceMessageAttributes
{
}
public class DeviceMessageAttributes
{
public GenericDeviceMessageAttributes[] ScalarCmd;
public GenericDeviceMessageAttributes[] RotateCmd;
public GenericDeviceMessageAttributes[] LinearCmd;
public SensorDeviceMessageAttributes[] SensorReadCmd;
public SensorDeviceMessageAttributes[] SensorSubscribeCmd;
public readonly RawDeviceMessageAttributes[] RawReadCmd;
public readonly RawDeviceMessageAttributes[] RawWriteCmd;
public readonly RawDeviceMessageAttributes[] RawSubscribeCmd;
public readonly NullDeviceMessageAttributes StopDeviceCmd;
[OnDeserialized]
internal void OnDeserializedMethod(StreamingContext context)
{
ScalarCmd?.Select((GenericDeviceMessageAttributes x, int i) => (x, i)).ToList().ForEach(delegate((GenericDeviceMessageAttributes x, int i) x)
{
x.x._index = (uint)x.i;
});
RotateCmd?.Select((GenericDeviceMessageAttributes x, int i) => (x, i)).ToList().ForEach(delegate((GenericDeviceMessageAttributes x, int i) x)
{
x.x._index = (uint)x.i;
});
LinearCmd?.Select((GenericDeviceMessageAttributes x, int i) => (x, i)).ToList().ForEach(delegate((GenericDeviceMessageAttributes x, int i) x)
{
x.x._index = (uint)x.i;
});
SensorReadCmd?.Select((SensorDeviceMessageAttributes x, int i) => (x, i)).ToList().ForEach(delegate((SensorDeviceMessageAttributes x, int i) x)
{
x.x._index = (uint)x.i;
});
SensorSubscribeCmd?.Select((SensorDeviceMessageAttributes x, int i) => (x, i)).ToList().ForEach(delegate((SensorDeviceMessageAttributes x, int i) x)
{
x.x._index = (uint)x.i;
});
}
}
public class MessageReceivedEventArgs : EventArgs
{
public ButtplugMessage Message { get; }
public MessageReceivedEventArgs(ButtplugMessage message)
{
Message = message;
}
}
[ButtplugMessageMetadata("Ok")]
public class Ok : ButtplugMessage, IButtplugMessageOutgoingOnly
{
public Ok(uint id)
: base(id)
{
}
}
[ButtplugMessageMetadata("Test")]
public class Test : ButtplugMessage
{
private string _testStringImpl;
[JsonProperty(/*Could not decode attribute arguments.*/)]
public string TestString
{
get
{
return _testStringImpl;
}
set
{
if (value == "Error")
{
throw new ArgumentException("Got an Error Message");
}
_testStringImpl = value;
}
}
public Test(string str, uint id = 1u)
: base(id)
{
TestString = str;
}
}
[ButtplugMessageMetadata("Error")]
public class Error : ButtplugMessage, IButtplugMessageOutgoingOnly
{
public enum ErrorClass
{
ERROR_UNKNOWN,
ERROR_INIT,
ERROR_PING,
ERROR_MSG,
ERROR_DEVICE
}
[JsonProperty(/*Could not decode attribute arguments.*/)]
public ErrorClass ErrorCode;
[JsonProperty(/*Could not decode attribute arguments.*/)]
public string ErrorMessage;
public Error(string errorMessage, ErrorClass errorCode, uint id)
: base(id)
{
ErrorMessage = errorMessage;
ErrorCode = errorCode;
}
}
public class MessageAttributes : IEquatable<MessageAttributes>
{
[JsonProperty(/*Could not decode attribute arguments.*/)]
public uint? FeatureCount;
public MessageAttributes()
{
}
public MessageAttributes(uint featureCount)
{
FeatureCount = featureCount;
}
public bool Equals(MessageAttributes attrs)
{
return FeatureCount == attrs.FeatureCount;
}
}
public class DeviceMessageInfo : IButtplugDeviceInfoMessage
{
[JsonProperty(/*Could not decode attribute arguments.*/)]
public readonly string DeviceName;
[JsonProperty(/*Could not decode attribute arguments.*/)]
public readonly uint DeviceIndex;
public readonly string DeviceDisplayName;
[JsonProperty(/*Could not decode attribute arguments.*/)]
public readonly uint DeviceMessageTimingGap;
[JsonProperty(/*Could not decode attribute arguments.*/)]
public readonly DeviceMessageAttributes DeviceMessages;
string IButtplugDeviceInfoMessage.DeviceName => DeviceName;
uint IButtplugDeviceInfoMessage.DeviceIndex => DeviceIndex;
DeviceMessageAttributes IButtplugDeviceInfoMessage.DeviceMessages => DeviceMessages;
string IButtplugDeviceInfoMessage.DeviceDisplayName => DeviceDisplayName;
uint IButtplugDeviceInfoMessage.DeviceMessageTimingGap => DeviceMessageTimingGap;
public DeviceMessageInfo(uint index, string name, DeviceMessageAttributes messages)
{
DeviceName = name;
DeviceIndex = index;
DeviceMessages = messages;
}
}
[ButtplugMessageMetadata("DeviceList")]
public class DeviceList : ButtplugMessage, IButtplugMessageOutgoingOnly
{
[JsonProperty(/*Could not decode attribute arguments.*/)]
public readonly DeviceMessageInfo[] Devices = new DeviceMessageInfo[0];
public DeviceList(DeviceMessageInfo[] deviceList, uint id)
: base(id)
{
Devices = deviceList;
}
internal DeviceList()
: base(0u)
{
}
}
[ButtplugMessageMetadata("DeviceAdded")]
public class DeviceAdded : ButtplugDeviceMessage, IButtplugMessageOutgoingOnly, IButtplugDeviceInfoMessage
{
[JsonProperty(/*Could not decode attribute arguments.*/)]
public string DeviceName;
public readonly string DeviceDisplayName;
[JsonProperty(/*Could not decode attribute arguments.*/)]
public readonly uint DeviceMessageTimingGap;
[JsonProperty(/*Could not decode attribute arguments.*/)]
public readonly DeviceMessageAttributes DeviceMessages;
string IButtplugDeviceInfoMessage.DeviceName => DeviceName;
uint IButtplugDeviceInfoMessage.DeviceIndex => base.DeviceIndex;
DeviceMessageAttributes IButtplugDeviceInfoMessage.DeviceMessages => DeviceMessages;
string IButtplugDeviceInfoMessage.DeviceDisplayName => DeviceDisplayName;
uint IButtplugDeviceInfoMessage.DeviceMessageTimingGap => DeviceMessageTimingGap;
public DeviceAdded(uint index, string name, DeviceMessageAttributes messages)
: base(0u, index)
{
DeviceName = name;
DeviceMessages = messages;
}
internal DeviceAdded()
: base(0u)
{
}
}
[ButtplugMessageMetadata("DeviceRemoved")]
public class DeviceRemoved : ButtplugMessage, IButtplugMessageOutgoingOnly
{
[JsonProperty(/*Could not decode attribute arguments.*/)]
public readonly uint DeviceIndex;
public DeviceRemoved(uint index)
: base(0u)
{
DeviceIndex = index;
}
}
[ButtplugMessageMetadata("RequestDeviceList")]
public class RequestDeviceList : ButtplugMessage
{
public RequestDeviceList(uint id = 1u)
: base(id)
{
}
}
[ButtplugMessageMetadata("StartScanning")]
public class StartScanning : ButtplugMessage
{
public StartScanning(uint id = 1u)
: base(id)
{
}
}
[ButtplugMessageMetadata("StopScanning")]
public class StopScanning : ButtplugMessage
{
public StopScanning(uint id = 1u)
: base(id)
{
}
}
[ButtplugMessageMetadata("ScanningFinished")]
public class ScanningFinished : ButtplugMessage, IButtplugMessageOutgoingOnly
{
public ScanningFinished()
: base(0u)
{
}
}
[ButtplugMessageMetadata("RequestServerInfo")]
public class RequestServerInfo : ButtplugMessage
{
[JsonProperty(/*Could not decode attribute arguments.*/)]
public string ClientName;
[JsonProperty(/*Could not decode attribute arguments.*/)]
public uint MessageVersion;
public RequestServerInfo(string clientName, uint id = 1u, uint schemversion = 3u)
: base(id)
{
ClientName = clientName;
MessageVersion = schemversion;
}
}
[ButtplugMessageMetadata("ServerInfo")]
public class ServerInfo : ButtplugMessage, IButtplugMessageOutgoingOnly
{
[JsonProperty(/*Could not decode attribute arguments.*/)]
public uint MessageVersion;
[JsonProperty(/*Could not decode attribute arguments.*/)]
public uint MaxPingTime;
[JsonProperty(/*Could not decode attribute arguments.*/)]
public string ServerName;
public ServerInfo(string serverName, uint messageVersion, uint maxPingTime, uint id = 1u)
: base(id)
{
ServerName = serverName;
MessageVersion = messageVersion;
MaxPingTime = maxPingTime;
}
}
[ButtplugMessageMetadata("Ping")]
public class Ping : ButtplugMessage
{
public Ping(uint id = 1u)
: base(id)
{
}
}
public class GenericMessageSubcommand
{
[JsonProperty(/*Could not decode attribute arguments.*/)]
public uint Index;
protected GenericMessageSubcommand(uint index)
{
Index = index;
}
}
[ButtplugMessageMetadata("ScalarCmd")]
public class ScalarCmd : ButtplugDeviceMessage
{
public class ScalarSubcommand : GenericMessageSubcommand
{
private double _scalarImpl;
public readonly ActuatorType ActuatorType;
[JsonProperty(/*Could not decode attribute arguments.*/)]
public double Scalar
{
get
{
return _scalarImpl;
}
set
{
if (value < 0.0)
{
throw new ArgumentException("ScalarCmd value cannot be less than 0!");
}
if (value > 1.0)
{
throw new ArgumentException("ScalarCmd value cannot be greater than 1!");
}
_scalarImpl = value;
}
}
public ScalarSubcommand(uint index, double scalar, ActuatorType actuatorType)
: base(index)
{
Scalar = scalar;
ActuatorType = actuatorType;
}
}
[JsonProperty(/*Could not decode attribute arguments.*/)]
public List<ScalarSubcommand> Scalars;
[JsonConstructor]
public ScalarCmd(uint deviceIndex, List<ScalarSubcommand> scalars, uint id = 1u)
: base(id, deviceIndex)
{
Scalars = scalars;
}
public ScalarCmd(List<ScalarSubcommand> scalars)
: this(uint.MaxValue, scalars)
{
}
}
[ButtplugMessageMetadata("RotateCmd")]
public class RotateCmd : ButtplugDeviceMessage
{
public class RotateSubcommand : GenericMessageSubcommand
{
private double _speedImpl;
[JsonProperty(/*Could not decode attribute arguments.*/)]
public bool Clockwise;
[JsonProperty(/*Could not decode attribute arguments.*/)]
public double Speed
{
get
{
return _speedImpl;
}
set
{
if (value < 0.0)
{
throw new ArgumentException("RotateCmd Speed cannot be less than 0!");
}
if (value > 1.0)
{
throw new ArgumentException("RotateCmd Speed cannot be greater than 1!");
}
_speedImpl = value;
}
}
public RotateSubcommand(uint index, double speed, bool clockwise)
: base(index)
{
Speed = speed;
Clockwise = clockwise;
}
}
[JsonProperty(/*Could not decode attribute arguments.*/)]
public List<RotateSubcommand> Rotations;
public static RotateCmd Create(double speed, bool clockwise, uint cmdCount)
{
return Create(uint.MaxValue, 1u, Enumerable.Repeat((speed, clockwise), (int)cmdCount));
}
public static RotateCmd Create(IEnumerable<(double speed, bool clockwise)> cmds)
{
return Create(uint.MaxValue, 1u, cmds);
}
public static RotateCmd Create(uint deviceIndex, uint msgId, double speed, bool clockwise, uint cmdCount)
{
return Create(deviceIndex, msgId, Enumerable.Repeat((speed, clockwise), (int)cmdCount));
}
public static RotateCmd Create(uint deviceIndex, uint msgId, IEnumerable<(double speed, bool clockwise)> cmds)
{
List<RotateSubcommand> list = new List<RotateSubcommand>(cmds.Count());
uint num = 0u;
foreach (var (speed, clockwise) in cmds)
{
list.Add(new RotateSubcommand(num, speed, clockwise));
num++;
}
return new RotateCmd(deviceIndex, list, msgId);
}
[JsonConstructor]
public RotateCmd(uint deviceIndex, List<RotateSubcommand> rotations, uint id = 1u)
: base(id, deviceIndex)
{
Rotations = rotations;
}
public RotateCmd(List<RotateSubcommand> rotations)
: this(uint.MaxValue, rotations)
{
}
}
[ButtplugMessageMetadata("LinearCmd")]
public class LinearCmd : ButtplugDeviceMessage
{
public class VectorSubcommand : GenericMessageSubcommand
{
private double _positionImpl;
[JsonProperty(/*Could not decode attribute arguments.*/)]
public uint Duration;
[JsonProperty(/*Could not decode attribute arguments.*/)]
public double Position
{
get
{
return _positionImpl;
}
set
{
if (value < 0.0)
{
throw new ArgumentException("LinearCmd Speed cannot be less than 0!");
}
if (value > 1.0)
{
throw new ArgumentException("LinearCmd Speed cannot be greater than 1!");
}
_positionImpl = value;
}
}
public VectorSubcommand(uint index, uint duration, double position)
: base(index)
{
Duration = duration;
Position = position;
}
}
[JsonProperty(/*Could not decode attribute arguments.*/)]
public List<VectorSubcommand> Vectors;
public static LinearCmd Create(uint duration, double position, uint cmdCount)
{
return Create(uint.MaxValue, 1u, Enumerable.Repeat((duration, position), (int)cmdCount));
}
public static LinearCmd Create(uint deviceIndex, uint msgId, uint duration, double position, uint cmdCount)
{
return Create(deviceIndex, msgId, Enumerable.Repeat((duration, position), (int)cmdCount));
}
public static LinearCmd Create(IEnumerable<(uint duration, double position)> cmds)
{
return Create(uint.MaxValue, 1u, cmds);
}
public static LinearCmd Create(uint deviceIndex, uint msgId, IEnumerable<(uint duration, double position)> cmds)
{
List<VectorSubcommand> list = new List<VectorSubcommand>(cmds.Count());
uint num = 0u;
foreach (var (duration, position) in cmds)
{
list.Add(new VectorSubcommand(num, duration, position));
num++;
}
return new LinearCmd(deviceIndex, list, msgId);
}
[JsonConstructor]
public LinearCmd(uint deviceIndex, List<VectorSubcommand> vectors, uint id = 1u)
: base(id, deviceIndex)
{
Vectors = vectors;
}
public LinearCmd(List<VectorSubcommand> vectors)
: this(uint.MaxValue, vectors)
{
}
}
[ButtplugMessageMetadata("StopDeviceCmd")]
public class StopDeviceCmd : ButtplugDeviceMessage
{
public StopDeviceCmd(uint deviceIndex = uint.MaxValue, uint id = 1u)
: base(id, deviceIndex)
{
}
}
[ButtplugMessageMetadata("StopAllDevices")]
public class StopAllDevices : ButtplugMessage
{
public StopAllDevices(uint id = 1u)
: base(id)
{
}
}
[ButtplugMessageMetadata("SensorReadCmd")]
public class SensorReadCmd : ButtplugDeviceMessage
{
[JsonProperty(/*Could not decode attribute arguments.*/)]
public uint SensorIndex;
[JsonProperty(/*Could not decode attribute arguments.*/)]
public SensorType SensorType;
[JsonConstructor]
public SensorReadCmd(uint deviceIndex, uint sensorIndex, SensorType sensorType, uint id = 1u)
: base(id, deviceIndex)
{
SensorIndex = sensorIndex;
SensorType = sensorType;
}
public SensorReadCmd(uint sensorIndex, SensorType sensorType)
: this(uint.MaxValue, sensorIndex, sensorType)
{
}
}
[ButtplugMessageMetadata("SensorReading")]
public class SensorReading : ButtplugDeviceMessage
{
[JsonProperty(/*Could not decode attribute arguments.*/)]
public readonly uint SensorIndex;
[JsonProperty(/*Could not decode attribute arguments.*/)]
public readonly SensorType SensorType;
[JsonProperty(/*Could not decode attribute arguments.*/)]
public readonly List<int> data;
}
}
namespace Buttplug.Client
{
public class ButtplugClient : IDisposable
{
protected Timer _pingTimer;
internal ButtplugClientMessageHandler _handler;
private readonly ConcurrentDictionary<uint, ButtplugClientDevice> _devices = new ConcurrentDictionary<uint, ButtplugClientDevice>();
private IButtplugClientConnector _connector;
public string Name { get; }
public ButtplugClientDevice[] Devices => _devices.Values.ToArray();
public bool Connected => _connector?.Connected ?? false;
public event EventHandler<DeviceAddedEventArgs> DeviceAdded;
public event EventHandler<DeviceRemovedEventArgs> DeviceRemoved;
public event EventHandler<ButtplugExceptionEventArgs> ErrorReceived;
public event EventHandler ScanningFinished;
public event EventHandler PingTimeout;
public event EventHandler ServerDisconnect;
public ButtplugClient(string clientName)
{
Name = clientName;
}
public async Task ConnectAsync(IButtplugClientConnector connector, CancellationToken token = default(CancellationToken))
{
if (Connected)
{
throw new ButtplugHandshakeException("Client already connected to a server.");
}
ButtplugUtils.ArgumentNotNull(connector, "connector");
_connector = connector;
_connector.Disconnected += delegate(object obj, EventArgs eventArgs)
{
this.ServerDisconnect?.Invoke(obj, eventArgs);
};
_connector.InvalidMessageReceived += ConnectorErrorHandler;
_connector.MessageReceived += MessageReceivedHandler;
_devices.Clear();
_handler = new ButtplugClientMessageHandler(connector);
await _connector.ConnectAsync(token).ConfigureAwait(continueOnCapturedContext: false);
ButtplugMessage res = await _handler.SendMessageAsync(new RequestServerInfo(Name), token).ConfigureAwait(continueOnCapturedContext: false);
if (!(res is ServerInfo si))
{
if (res is Error e)
{
await DisconnectAsync().ConfigureAwait(continueOnCapturedContext: false);
throw ButtplugException.FromError(e);
}
await DisconnectAsync().ConfigureAwait(continueOnCapturedContext: false);
throw new ButtplugHandshakeException("Unrecognized message " + res.Name + " during handshake", res.Id);
}
if (si.MaxPingTime != 0)
{
_pingTimer?.Dispose();
_pingTimer = new Timer(OnPingTimer, null, 0, Convert.ToInt32(Math.Round((double)si.MaxPingTime / 2.0, 0)));
}
if (si.MessageVersion < 3)
{
await DisconnectAsync().ConfigureAwait(continueOnCapturedContext: false);
throw new ButtplugHandshakeException($"Buttplug Server's schema version ({si.MessageVersion}) is less than the client's ({3u}). A newer server is required.", res.Id);
}
ButtplugMessage resp = await _handler.SendMessageAsync(new RequestDeviceList()).ConfigureAwait(continueOnCapturedContext: false);
if (resp is DeviceList deviceList)
{
DeviceMessageInfo[] devices = deviceList.Devices;
foreach (DeviceMessageInfo deviceMessageInfo in devices)
{
if (!_devices.ContainsKey(deviceMessageInfo.DeviceIndex))
{
ButtplugClientDevice buttplugClientDevice = new ButtplugClientDevice(_handler, deviceMessageInfo);
_devices[deviceMessageInfo.DeviceIndex] = buttplugClientDevice;
this.DeviceAdded?.Invoke(this, new DeviceAddedEventArgs(buttplugClientDevice));
}
}
return;
}
await DisconnectAsync().ConfigureAwait(continueOnCapturedContext: false);
if (resp is Error msg)
{
throw ButtplugException.FromError(msg);
}
throw new ButtplugHandshakeException("Received unknown response to DeviceList handshake query");
}
public async Task DisconnectAsync()
{
if (Connected)
{
_connector.MessageReceived -= MessageReceivedHandler;
await _connector.DisconnectAsync().ConfigureAwait(continueOnCapturedContext: false);
this.ServerDisconnect?.Invoke(this, EventArgs.Empty);
}
}
public async Task StartScanningAsync(CancellationToken token = default(CancellationToken))
{
await _handler.SendMessageExpectOk(new StartScanning(), token).ConfigureAwait(continueOnCapturedContext: false);
}
public async Task StopScanningAsync(CancellationToken token = default(CancellationToken))
{
await _handler.SendMessageExpectOk(new StopScanning(), token).ConfigureAwait(continueOnCapturedContext: false);
}
public async Task StopAllDevicesAsync(CancellationToken token = default(CancellationToken))
{
await _handler.SendMessageExpectOk(new StopAllDevices(), token).ConfigureAwait(continueOnCapturedContext: false);
}
private void ConnectorErrorHandler(object sender, ButtplugExceptionEventArgs exception)
{
this.ErrorReceived?.Invoke(this, exception);
}
private async void MessageReceivedHandler(object sender, MessageReceivedEventArgs args)
{
ButtplugMessage message = args.Message;
if (!(message is DeviceAdded deviceAdded))
{
ButtplugClientDevice value;
if (!(message is DeviceRemoved deviceRemoved))
{
if (!(message is ScanningFinished))
{
if (message is Error error)
{
this.ErrorReceived?.Invoke(this, new ButtplugExceptionEventArgs(ButtplugException.FromError(error)));
if (error.ErrorCode == Error.ErrorClass.ERROR_PING)
{
this.PingTimeout?.Invoke(this, EventArgs.Empty);
await DisconnectAsync().ConfigureAwait(continueOnCapturedContext: false);
}
}
else
{
this.ErrorReceived?.Invoke(this, new ButtplugExceptionEventArgs(new ButtplugMessageException($"Got unhandled message: {message}", message.Id)));
}
}
else
{
this.ScanningFinished?.Invoke(this, EventArgs.Empty);
}
}
else if (!_devices.ContainsKey(deviceRemoved.DeviceIndex))
{
this.ErrorReceived?.Invoke(this, new ButtplugExceptionEventArgs(new ButtplugDeviceException("Got device removed message for unknown device.", message.Id)));
}
else if (_devices.TryRemove(deviceRemoved.DeviceIndex, out value))
{
this.DeviceRemoved?.Invoke(this, new DeviceRemovedEventArgs(value));
}
}
else
{
ButtplugClientDevice dev = new ButtplugClientDevice(_handler, deviceAdded);
_devices.AddOrUpdate(deviceAdded.DeviceIndex, dev, (uint u, ButtplugClientDevice device) => dev);
this.DeviceAdded?.Invoke(this, new DeviceAddedEventArgs(dev));
}
}
private async void OnPingTimer(object state)
{
try
{
await _handler.SendMessageExpectOk(new Ping()).ConfigureAwait(continueOnCapturedContext: false);
}
catch (Exception inner)
{
this.ErrorReceived?.Invoke(this, new ButtplugExceptionEventArgs(new ButtplugPingException("Exception thrown during ping update", 0u, inner)));
await DisconnectAsync().ConfigureAwait(continueOnCapturedContext: false);
}
}
protected virtual void Dispose(bool disposing)
{
DisconnectAsync().GetAwaiter().GetResult();
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
public class ButtplugClientConnectorException : ButtplugException
{
public ButtplugClientConnectorException(string message, Exception inner = null)
: base(message, Error.ErrorClass.ERROR_UNKNOWN, 0u, inner)
{
}
}
public class ButtplugClientDevice
{
private readonly ButtplugClientMessageHandler _handler;
public uint Index { get; }
public string Name { get; }
public string DisplayName { get; }
public uint MessageTimingGap { get; }
public DeviceMessageAttributes MessageAttributes { get; }
public List<GenericDeviceMessageAttributes> VibrateAttributes => GenericAcutatorAttributes(ActuatorType.Vibrate);
public List<GenericDeviceMessageAttributes> OscillateAttributes => GenericAcutatorAttributes(ActuatorType.Oscillate);
public List<GenericDeviceMessageAttributes> RotateAttributes
{
get
{
if (MessageAttributes.RotateCmd != null)
{
return MessageAttributes.RotateCmd.ToList();
}
return Enumerable.Empty<GenericDeviceMessageAttributes>().ToList();
}
}
public List<GenericDeviceMessageAttributes> LinearAttributes
{
get
{
if (MessageAttributes.LinearCmd != null)
{
return MessageAttributes.LinearCmd.ToList();
}
return Enumerable.Empty<GenericDeviceMessageAttributes>().ToList();
}
}
public bool HasBattery => SensorReadAttributes(SensorType.Battery).Any();
internal ButtplugClientDevice(ButtplugClientMessageHandler handler, IButtplugDeviceInfoMessage devInfo)
: this(handler, devInfo.DeviceIndex, devInfo.DeviceName, devInfo.DeviceMessages, devInfo.DeviceDisplayName, devInfo.DeviceMessageTimingGap)
{
ButtplugUtils.ArgumentNotNull(devInfo, "devInfo");
}
internal ButtplugClientDevice(ButtplugClientMessageHandler handler, uint index, string name, DeviceMessageAttributes messages, string displayName, uint messageTimingGap)
{
ButtplugUtils.ArgumentNotNull(handler, "handler");
_handler = handler;
Index = index;
Name = name;
MessageAttributes = messages;
DisplayName = displayName;
MessageTimingGap = messageTimingGap;
}
public List<GenericDeviceMessageAttributes> GenericAcutatorAttributes(ActuatorType actuator)
{
if (MessageAttributes.ScalarCmd != null)
{
return MessageAttributes.ScalarCmd.Where((GenericDeviceMessageAttributes x) => x.ActuatorType == actuator).ToList();
}
return Enumerable.Empty<GenericDeviceMessageAttributes>().ToList();
}
public async Task ScalarAsync(ScalarCmd.ScalarSubcommand command)
{
List<ScalarCmd.ScalarSubcommand> scalars = new List<ScalarCmd.ScalarSubcommand>();
GenericAcutatorAttributes(command.ActuatorType).ForEach(delegate(GenericDeviceMessageAttributes x)
{
scalars.Add(new ScalarCmd.ScalarSubcommand(x.Index, command.Scalar, command.ActuatorType));
});
if (!scalars.Any())
{
throw new ButtplugDeviceException("Scalar command for device " + Name + " did not generate any commands. Are you sure the device supports the ActuatorType sent?");
}
await _handler.SendMessageExpectOk(new ScalarCmd(Index, scalars)).ConfigureAwait(continueOnCapturedContext: false);
}
public async Task ScalarAsync(List<ScalarCmd.ScalarSubcommand> command)
{
if (!command.Any())
{
throw new ArgumentException("Command List for ScalarAsync must have at least 1 command.");
}
await _handler.SendMessageExpectOk(new ScalarCmd(Index, command)).ConfigureAwait(continueOnCapturedContext: false);
}
public async Task VibrateAsync(double speed)
{
await ScalarAsync(new ScalarCmd.ScalarSubcommand(uint.MaxValue, speed, ActuatorType.Vibrate));
}
public async Task VibrateAsync(IEnumerable<double> cmds)
{
List<GenericDeviceMessageAttributes> vibrateAttributes = VibrateAttributes;
if (cmds.Count() > vibrateAttributes.Count())
{
throw new ButtplugDeviceException($"Device {Name} only has {vibrateAttributes.Count()} vibrators, but {cmds.Count()} commands given.");
}
await ScalarAsync(vibrateAttributes.Select((GenericDeviceMessageAttributes x, int i) => new ScalarCmd.ScalarSubcommand(x.Index, cmds.ElementAt(i), ActuatorType.Vibrate)).ToList()).ConfigureAwait(continueOnCapturedContext: false);
}
public async Task VibrateAsync(IEnumerable<(uint, double)> cmds)
{
await ScalarAsync(cmds.Select(((uint, double) x) => new ScalarCmd.ScalarSubcommand(x.Item1, x.Item2, ActuatorType.Vibrate)).ToList()).ConfigureAwait(continueOnCapturedContext: false);
}
public async Task OscillateAsync(double speed)
{
await ScalarAsync(new ScalarCmd.ScalarSubcommand(uint.MaxValue, speed, ActuatorType.Oscillate));
}
public async Task OscillateAsync(IEnumerable<double> cmds)
{
List<GenericDeviceMessageAttributes> oscillateAttributes = OscillateAttributes;
if (cmds.Count() > oscillateAttributes.Count())
{
throw new ButtplugDeviceException($"Device {Name} only has {oscillateAttributes.Count()} vibrators, but {cmds.Count()} commands given.");
}
await ScalarAsync(oscillateAttributes.Select((GenericDeviceMessageAttributes x, int i) => new ScalarCmd.ScalarSubcommand(x.Index, cmds.ElementAt(i), ActuatorType.Oscillate)).ToList()).ConfigureAwait(continueOnCapturedContext: false);
}
public async Task OscillateAsync(IEnumerable<(uint, double)> cmds)
{
await ScalarAsync(cmds.Select(((uint, double) x) => new ScalarCmd.ScalarSubcommand(x.Item1, x.Item2, ActuatorType.Oscillate)).ToList()).ConfigureAwait(continueOnCapturedContext: false);
}
public async Task RotateAsync(double speed, bool clockwise)
{
if (!RotateAttributes.Any())
{
throw new ButtplugDeviceException("Device " + Name + " does not support rotation");
}
RotateCmd rotateCmd = RotateCmd.Create(speed, clockwise, (uint)RotateAttributes.Count);
rotateCmd.DeviceIndex = Index;
await _handler.SendMessageExpectOk(rotateCmd).ConfigureAwait(continueOnCapturedContext: false);
}
public async Task RotateAsync(IEnumerable<(double, bool)> cmds)
{
if (!RotateAttributes.Any())
{
throw new ButtplugDeviceException("Device " + Name + " does not support rotation");
}
RotateCmd rotateCmd = RotateCmd.Create(cmds);
rotateCmd.DeviceIndex = Index;
await _handler.SendMessageExpectOk(rotateCmd).ConfigureAwait(continueOnCapturedContext: false);
}
public async Task LinearAsync(uint duration, double position)
{
if (!LinearAttributes.Any())
{
throw new ButtplugDeviceException("Device " + Name + " does not support linear position");
}
LinearCmd linearCmd = LinearCmd.Create(duration, position, (uint)LinearAttributes.Count);
linearCmd.DeviceIndex = Index;
await _handler.SendMessageExpectOk(linearCmd).ConfigureAwait(continueOnCapturedContext: false);
}
public async Task LinearAsync(IEnumerable<(uint, double)> cmds)
{
if (!LinearAttributes.Any())
{
throw new ButtplugDeviceException("Device " + Name + " does not support linear position");
}
LinearCmd linearCmd = LinearCmd.Create(cmds);
linearCmd.DeviceIndex = Index;
await _handler.SendMessageExpectOk(linearCmd).ConfigureAwait(continueOnCapturedContext: false);
}
public List<SensorDeviceMessageAttributes> SensorReadAttributes(SensorType sensor)
{
if (MessageAttributes.SensorReadCmd != null)
{
return MessageAttributes.SensorReadCmd.Where((SensorDeviceMessageAttributes x) => x.SensorType == sensor).ToList();
}
return Enumerable.Empty<SensorDeviceMessageAttributes>().ToList();
}
public async Task<double> BatteryAsync()
{
if (!HasBattery)
{
throw new ButtplugDeviceException("Device " + Name + " does not have battery capabilities.");
}
ButtplugMessage buttplugMessage = await _handler.SendMessageAsync(new SensorReadCmd(Index, SensorReadAttributes(SensorType.Battery).ElementAt(0).Index, SensorType.Battery)).ConfigureAwait(continueOnCapturedContext: false);
if (!(buttplugMessage is SensorReading sensorReading))
{
if (buttplugMessage is Error msg)
{
throw ButtplugException.FromError(msg);
}
throw new ButtplugMessageException("Message type " + buttplugMessage.Name + " not handled by BatteryAsync", buttplugMessage.Id);
}
return (double)sensorReading.data[0] / 100.0;
}
public async Task Stop()
{
await _handler.SendMessageExpectOk(new StopDeviceCmd(Index)).ConfigureAwait(continueOnCapturedContext: false);
}
}
internal class ButtplugClientMessageHandler
{
private IButtplugClientConnector _connector;
internal ButtplugClientMessageHandler(IButtplugClientConnector connector)
{
_connector = connector;
}
public async Task<ButtplugMessage> SendMessageAsync(ButtplugMessage msg, CancellationToken token = default(CancellationToken))
{
if (!_connector.Connected)
{
throw new ButtplugClientConnectorException("Client not connected.");
}
return await _connector.SendAsync(msg, token).ConfigureAwait(continueOnCapturedContext: false);
}
public async Task SendMessageExpectOk(ButtplugMessage msg, CancellationToken token = default(CancellationToken))
{
ButtplugMessage buttplugMessage = await SendMessageAsync(msg, token).ConfigureAwait(continueOnCapturedContext: false);
if (!(buttplugMessage is Ok))
{
if (buttplugMessage is Error msg2)
{
throw ButtplugException.FromError(msg2);
}
throw new ButtplugMessageException("Message type " + msg.Name + " not handled by SendMessageExpectOk", msg.Id);
}
}
}
public class ButtplugConnectorJSONParser
{
private readonly ButtplugJsonMessageParser _parser = new ButtplugJsonMessageParser();
public string Serialize(ButtplugMessage msg)
{
return _parser.Serialize(msg);
}
public string Serialize(ButtplugMessage[] msgs)
{
return _parser.Serialize(msgs);
}
public IEnumerable<ButtplugMessage> Deserialize(string msg)
{
return _parser.Deserialize(msg);
}
}
public class ButtplugConnectorMessageSorter : IDisposable
{
private int _counter;
private readonly ConcurrentDictionary<uint, TaskCompletionSource<ButtplugMessage>> _waitingMsgs = new ConcurrentDictionary<uint, TaskCompletionSource<ButtplugMessage>>();
public uint NextMsgId => Convert.ToUInt32(Interlocked.Increment(ref _counter));
public Task<ButtplugMessage> PrepareMessage(ButtplugMessage msg)
{
msg.Id = NextMsgId;
TaskCompletionSource<ButtplugMessage> taskCompletionSource = new TaskCompletionSource<ButtplugMessage>();
_waitingMsgs.TryAdd(msg.Id, taskCompletionSource);
return taskCompletionSource.Task;
}
public void CheckMessage(ButtplugMessage msg)
{
if (msg.Id == 0)
{
throw new ButtplugMessageException("Cannot sort message with System ID", msg.Id);
}
if (!_waitingMsgs.TryRemove(msg.Id, out var value))
{
throw new ButtplugMessageException("Message with non-matching ID received.", msg.Id);
}
if (msg is Error msg2)
{
value.SetException(ButtplugException.FromError(msg2));
}
else
{
value.SetResult(msg);
}
}
protected virtual void Dispose(bool disposing)
{
foreach (TaskCompletionSource<ButtplugMessage> value in _waitingMsgs.Values)
{
value.TrySetException(new Exception("Sorter has been destroyed with live tasks still in queue."));
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
public class ButtplugRemoteJSONConnector : IDisposable
{
private readonly ButtplugConnectorJSONParser _jsonSerializer = new ButtplugConnectorJSONParser();
private readonly ButtplugConnectorMessageSorter _msgSorter = new ButtplugConnectorMessageSorter();
public event EventHandler<MessageReceivedEventArgs> MessageReceived;
public event EventHandler<ButtplugExceptionEventArgs> InvalidMessageReceived;
protected Tuple<string, Task<ButtplugMessage>> PrepareMessage(ButtplugMessage msg)
{
Task<ButtplugMessage> item = _msgSorter.PrepareMessage(msg);
return new Tuple<string, Task<ButtplugMessage>>(_jsonSerializer.Serialize(msg), item);
}
protected void ReceiveMessages(string jSONMsg)
{
IEnumerable<ButtplugMessage> enumerable;
try
{
enumerable = _jsonSerializer.Deserialize(jSONMsg);
}
catch (ButtplugMessageException ex)
{
this.InvalidMessageReceived?.Invoke(this, new ButtplugExceptionEventArgs(ex));
return;
}
foreach (ButtplugMessage item in enumerable)
{
if (item.Id == 0)
{
this.MessageReceived?.Invoke(this, new MessageReceivedEventArgs(item));
continue;
}
try
{
_msgSorter.CheckMessage(item);
}
catch (ButtplugMessageException ex2)
{
this.InvalidMessageReceived?.Invoke(this, new ButtplugExceptionEventArgs(ex2));
}
}
}
protected virtual void Dispose(bool disposing)
{
_msgSorter.Dispose();
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
public class DeviceAddedEventArgs
{
public readonly ButtplugClientDevice Device;
public DeviceAddedEventArgs(ButtplugClientDevice device)
{
Device = device;
}
}
public class DeviceRemovedEventArgs
{
public readonly ButtplugClientDevice Device;
public DeviceRemovedEventArgs(ButtplugClientDevice device)
{
Device = device;
}
}
public interface IButtplugClientConnector
{
bool Connected { get; }
event EventHandler<MessageReceivedEventArgs> MessageReceived;
event EventHandler<ButtplugExceptionEventArgs> InvalidMessageReceived;
event EventHandler Disconnected;
Task ConnectAsync(CancellationToken token = default(CancellationToken));
Task DisconnectAsync(CancellationToken token = default(CancellationToken));
Task<ButtplugMessage> SendAsync(ButtplugMessage msg, CancellationToken token = default(CancellationToken));
}
}
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.IO.Pipes;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Security;
using System.Security.Authentication;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using vtortola.WebSockets.Async;
using vtortola.WebSockets.Extensibility;
using vtortola.WebSockets.Http;
using vtortola.WebSockets.Rfc6455.Header;
using vtortola.WebSockets.Tools;
using vtortola.WebSockets.Transports;
using vtortola.WebSockets.Transports.NamedPipes;
using vtortola.WebSockets.Transports.Sockets;
using vtortola.WebSockets.Transports.Tcp;
using vtortola.WebSockets.Transports.UnixSockets;
[assembly: AssemblyCompany("deniszykov")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("A lightweight and highly scalable asynchronous WebSocket listener for .NET Core, .NET and Mono.\r\nhttps://github.com/deniszykov/WebSocketListener")]
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/deniszykov/WebSocketListener")]
[assembly: AssemblyTitle("deniszykov.WebSocketListener")]
[assembly: AssemblyInformationalVersion("4.2.16")]
[assembly: AssemblyProduct("deniszykov.WebSocketListener")]
[assembly: AssemblyFileVersion("4.2.16.0")]
[assembly: AssemblyVersion("4.2.16.0")]
namespace vtortola.WebSockets.Deflate
{
public sealed class WebSocketDeflateContext : IWebSocketMessageExtensionContext
{
public WebSocketMessageReadStream ExtendReader(WebSocketMessageReadStream message)
{
if (message == null)
{
throw new ArgumentNullException("message");
}
if (message.Flags.Rsv1)
{
return new WebSocketDeflateReadStream(message);
}
return message;
}
public WebSocketMessageWriteStream ExtendWriter(WebSocketMessageWriteStream message)
{
if (message == null)
{
throw new ArgumentNullException("message");
}
message.ExtensionFlags.Rsv1 = true;
return new WebSocketDeflateWriteStream(message);
}
}
public sealed class WebSocketDeflateExtension : IWebSocketMessageExtension
{
public const string EXTENSION_NAME = "permessage-deflate";
private static readonly ReadOnlyCollection<WebSocketExtensionOption> DefaultOptions = new ReadOnlyCollection<WebSocketExtensionOption>(new WebSocketExtensionOption[1]
{
new WebSocketExtensionOption("client_no_context_takeover")
});
private static readonly WebSocketExtension DefaultResponse = new WebSocketExtension("permessage-deflate", DefaultOptions);
public string Name => "permessage-deflate";
public bool TryNegotiate(WebSocketHttpRequest request, out WebSocketExtension extensionResponse, out IWebSocketMessageExtensionContext context)
{
if (request == null)
{
throw new ArgumentNullException("request");
}
extensionResponse = DefaultResponse;
context = new WebSocketDeflateContext();
return true;
}
public IWebSocketMessageExtension Clone()
{
return (WebSocketDeflateExtension)MemberwiseClone();
}
public override string ToString()
{
return DefaultResponse.ToString();
}
}
public sealed class WebSocketDeflateReadStream : WebSocketMessageReadStream
{
private const int STATE_OPEN = 0;
private const int STATE_CLOSED = 1;
private const int STATE_DISPOSED = 2;
private readonly WebSocketMessageReadStream innerStream;
private readonly DeflateStream deflateStream;
private volatile int state;
public override WebSocketMessageType MessageType => innerStream.MessageType;
public override WebSocketExtensionFlags Flags => innerStream.Flags;
internal override WebSocketListenerOptions Options => innerStream.Options;
public WebSocketDeflateReadStream([NotNull] WebSocketMessageReadStream innerStream)
{
if (innerStream == null)
{
throw new ArgumentNullException("innerStream");
}
this.innerStream = innerStream;
deflateStream = new DeflateStream(innerStream, CompressionMode.Decompress, leaveOpen: true);
}
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
return deflateStream.ReadAsync(buffer, offset, count, cancellationToken);
}
public override Task CloseAsync()
{
if (Interlocked.CompareExchange(ref state, 1, 0) != 0)
{
return TaskHelper.CompletedTask;
}
return innerStream.CloseAsync();
}
protected override void Dispose(bool disposing)
{
if (Interlocked.Exchange(ref state, 2) != 2)
{
SafeEnd.Dispose(deflateStream);
SafeEnd.Dispose(innerStream);
}
}
}
public sealed class WebSocketDeflateWriteStream : WebSocketMessageWriteStream
{
private static readonly byte[] FINAL_BYTE = new byte[1];
private const int STATE_OPEN = 0;
private const int STATE_CLOSED = 1;
private const int STATE_DISPOSED = 2;
private readonly WebSocketMessageWriteStream innerStream;
private readonly DeflateStream deflateStream;
private volatile int state;
internal override WebSocketListenerOptions Options => innerStream.Options;
public WebSocketDeflateWriteStream([NotNull] WebSocketMessageWriteStream innerStream)
{
if (innerStream == null)
{
throw new ArgumentNullException("innerStream");
}
this.innerStream = innerStream;
deflateStream = new DeflateStream(innerStream, CompressionLevel.Optimal, leaveOpen: true);
}
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (buffer == null)
{
throw new ArgumentNullException("buffer");
}
if (offset < 0 || offset > buffer.Length)
{
throw new ArgumentOutOfRangeException("offset");
}
if (count < 0 || offset + count > buffer.Length)
{
throw new ArgumentOutOfRangeException("count");
}
if (count == 0)
{
return Task.FromResult(0);
}
return deflateStream.WriteAsync(buffer, offset, count, cancellationToken);
}
public override async Task WriteAndCloseAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (buffer == null)
{
throw new ArgumentNullException("buffer");
}
if (offset < 0 || offset > buffer.Length)
{
throw new ArgumentOutOfRangeException("offset");
}
if (count < 0 || offset + count > buffer.Length)
{
throw new ArgumentOutOfRangeException("count");
}
if (count > 0)
{
await deflateStream.WriteAsync(buffer, offset, count, cancellationToken);
}
await CloseAsync().ConfigureAwait(continueOnCapturedContext: false);
}
public override async Task CloseAsync()
{
if (Interlocked.CompareExchange(ref state, 1, 0) == 0)
{
await deflateStream.FlushAsync(CancellationToken.None);
deflateStream.Dispose();
await innerStream.WriteAndCloseAsync(FINAL_BYTE, 0, 1, CancellationToken.None).ConfigureAwait(continueOnCapturedContext: false);
}
}
protected override void Dispose(bool disposing)
{
if (Interlocked.Exchange(ref state, 2) != 2)
{
SafeEnd.Dispose(deflateStream);
SafeEnd.Dispose(innerStream);
base.Dispose(disposing);
}
}
}
public static class WebSocketMessageExtensionCollectionExtensions
{
public static WebSocketMessageExtensionCollection RegisterDeflateCompression(this WebSocketMessageExtensionCollection collection)
{
if (collection == null)
{
throw new ArgumentNullException("collection");
}
collection.Add(new WebSocketDeflateExtension());
return collection;
}
}
}
namespace JetBrains.Annotations
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.Delegate | AttributeTargets.GenericParameter)]
internal sealed class CanBeNullAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.Delegate | AttributeTargets.GenericParameter)]
internal sealed class NotNullAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Delegate)]
internal sealed class ItemNotNullAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Delegate)]
internal sealed class ItemCanBeNullAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Delegate)]
internal sealed class StringFormatMethodAttribute : Attribute
{
[NotNull]
public string FormatParameterName { get; private set; }
public StringFormatMethodAttribute([NotNull] string formatParameterName)
{
FormatParameterName = formatParameterName;
}
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = true)]
internal sealed class ValueProviderAttribute : Attribute
{
[NotNull]
public string Name { get; private set; }
public ValueProviderAttribute([NotNull] string name)
{
Name = name;
}
}
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class InvokerParameterNameAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method)]
internal sealed class NotifyPropertyChangedInvocatorAttribute : Attribute
{
[CanBeNull]
public string ParameterName { get; private set; }
public NotifyPropertyChangedInvocatorAttribute()
{
}
public NotifyPropertyChangedInvocatorAttribute([NotNull] string parameterName)
{
ParameterName = parameterName;
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
internal sealed class ContractAnnotationAttribute : Attribute
{
[NotNull]
public string Contract { get; private set; }
public bool ForceFullStates { get; private set; }
public ContractAnnotationAttribute([NotNull] string contract)
: this(contract, forceFullStates: false)
{
}
public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates)
{
Contract = contract;
ForceFullStates = forceFullStates;
}
}
[AttributeUsage(AttributeTargets.All)]
internal sealed class LocalizationRequiredAttribute : Attribute
{
public bool Required { get; private set; }
public LocalizationRequiredAttribute()
: this(required: true)
{
}
public LocalizationRequiredAttribute(bool required)
{
Required = required;
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface)]
internal sealed class CannotApplyEqualityOperatorAttribute : Attribute
{
}
[BaseTypeRequired(typeof(Attribute))]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
internal sealed class BaseTypeRequiredAttribute : Attribute
{
[NotNull]
public Type BaseType { get; private set; }
public BaseTypeRequiredAttribute([NotNull] Type baseType)
{
BaseType = baseType;
}
}
[AttributeUsage(AttributeTargets.All)]
internal sealed class UsedImplicitlyAttribute : Attribute
{
public ImplicitUseKindFlags UseKindFlags { get; private set; }
public ImplicitUseTargetFlags TargetFlags { get; private set; }
public UsedImplicitlyAttribute()
: this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default)
{
}
public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags)
: this(useKindFlags, ImplicitUseTargetFlags.Default)
{
}
public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags)
: this(ImplicitUseKindFlags.Default, targetFlags)
{
}
public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags)
{
UseKindFlags = useKindFlags;
TargetFlags = targetFlags;
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.GenericParameter)]
internal sealed class MeansImplicitUseAttribute : Attribute
{
[UsedImplicitly]
public ImplicitUseKindFlags UseKindFlags { get; private set; }
[UsedImplicitly]
public ImplicitUseTargetFlags TargetFlags { get; private set; }
public MeansImplicitUseAttribute()
: this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default)
{
}
public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags)
: this(useKindFlags, ImplicitUseTargetFlags.Default)
{
}
public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags)
: this(ImplicitUseKindFlags.Default, targetFlags)
{
}
public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags)
{
UseKindFlags = useKindFlags;
TargetFlags = targetFlags;
}
}
[Flags]
internal enum ImplicitUseKindFlags
{
Default = 7,
Access = 1,
Assign = 2,
InstantiatedWithFixedConstructorSignature = 4,
InstantiatedNoFixedConstructorSignature = 8
}
[Flags]
internal enum ImplicitUseTargetFlags
{
Default = 1,
Itself = 1,
Members = 2,
WithMembers = 3
}
[MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)]
internal sealed class PublicAPIAttribute : Attribute
{
[CanBeNull]
public string Comment { get; private set; }
public PublicAPIAttribute()
{
}
public PublicAPIAttribute([NotNull] string comment)
{
Comment = comment;
}
}
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class InstantHandleAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method)]
internal sealed class PureAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method)]
internal sealed class MustUseReturnValueAttribute : Attribute
{
[CanBeNull]
public string Justification { get; private set; }
public MustUseReturnValueAttribute()
{
}
public MustUseReturnValueAttribute([NotNull] string justification)
{
Justification = justification;
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.GenericParameter)]
internal sealed class ProvidesContextAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class PathReferenceAttribute : Attribute
{
[CanBeNull]
public string BasePath { get; private set; }
public PathReferenceAttribute()
{
}
public PathReferenceAttribute([NotNull][PathReference] string basePath)
{
BasePath = basePath;
}
}
[AttributeUsage(AttributeTargets.Method)]
internal sealed class SourceTemplateAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter, AllowMultiple = true)]
internal sealed class MacroAttribute : Attribute
{
[CanBeNull]
public string Expression { get; set; }
public int Editable { get; set; }
[CanBeNull]
public string Target { get; set; }
}
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
internal sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute
{
[NotNull]
public string Format { get; private set; }
public AspMvcAreaMasterLocationFormatAttribute([NotNull] string format)
{
Format = format;
}
}
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
internal sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute
{
[NotNull]
public string Format { get; private set; }
public AspMvcAreaPartialViewLocationFormatAttribute([NotNull] string format)
{
Format = format;
}
}
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
internal sealed class AspMvcAreaViewLocationFormatAttribute : Attribute
{
[NotNull]
public string Format { get; private set; }
public AspMvcAreaViewLocationFormatAttribute([NotNull] string format)
{
Format = format;
}
}
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
internal sealed class AspMvcMasterLocationFormatAttribute : Attribute
{
[NotNull]
public string Format { get; private set; }
public AspMvcMasterLocationFormatAttribute([NotNull] string format)
{
Format = format;
}
}
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
internal sealed class AspMvcPartialViewLocationFormatAttribute : Attribute
{
[NotNull]
public string Format { get; private set; }
public AspMvcPartialViewLocationFormatAttribute([NotNull] string format)
{
Format = format;
}
}
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
internal sealed class AspMvcViewLocationFormatAttribute : Attribute
{
[NotNull]
public string Format { get; private set; }
public AspMvcViewLocationFormatAttribute([NotNull] string format)
{
Format = format;
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter)]
internal sealed class AspMvcActionAttribute : Attribute
{
[CanBeNull]
public string AnonymousProperty { get; private set; }
public AspMvcActionAttribute()
{
}
public AspMvcActionAttribute([NotNull] string anonymousProperty)
{
AnonymousProperty = anonymousProperty;
}
}
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class AspMvcAreaAttribute : Attribute
{
[CanBeNull]
public string AnonymousProperty { get; private set; }
public AspMvcAreaAttribute()
{
}
public AspMvcAreaAttribute([NotNull] string anonymousProperty)
{
AnonymousProperty = anonymousProperty;
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter)]
internal sealed class AspMvcControllerAttribute : Attribute
{
[CanBeNull]
public string AnonymousProperty { get; private set; }
public AspMvcControllerAttribute()
{
}
public AspMvcControllerAttribute([NotNull] string anonymousProperty)
{
AnonymousProperty = anonymousProperty;
}
}
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class AspMvcMasterAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class AspMvcModelTypeAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter)]
internal sealed class AspMvcPartialViewAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
internal sealed class AspMvcSuppressViewErrorAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class AspMvcDisplayTemplateAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class AspMvcEditorTemplateAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class AspMvcTemplateAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter)]
internal sealed class AspMvcViewAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class AspMvcViewComponentAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter)]
internal sealed class AspMvcViewComponentViewAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter)]
internal sealed class AspMvcActionSelectorAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
internal sealed class HtmlElementAttributesAttribute : Attribute
{
[CanBeNull]
public string Name { get; private set; }
public HtmlElementAttributesAttribute()
{
}
public HtmlElementAttributesAttribute([NotNull] string name)
{
Name = name;
}
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
internal sealed class HtmlAttributeValueAttribute : Attribute
{
[NotNull]
public string Name { get; private set; }
public HtmlAttributeValueAttribute([NotNull] string name)
{
Name = name;
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter)]
internal sealed class RazorSectionAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property)]
internal sealed class CollectionAccessAttribute : Attribute
{
public CollectionAccessType CollectionAccessType { get; private set; }
public CollectionAccessAttribute(CollectionAccessType collectionAccessType)
{
CollectionAccessType = collectionAccessType;
}
}
[Flags]
internal enum CollectionAccessType
{
None = 0,
Read = 1,
ModifyExistingContent = 2,
UpdatedContent = 6
}
[AttributeUsage(AttributeTargets.Method)]
internal sealed class AssertionMethodAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class AssertionConditionAttribute : Attribute
{
public AssertionConditionType ConditionType { get; private set; }
public AssertionConditionAttribute(AssertionConditionType conditionType)
{
ConditionType = conditionType;
}
}
internal enum AssertionConditionType
{
IS_TRUE,
IS_FALSE,
IS_NULL,
IS_NOT_NULL
}
[Obsolete("Use [ContractAnnotation('=> halt')] instead")]
[AttributeUsage(AttributeTargets.Method)]
internal sealed class TerminatesProgramAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method)]
internal sealed class LinqTunnelAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class NoEnumerationAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class RegexPatternAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Interface)]
internal sealed class NoReorderAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Class)]
internal sealed class XamlItemsControlAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
internal sealed class XamlItemBindingOfItemsControlAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
internal sealed class AspChildControlTypeAttribute : Attribute
{
[NotNull]
public string TagName { get; private set; }
[NotNull]
public Type ControlType { get; private set; }
public AspChildControlTypeAttribute([NotNull] string tagName, [NotNull] Type controlType)
{
TagName = tagName;
ControlType = controlType;
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)]
internal sealed class AspDataFieldAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)]
internal sealed class AspDataFieldsAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
internal sealed class AspMethodPropertyAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
internal sealed class AspRequiredAttributeAttribute : Attribute
{
[NotNull]
public string Attribute { get; private set; }
public AspRequiredAttributeAttribute([NotNull] string attribute)
{
Attribute = attribute;
}
}
[AttributeUsage(AttributeTargets.Property)]
internal sealed class AspTypePropertyAttribute : Attribute
{
public bool CreateConstructorReferences { get; private set; }
public AspTypePropertyAttribute(bool createConstructorReferences)
{
CreateConstructorReferences = createConstructorReferences;
}
}
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class RazorImportNamespaceAttribute : Attribute
{
[NotNull]
public string Name { get; private set; }
public RazorImportNamespaceAttribute([NotNull] string name)
{
Name = name;
}
}
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class RazorInjectionAttribute : Attribute
{
[NotNull]
public string Type { get; private set; }
[NotNull]
public string FieldName { get; private set; }
public RazorInjectionAttribute([NotNull] string type, [NotNull] string fieldName)
{
Type = type;
FieldName = fieldName;
}
}
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class RazorDirectiveAttribute : Attribute
{
[NotNull]
public string Directive { get; private set; }
public RazorDirectiveAttribute([NotNull] string directive)
{
Directive = directive;
}
}
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class RazorPageBaseTypeAttribute : Attribute
{
[NotNull]
public string BaseType { get; private set; }
[CanBeNull]
public string PageName { get; private set; }
public RazorPageBaseTypeAttribute([NotNull] string baseType)
{
BaseType = baseType;
}
public RazorPageBaseTypeAttribute([NotNull] string baseType, string pageName)
{
BaseType = baseType;
PageName = pageName;
}
}
[AttributeUsage(AttributeTargets.Method)]
internal sealed class RazorHelperCommonAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
internal sealed class RazorLayoutAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method)]
internal sealed class RazorWriteLiteralMethodAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method)]
internal sealed class RazorWriteMethodAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class RazorWriteMethodParameterAttribute : Attribute
{
}
}
namespace vtortola.WebSockets
{
public abstract class BufferManager
{
[Obsolete("Is not used")]
public abstract int SmallBufferSize { get; }
public abstract int LargeBufferSize { get; }
public static BufferManager CreateBufferManager(long maxBufferPoolSize, int maxBufferSize)
{
if (maxBufferPoolSize < 0)
{
throw new ArgumentOutOfRangeException("maxBufferPoolSize");
}
if (maxBufferSize < 0)
{
throw new ArgumentOutOfRangeException("maxBufferSize");
}
if (maxBufferSize < 256)
{
maxBufferSize = 256;
}
if (maxBufferPoolSize < maxBufferSize)
{
maxBufferPoolSize = maxBufferSize * 10;
}
int num = (int)Math.Pow(2.0, Math.Ceiling(Math.Log(maxBufferSize) / Math.Log(2.0)));
int num2 = (int)Math.Max(2.0, Math.Ceiling((float)maxBufferPoolSize / 2f / (float)num));
int num3 = Math.Max(32, num / 256);
int smallPoolSizeLimit = (int)Math.Max(2L, (maxBufferPoolSize - num2 * num) / num3);
return new DefaultBufferManager(num3, smallPoolSizeLimit, num, num2);
}
[NotNull]
public static BufferManager CreateBufferManager(int smallBufferSize, int smallBufferPoolSize, int largeBufferSize, int largeBufferPoolSize)
{
if (smallBufferSize < 0)
{
throw new ArgumentOutOfRangeException("smallBufferSize");
}
if (largeBufferSize < 0)
{
throw new ArgumentOutOfRangeException("largeBufferSize");
}
if (smallBufferPoolSize < 0)
{
throw new ArgumentOutOfRangeException("smallBufferPoolSize");
}
if (largeBufferPoolSize < 0)
{
throw new ArgumentOutOfRangeException("largeBufferPoolSize");
}
return new DefaultBufferManager(smallBufferSize, smallBufferPoolSize, largeBufferSize, largeBufferPoolSize);
}
[Obsolete("Is not used")]
public abstract void Clear();
public abstract void ReturnBuffer(byte[] buffer);
public abstract byte[] TakeBuffer(int bufferSize);
}
public sealed class ConsoleLogger : ILogger
{
public static ConsoleLogger Instance = new ConsoleLogger();
public bool IsDebugEnabled { get; set; }
public bool IsWarningEnabled { get; set; }
public bool IsErrorEnabled { get; set; }
public ConsoleLogger()
{
IsDebugEnabled = true;
IsWarningEnabled = true;
IsErrorEnabled = true;
}
public void Debug(string message, Exception error = null)
{
if (IsDebugEnabled)
{
if (!string.IsNullOrEmpty(message))
{
Console.WriteLine(message);
}
if (error != null)
{
Console.WriteLine(error);
}
}
}
public void Warning(string message, Exception error = null)
{
if (IsWarningEnabled)
{
string.IsNullOrEmpty(message);
if (error != null)
{
Console.WriteLine(error);
}
}
}
public void Error(string message, Exception error = null)
{
if (IsErrorEnabled)
{
string.IsNullOrEmpty(message);
if (error != null)
{
Console.WriteLine(error);
}
}
}
}
public sealed class DebugLogger : ILogger
{
public static DebugLogger Instance = new DebugLogger();
public bool IsDebugEnabled { get; set; }
public bool IsWarningEnabled { get; set; }
public bool IsErrorEnabled { get; set; }
public DebugLogger()
{
IsDebugEnabled = true;
IsWarningEnabled = true;
IsErrorEnabled = true;
}
public void Debug(string message, Exception error = null)
{
if (IsDebugEnabled)
{
string.IsNullOrEmpty(message);
}
}
public void Warning(string message, Exception error = null)
{
if (IsWarningEnabled)
{
string.IsNullOrEmpty(message);
}
}
public void Error(string message, Exception error = null)
{
if (IsErrorEnabled)
{
string.IsNullOrEmpty(message);
}
}
}
internal sealed class DefaultBufferManager : BufferManager
{
private readonly ObjectPool<byte[]> smallPool;
private readonly ObjectPool<byte[]> largePool;
public override int SmallBufferSize { get; }
public override int LargeBufferSize { get; }
public DefaultBufferManager(int smallBufferSize, int smallPoolSizeLimit, int largeBufferSize, int largePoolSizeLimit)
{
SmallBufferSize = smallBufferSize;
LargeBufferSize = largeBufferSize;
smallPool = new ObjectPool<byte[]>(() => new byte[smallBufferSize], smallPoolSizeLimit);
largePool = new ObjectPool<byte[]>(() => new byte[largeBufferSize], largePoolSizeLimit);
}
public override void Clear()
{
smallPool.Clear();
largePool.Clear();
}
public override void ReturnBuffer(byte[] buffer)
{
if (buffer == null)
{
throw new ArgumentNullException("buffer");
}
if (buffer.Length >= LargeBufferSize)
{
largePool.Return(buffer);
return;
}
if (buffer.Length >= SmallBufferSize)
{
smallPool.Return(buffer);
return;
}
throw new ArgumentException("Length of buffer does not match the pool's buffer length property.", "buffer");
}
public override byte[] TakeBuffer(int bufferSize)
{
if (bufferSize < 0 || bufferSize > LargeBufferSize)
{
throw new ArgumentOutOfRangeException("bufferSize");
}
if (bufferSize >= SmallBufferSize)
{
return largePool.Take();
}
return smallPool.Take();
}
}
[PublicAPI]
public interface IHttpFallback
{
void Post([NotNull] IHttpRequest request, [NotNull] NetworkConnection networkConnection);
}
[PublicAPI]
public interface IHttpRequest
{
EndPoint LocalEndPoint { get; }
EndPoint RemoteEndPoint { get; }
Uri RequestUri { get; }
Version HttpVersion { get; }
bool IsSecure { get; }
CookieCollection Cookies { get; }
Headers<RequestHeader> Headers { get; }
IDictionary<string, object> Items { get; }
}
public interface IWebSocketConnectionExtension
{
[ItemNotNull]
[NotNull]
Task<NetworkConnection> ExtendConnectionAsync([NotNull] NetworkConnection networkConnection);
[NotNull]
IWebSocketConnectionExtension Clone();
}
public interface IWebSocketMessageExtension
{
string Name { get; }
bool TryNegotiate(WebSocketHttpRequest request, out WebSocketExtension extensionResponse, out IWebSocketMessageExtensionContext context);
IWebSocketMessageExtension Clone();
new string ToString();
}
public interface IWebSocketMessageExtensionContext
{
[NotNull]
WebSocketMessageReadStream ExtendReader([NotNull] WebSocketMessageReadStream message);
[NotNull]
WebSocketMessageWriteStream ExtendWriter([NotNull] WebSocketMessageWriteStream message);
}
[PublicAPI]
public sealed class WebSocketConnectionExtensionCollection : IReadOnlyCollection<IWebSocketConnectionExtension>, IEnumerable<IWebSocketConnectionExtension>, IEnumerable
{
private readonly List<IWebSocketConnectionExtension> extensions;
private volatile int useCounter;
public int Count => extensions.Count;
public bool IsReadOnly => useCounter > 0;
public WebSocketConnectionExtensionCollection()
{
extensions = new List<IWebSocketConnectionExtension>();
}
public void Add(IWebSocketConnectionExtension extension)
{
if (extension == null)
{
throw new ArgumentNullException("extension");
}
if (IsReadOnly)
{
throw new WebSocketException("New entries cannot be added because this collection is used in running WebSocketClient or WebSocketListener.");
}
if (extensions.Any((IWebSocketConnectionExtension ext) => ext.GetType() == extension.GetType()))
{
throw new WebSocketException($"Can't add extension '{extension}' because another extension of type '{extension.GetType().Name}' is already exists in collection.");
}
extensions.Add(extension);
}
public WebSocketConnectionExtensionCollection RegisterSecureConnection(X509Certificate2 certificate, RemoteCertificateValidationCallback validation = null, SslProtocols supportedSslProtocols = SslProtocols.Tls12)
{
if (certificate == null)
{
throw new ArgumentNullException("certificate");
}
WebSocketSecureConnectionExtension extension = new WebSocketSecureConnectionExtension(certificate, validation, supportedSslProtocols);
Add(extension);
return this;
}
IEnumerator<IWebSocketConnectionExtension> IEnumerable<IWebSocketConnectionExtension>.GetEnumerator()
{
return extensions.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return extensions.GetEnumerator();
}
public List<IWebSocketConnectionExtension>.Enumerator GetEnumerator()
{
return extensions.GetEnumerator();
}
internal WebSocketConnectionExtensionCollection Clone()
{
WebSocketConnectionExtensionCollection webSocketConnectionExtensionCollection = new WebSocketConnectionExtensionCollection();
foreach (IWebSocketConnectionExtension extension in extensions)
{
webSocketConnectionExtensionCollection.extensions.Add(extension.Clone());
}
return webSocketConnectionExtensionCollection;
}
internal void SetUsed(bool isUsed)
{
int num = 0;
num = ((!isUsed) ? Interlocked.Decrement(ref useCounter) : Interlocked.Increment(ref useCounter));
if (num < 0)
{
throw new InvalidOperationException("The collection is released more than once.");
}
}
public override string ToString()
{
return string.Join(", ", extensions);
}
}
public sealed class WebSocketFactoryCollection : IReadOnlyCollection<WebSocketFactory>, IEnumerable<WebSocketFactory>, IEnumerable
{
private readonly Dictionary<short, WebSocketFactory> factoryByVersion;
private volatile int useCounter;
public IEnumerable<short> SupportedVersions => factoryByVersion.Keys;
public int Count => factoryByVersion.Count;
public bool IsReadOnly => useCounter > 0;
public WebSocketFactoryCollection()
{
factoryByVersion = new Dictionary<short, WebSocketFactory>();
}
public void Add(WebSocketFactory factory)
{
if (factory == null)
{
throw new ArgumentNullException("factory");
}
if (IsReadOnly)
{
throw new WebSocketException("New entries cannot be added because this collection is used in running WebSocketClient or WebSocketListener.");
}
if (factoryByVersion.ContainsKey(factory.Version))
{
throw new WebSocketException(string.Format("Can't add {0} '{1}' because another {2} with ", "WebSocketFactory", factory, "WebSocketFactory") + $"version '{factory.Version}' is already exists in collection.");
}
factoryByVersion.Add(factory.Version, factory);
}
IEnumerator IEnumerable.GetEnumerator()
{
return factoryByVersion.Values.GetEnumerator();
}
IEnumerator<WebSocketFactory> IEnumerable<WebSocketFactory>.GetEnumerator()
{
return factoryByVersion.Values.GetEnumerator();
}
public Dictionary<short, WebSocketFactory>.ValueCollection.Enumerator GetEnumerator()
{
return factoryByVersion.Values.GetEnumerator();
}
internal WebSocketFactoryCollection Clone()
{
WebSocketFactoryCollection webSocketFactoryCollection = new WebSocketFactoryCollection();
foreach (KeyValuePair<short, WebSocketFactory> item in factoryByVersion)
{
webSocketFactoryCollection.factoryByVersion[item.Key] = item.Value.Clone();
}
return webSocketFactoryCollection;
}
internal void SetUsed(bool isUsed)
{
int num = 0;
num = ((!isUsed) ? Interlocked.Decrement(ref useCounter) : Interlocked.Increment(ref useCounter));
if (num < 0)
{
throw new InvalidOperationException("The collection is released more than once.");
}
}
internal WebSocketFactory GetLast()
{
return factoryByVersion[factoryByVersion.Keys.Max()];
}
public bool TryGetWebSocketFactory(WebSocketHttpRequest request, out WebSocketFactory factory)
{
if (request == null)
{
throw new ArgumentNullException("request");
}
factory = null;
short result = 0;
if (short.TryParse(request.Headers[RequestHeader.WebSocketVersion], out result) && factoryByVersion.TryGetValue(result, out factory))
{
return true;
}
return false;
}
}
public sealed class WebSocketMessageExtensionCollection : IReadOnlyCollection<IWebSocketMessageExtension>, IEnumerable<IWebSocketMessageExtension>, IEnumerable
{
private readonly List<IWebSocketMessageExtension> extensions;
private volatile int useCounter;
public int Count => extensions.Count;
public bool IsReadOnly => useCounter > 0;
public WebSocketMessageExtensionCollection()
{
extensions = new List<IWebSocketMessageExtension>();
}
public void Add(IWebSocketMessageExtension extension)
{
if (extension == null)
{
throw new ArgumentNullException("extension");
}
if (IsReadOnly)
{
throw new WebSocketException("New entries cannot be added because this collection is used in running WebSocketClient or WebSocketListener.");
}
if (extensions.Any((IWebSocketMessageExtension ext) => ext.GetType() == extension.GetType()))
{
throw new WebSocketException($"Can't add extension '{extension}' because another extension of type '{extension.GetType().Name}' is already exists in collection.");
}
extensions.Add(extension);
}
IEnumerator<IWebSocketMessageExtension> IEnumerable<IWebSocketMessageExtension>.GetEnumerator()
{
return extensions.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return extensions.GetEnumerator();
}
public List<IWebSocketMessageExtension>.Enumerator GetEnumerator()
{
return extensions.GetEnumerator();
}
internal WebSocketMessageExtensionCollection Clone()
{
WebSocketMessageExtensionCollection webSocketMessageExtensionCollection = new WebSocketMessageExtensionCollection();
foreach (IWebSocketMessageExtension extension in extensions)
{
webSocketMessageExtensionCollection.extensions.Add(extension.Clone());
}
return webSocketMessageExtensionCollection;
}
internal void SetUsed(bool isUsed)
{
int num = 0;
num = ((!isUsed) ? Interlocked.Decrement(ref useCounter) : Interlocked.Increment(ref useCounter));
if (num < 0)
{
throw new InvalidOperationException("The collection is released more than once.");
}
}
public override string ToString()
{
return string.Join(", ", extensions);
}
}
public sealed class WebSocketSecureConnectionExtension : IWebSocketConnectionExtension
{
private readonly X509Certificate2 _certificate;
private readonly RemoteCertificateValidationCallback _validation;
private readonly SslProtocols _protocols;
public WebSocketSecureConnectionExtension(X509Certificate2 certificate)
{
if (certificate == null)
{
throw new ArgumentNullException("certificate");
}
_certificate = certificate;
_protocols = SslProtocols.Tls12;
}
public WebSocketSecureConnectionExtension(X509Certificate2 certificate, RemoteCertificateValidationCallback validation)
{
if (certificate == null)
{
throw new ArgumentNullException("certificate");
}
_certificate = certificate;
_validation = validation;
_protocols = SslProtocols.Tls12;
}
public WebSocketSecureConnectionExtension(X509Certificate2 certificate, RemoteCertificateValidationCallback validation, SslProtocols supportedSslProtocols)
{
if (certificate == null)
{
throw new ArgumentNullException("certificate");
}
_certificate = certificate;
_validation = validation;
_protocols = supportedSslProtocols;
}
public async Task<NetworkConnection> ExtendConnectionAsync(NetworkConnection networkConnection)
{
if (networkConnection == null)
{
throw new ArgumentNullException("networkConnection");
}
SslStream ssl = new SslStream(networkConnection.AsStream(), leaveInnerStreamOpen: false, _validation);
try
{
await ssl.AuthenticateAsServerAsync(_certificate, _validation != null, _protocols, checkCertificateRevocation: false).ConfigureAwait(continueOnCapturedContext: false);
return new SslNetworkConnection(ssl, networkConnection);
}
catch
{
SafeEnd.Dispose(ssl);
throw;
}
}
public IWebSocketConnectionExtension Clone()
{
return (IWebSocketConnectionExtension)MemberwiseClone();
}
public override string ToString()
{
return $"Secure Connection: protocols: {_protocols}, certificate: {_certificate.SubjectName}";
}
}
public sealed class WebSocketExtensionFlags
{
private bool _rsv1;
private bool _rsv2;
private bool _rsv3;
private readonly bool _none;
public static readonly WebSocketExtensionFlags None = new WebSocketExtensionFlags(none: true);
public bool Rsv1
{
get
{
return _rsv1;
}
set
{
_rsv1 = value && !_none;
}
}
public bool Rsv2
{
get
{
return _rsv2;
}
set
{
_rsv2 = value && !_none;
}
}
public bool Rsv3
{
get
{
return _rsv3;
}
set
{
_rsv3 = value && !_none;
}
}
public WebSocketExtensionFlags()
{
_none = false;
}
private WebSocketExtensionFlags(bool none)
{
_none = true;
}
}
public enum WebSocketMessageType
{
Text = 1,
Binary
}
public delegate Task<bool> HttpAuthenticationCallback(WebSocketHttpRequest request, WebSocketHttpResponse response);
public static class CookieParser
{
public static IEnumerable<Cookie> Parse([CanBeNull] string cookieString)
{
if (string.IsNullOrWhiteSpace(cookieString))
{
yield break;
}
string text = string.Empty;
string text2 = string.Empty;
for (int i = 0; i < cookieString.Length; i++)
{
char c = cookieString[i];
if (c == '=' && string.IsNullOrWhiteSpace(text2))
{
text2 = text;
text = string.Empty;
}
else if (c == ';')
{
if (!string.IsNullOrWhiteSpace(text2))
{
yield return CreateCookie(text2, text);
}
else
{
yield return CreateCookie(text, string.Empty);
}
text2 = string.Empty;
text = string.Empty;
}
else
{
text += c;
}
}
if (!string.IsNullOrWhiteSpace(text2) && !string.IsNullOrWhiteSpace(text))
{
yield return CreateCookie(text2, text);
}
}
private static Cookie CreateCookie(string key, string value)
{
return new Cookie(key.Trim(), WebUtility.UrlDecode(value.Trim()));
}
}
public sealed class WebSocketExtension
{
public static readonly ReadOnlyCollection<WebSocketExtensionOption> Empty = new ReadOnlyCollection<WebSocketExtensionOption>(new List<WebSocketExtensionOption>());
private readonly string extensionString;
public readonly string Name;
public readonly ReadOnlyCollection<WebSocketExtensionOption> Options;
public WebSocketExtension(string name, IList<WebSocketExtensionOption> options)
{
if (name == null)
{
throw new ArgumentNullException("name");
}
if (options == null)
{
throw new ArgumentNullException("options");
}
Name = name;
Options = (options as ReadOnlyCollection<WebSocketExtensionOption>) ?? new ReadOnlyCollection<WebSocketExtensionOption>(options);
extensionString = ((Options.Count > 0) ? (Name + ";" + string.Join(";", Options)) : Name);
}
public WebSocketExtension(string name)
{
Name = name;
Options = Empty;
}
public override string ToString()
{
return extensionString;
}
}
public class WebSocketExtensionOption
{
public readonly string Name;
public readonly string Value;
public readonly bool ClientAvailableOption;
public WebSocketExtensionOption(string name)
{
if (name == null)
{
throw new ArgumentNullException("name");
}
Name = name;
}
public WebSocketExtensionOption(string name, bool clientAvailableOption)
{
if (name == null)
{
throw new ArgumentNullException("name");
}
Name = name;
ClientAvailableOption = clientAvailableOption;
}
public WebSocketExtensionOption(string name, string value)
{
if (name == null)
{
throw new ArgumentNullException("name");
}
Name = name;
Value = value;
}
public override string ToString()
{
if (string.IsNullOrEmpty(Value))
{
return Name;
}
return Name + "=" + Value;
}
}
internal class WebSocketHandshake : IComparable<WebSocketHandshake>, IEquatable<WebSocketHandshake>
{
private static long LastId = 1L;
private bool _invalidated;
public readonly long Id;
[NotNull]
public readonly WebSocketHttpRequest Request;
public readonly WebSocketHttpResponse Response;
public readonly List<IWebSocketMessageExtensionContext> NegotiatedMessageExtensions;
public bool IsWebSocketRequest { get; internal set; }
public bool IsVersionSupported { get; internal set; }
public bool IsResponseSent { get; internal set; }
public WebSocketFactory Factory { get; internal set; }
public ExceptionDispatchInfo Error { get; internal set; }
public bool IsValidWebSocketRequest
{
get
{
if (!_invalidated && Error == null && IsWebSocketRequest && IsVersionSupported)
{
return Response.Status == HttpStatusCode.SwitchingProtocols;
}
return false;
}
set
{
_invalidated = !value;
}
}
public bool IsValidHttpRequest
{
get
{
if (!_invalidated)
{
return Error == null;
}
return false;
}
set
{
_invalidated = !value;
}
}
public WebSocketHandshake([NotNull] WebSocketHttpRequest request)
{
if (request == null)
{
throw new ArgumentNullException("request");
}
Id = Interlocked.Increment(ref LastId);
Request = request;
Response = new WebSocketHttpResponse();
NegotiatedMessageExtensions = new List<IWebSocketMessageExtensionContext>();
}
public string ComputeHandshake()
{
string text = Request.Headers[RequestHeader.WebSocketKey];
if (string.IsNullOrEmpty(text))
{
throw new InvalidOperationException("Missing or wrong " + Headers<RequestHeader>.GetHeaderName(RequestHeader.WebSocketKey) + " header in request.");
}
using SHA1 sHA = SHA1.Create();
return Convert.ToBase64String(sHA.ComputeHash(Encoding.UTF8.GetBytes(text + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")));
}
public string GenerateClientNonce()
{
return Convert.ToBase64String(Guid.NewGuid().ToByteArray());
}
public int CompareTo(WebSocketHandshake other)
{
if (other == null)
{
return 1;
}
long id = Id;
return id.CompareTo(other.Id);
}
public bool Equals(WebSocketHandshake other)
{
if (other == null)
{
return false;
}
if (this == other)
{
return true;
}
return Id == other.Id;
}
public override bool Equals(object obj)
{
return Equals(obj as WebSocketHandshake);
}
public override int GetHashCode()
{
long id = Id;
return id.GetHashCode();
}
public override string ToString()
{
return $"Handshake, id: {Id}, request: {Request}, response: {Response}";
}
}
internal class WebSocketHandshaker
{
private static readonly Version HttpVersion11 = new Version(1, 1);
private static readonly Version HttpVersion10 = new Version(1, 0);
private readonly ILogger log;
private readonly WebSocketListenerOptions options;
private readonly WebSocketFactoryCollection factories;
public WebSocketHandshaker(WebSocketFactoryCollection factories, WebSocketListenerOptions options)
{
if (factories == null)
{
throw new ArgumentNullException("factories");
}
if (options == null)
{
throw new ArgumentNullException("options");
}
log = options.Logger;
this.factories = factories;
this.options = options;
}
public async Task<WebSocketHandshake> HandshakeAsync(NetworkConnection networkConnection)
{
if (networkConnection == null)
{
throw new ArgumentNullException("networkConnection");
}
WebSocketHttpRequest request = new WebSocketHttpRequest(HttpRequestDirection.Incoming)
{
LocalEndPoint = (networkConnection.LocalEndPoint ?? WebSocketHttpRequest.NoAddress),
RemoteEndPoint = (networkConnection.RemoteEndPoint ?? WebSocketHttpRequest.NoAddress),
IsSecure = (networkConnection is SslNetworkConnection)
};
WebSocketHandshake handshake = new WebSocketHandshake(request);
try
{
await ReadHttpRequestAsync(networkConnection, handshake).ConfigureAwait(continueOnCapturedContext: false);
if (!IsWebSocketRequestValid(handshake))
{
await WriteHttpResponseAsync(handshake, networkConnection).ConfigureAwait(continueOnCapturedContext: false);
return handshake;
}
handshake.IsWebSocketRequest = true;
WebSocketFactory factory = null;
if (!factories.TryGetWebSocketFactory(handshake.Request, out factory))
{
await WriteHttpResponseAsync(handshake, networkConnection).ConfigureAwait(continueOnCapturedContext: false);
return handshake;
}
handshake.Factory = factory;
handshake.IsVersionSupported = true;
ConsolidateObjectModel(handshake);
SelectExtensions(handshake);
if (!(await RunHttpNegotiationHandlerAsync(handshake).ConfigureAwait(continueOnCapturedContext: false)))
{
throw new WebSocketException("HTTP authentication failed.");
}
await WriteHttpResponseAsync(handshake, networkConnection).ConfigureAwait(continueOnCapturedContext: false);
}
catch (Exception ex)
{
if (log.IsDebugEnabled)
{
log.Debug("Failed to handshake request.", ex);
}
handshake.Error = ExceptionDispatchInfo.Capture(ex);
if (!handshake.IsResponseSent)
{
try
{
await WriteHttpResponseAsync(handshake, networkConnection).ConfigureAwait(continueOnCapturedContext: false);
}
catch (Exception error)
{
if (log.IsDebugEnabled)
{
log.Debug("Failed to write error response.", error);
}
}
}
}
return handshake;
}
private static bool IsWebSocketRequestValid(WebSocketHandshake handShake)
{
if (handShake == null)
{
throw new ArgumentNullException("handShake");
}
Headers<RequestHeader> headers = handShake.Request.Headers;
if (headers.Contains(RequestHeader.Host) && headers.Contains(RequestHeader.Upgrade) && headers.GetValues(RequestHeader.Upgrade).Contains("websocket", StringComparison.OrdinalIgnoreCase) && headers.Contains(RequestHeader.Connection) && !string.IsNullOrWhiteSpace(headers.Get(RequestHeader.WebSocketKey)))
{
return headers.Contains(RequestHeader.WebSocketVersion);
}
return false;
}
private async Task<bool> RunHttpNegotiationHandlerAsync(WebSocketHandshake handshake)
{
if (handshake == null)
{
throw new ArgumentNullException("handshake");
}
if (options.HttpAuthenticationHandler != null)
{
try
{
return await options.HttpAuthenticationHandler(handshake.Request, handshake.Response).ConfigureAwait(continueOnCapturedContext: false);
}
catch (Exception source)
{
handshake.Response.Status = HttpStatusCode.InternalServerError;
handshake.Error = ExceptionDispatchInfo.Capture(source);
return false;
}
}
return true;
}
private void SelectExtensions(WebSocketHandshake handshake)
{
if (handshake == null)
{
throw new ArgumentNullException("handshake");
}
foreach (WebSocketExtension extRequest in handshake.Request.WebSocketExtensions)
{
IWebSocketMessageExtension webSocketMessageExtension = handshake.Factory.MessageExtensions.SingleOrDefault((IWebSocketMessageExtension x) => x.Name.Equals(extRequest.Name, StringComparison.OrdinalIgnoreCase));
if (webSocketMessageExtension != null && webSocketMessageExtension.TryNegotiate(handshake.Request, out var extensionResponse, out var context))
{
handshake.NegotiatedMessageExtensions.Add(context);
handshake.Response.WebSocketExtensions.Add(extensionResponse);
}
}
}
private async Task WriteHttpResponseAsync(WebSocketHandshake handshake, NetworkConnection networkConnection)
{
if (handshake == null)
{
throw new ArgumentNullException("handshake");
}
if (networkConnection == null)
{
throw new ArgumentNullException("networkConnection");
}
if (handshake.IsWebSocketRequest || !handshake.IsValidHttpRequest || options.HttpFallback == null)
{
handshake.IsResponseSent = true;
using StreamWriter writer = new StreamWriter(networkConnection.AsStream(), Encoding.ASCII, 1024, leaveOpen: true);
await WriteResponseInternal(handshake, writer).ConfigureAwait(continueOnCapturedContext: false);
await writer.FlushAsync().ConfigureAwait(continueOnCapturedContext: false);
}
}
private async Task WriteResponseInternal(WebSocketHandshake handshake, StreamWriter writer)
{
if (handshake == null)
{
throw new ArgumentNullException("handshake");
}
if (writer == null)
{
throw new ArgumentNullException("writer");
}
if (!handshake.IsWebSocketRequest)
{
handshake.Response.Status = HttpStatusCode.BadRequest;
await SendNegotiationErrorResponseAsync(writer, handshake.Response.Status);
}
else if (!handshake.IsVersionSupported)
{
handshake.Response.Status = HttpStatusCode.UpgradeRequired;
await SendVersionNegotiationErrorResponse(writer);
}
else if (handshake.IsValidWebSocketRequest)
{
await SendNegotiationResponse(handshake, writer);
}
else
{
handshake.Response.Status = ((handshake.Response.Status != HttpStatusCode.SwitchingProtocols) ? handshake.Response.Status : HttpStatusCode.BadRequest);
await SendNegotiationErrorResponseAsync(writer, handshake.Response.Status);
}
}
private async Task ReadHttpRequestAsync(NetworkConnection clientStream, WebSocketHandshake handshake)
{
if (clientStream == null)
{
throw new ArgumentNullException("clientStream");
}
if (handshake == null)
{
throw new ArgumentNullException("handshake");
}
using StreamReader sr = new StreamReader(clientStream.AsStream(), Encoding.ASCII, detectEncodingFromByteOrderMarks: false, 1024, leaveOpen: true);
ParseGET(await sr.ReadLineAsync(), handshake);
string header;
while (!string.IsNullOrWhiteSpace(header = await sr.ReadLineAsync()))
{
handshake.Request.Headers.TryParseAndAdd(header);
}
ParseCookies(handshake);
}
private void ParseGET(string line, WebSocketHandshake handshake)
{
if (handshake == null)
{
throw new ArgumentNullException("handshake");
}
if (string.IsNullOrWhiteSpace(line))
{
throw new WebSocketException("Empty request line is received. Probably connection is closed.");
}
string[] array = line.Split(new char[1] { ' ' });
if (!line.StartsWith("GET", StringComparison.Ordinal))
{
throw new WebSocketException($"Invalid request method '{array.FirstOrDefault() ?? line}' while 'GET' is expected.");
}
handshake.Request.RequestUri = new Uri(array[1], UriKind.Relative);
string text = array[2];
handshake.Request.HttpVersion = (text.EndsWith("1.1") ? HttpVersion11 : HttpVersion10);
}
private async Task SendNegotiationResponse(WebSocketHandshake handshake, StreamWriter writer)
{
await writer.WriteAsync("HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n");
if (handshake.Response.Cookies.Count > 0)
{
foreach (object cookie in handshake.Response.Cookies)
{
await writer.WriteAsync("Set-Cookie: ");
await writer.WriteAsync(cookie.ToString());
await writer.WriteAsync("\r\n");
}
}
await writer.WriteAsync("Sec-WebSocket-Accept: ");
await writer.WriteAsync(handshake.ComputeHandshake());
if (handshake.Response.Headers.Contains(ResponseHeader.WebSocketProtocol))
{
await writer.WriteAsync("\r\nSec-WebSocket-Protocol: ");
await writer.WriteAsync(handshake.Response.Headers[ResponseHeader.WebSocketProtocol]);
}
WriteHandshakeCookies(handshake, writer);
await writer.WriteAsync("\r\n\r\n");
}
private static void WriteHandshakeCookies(WebSocketHandshake handshake, StreamWriter writer)
{
if (handshake == null)
{
throw new ArgumentNullException("handshake");
}
if (writer == null)
{
throw new ArgumentNullException("writer");
}
if (!handshake.Response.WebSocketExtensions.Any())
{
return;
}
bool flag = true;
bool flag2 = true;
writer.Write("\r\nSec-WebSocket-Extensions: ");
foreach (WebSocketExtension webSocketExtension in handshake.Response.WebSocketExtensions)
{
if (!flag)
{
writer.Write(",");
}
writer.Write(webSocketExtension.Name);
IEnumerable<WebSocketExtensionOption> enumerable = webSocketExtension.Options.Where((WebSocketExtensionOption x) => !x.ClientAvailableOption);
if (!webSocketExtension.Options.Any())
{
continue;
}
writer.Write(";");
foreach (WebSocketExtensionOption item in enumerable)
{
if (!flag2)
{
writer.Write(";");
}
writer.Write(item.Name);
if (item.Value != null)
{
writer.Write("=");
writer.Write(item.Value);
}
flag2 = false;
}
flag = false;
}
}
private async Task SendNegotiationErrorResponseAsync(StreamWriter writer, HttpStatusCode code)
{
if (writer == null)
{
throw new ArgumentNullException("writer");
}
await writer.WriteAsync($"HTTP/1.1 {(int)code} {HttpStatusDescription.Get(code)}\r\nConnection: close\r\n\r\n");
}
private async Task SendVersionNegotiationErrorResponse(StreamWriter writer)
{
await writer.WriteAsync("HTTP/1.1 426 Upgrade Required\r\nSec-WebSocket-Version: ");
bool first = true;
foreach (WebSocketFactory standard in factories)
{
if (!first)
{
await writer.WriteAsync(",");
}
first = false;
await writer.WriteAsync(standard.Version.ToString());
}
await writer.WriteAsync("\r\n\r\n");
}
private void ConsolidateObjectModel(WebSocketHandshake handshake)
{
if (handshake == null)
{
throw new ArgumentNullException("handshake");
}
ParseWebSocketProtocol(handshake);
ParseWebSocketExtensions(handshake);
}
private void ParseWebSocketProtocol(WebSocketHandshake handshake)
{
if (handshake == null)
{
throw new ArgumentNullException("handshake");
}
if (!options.SubProtocols.Any() || !handshake.Request.Headers.Contains(RequestHeader.WebSocketProtocol))
{
return;
}
foreach (string value in handshake.Request.Headers.GetValues(RequestHeader.WebSocketProtocol))
{
if (options.SubProtocols.Contains<string>(value, StringComparer.OrdinalIgnoreCase))
{
handshake.Response.Headers[ResponseHeader.WebSocketProtocol] = value;
break;
}
}
}
private void ParseWebSocketExtensions(WebSocketHandshake handshake)
{
if (handshake == null)
{
throw new ArgumentNullException("handshake");
}
List<WebSocketExtension> list = new List<WebSocketExtension>();
Headers<RequestHeader> headers = handshake.Request.Headers;
if (headers.Contains(RequestHeader.WebSocketExtensions))
{
foreach (string value in headers.GetValues(RequestHeader.WebSocketExtensions))
{
List<WebSocketExtensionOption> list2 = new List<WebSocketExtensionOption>();
string text = null;
foreach (KeyValuePair<string, string> item in HeadersHelper.SplitAndTrimKeyValue(value, ';', '=', StringSplitOptions.RemoveEmptyEntries))
{
if (text == null)
{
text = item.Value;
}
else if (string.IsNullOrEmpty(item.Key))
{
list2.Add(new WebSocketExtensionOption(item.Value, clientAvailableOption: true));
}
else
{
list2.Add(new WebSocketExtensionOption(item.Key, item.Value));
}
}
if (string.IsNullOrEmpty(text))
{
throw new WebSocketException("Wrong value '" + headers[RequestHeader.WebSocketExtensions] + "' of " + Headers<ResponseHeader>.GetHeaderName(ResponseHeader.WebSocketExtensions) + " header in request.");
}
list.Add(new WebSocketExtension(text, list2));
}
}
handshake.Request.SetExtensions(list);
}
private void ParseCookies(WebSocketHandshake handshake)
{
if (handshake == null)
{
throw new ArgumentNullException("handshake");
}
string domain = handshake.Request.Headers[RequestHeader.Host];
foreach (string value in handshake.Request.Headers.GetValues(RequestHeader.Cookie))
{
try
{
foreach (Cookie item in CookieParser.Parse(value))
{
item.Domain = domain;
item.Path = string.Empty;
handshake.Request.Cookies.Add(item);
}
}
catch (Exception ex)
{
throw new WebSocketException("Cannot parse cookie string: '" + value + "' because: " + ex.Message);
}
}
}
}
public sealed class WebSocketHttpRequest : IHttpRequest
{
public static readonly IPEndPoint NoAddress = new IPEndPoint(IPAddress.None, 0);
public EndPoint LocalEndPoint { get; internal set; }
public EndPoint RemoteEndPoint { get; internal set; }
public Uri RequestUri { get; internal set; }
public Version HttpVersion { get; internal set; }
public bool IsSecure { get; internal set; }
public CookieCollection Cookies { get; }
public Headers<RequestHeader> Headers { get; }
public IDictionary<string, object> Items { get; }
public HttpRequestDirection Direction { get; }
public IReadOnlyList<WebSocketExtension> WebSocketExtensions { get; private set; }
public WebSocketHttpRequest(HttpRequestDirection direction)
{
Headers = new Headers<RequestHeader>();
Cookies = new CookieCollection();
Items = new Dictionary<string, object>();
LocalEndPoint = NoAddress;
RemoteEndPoint = NoAddress;
Direction = direction;
}
internal void SetExtensions(List<WebSocketExtension> extensions)
{
if (extensions == null)
{
throw new ArgumentNullException("extensions");
}
WebSocketExtensions = new ReadOnlyCollection<WebSocketExtension>(extensions);
}
public override string ToString()
{
if (RequestUri != null)
{
return RequestUri.ToString();
}
return $"{LocalEndPoint}->{RemoteEndPoint}";
}
}
public sealed class WebSocketHttpResponse
{
public readonly CookieCollection Cookies;
public readonly Headers<ResponseHeader> Headers;
public HttpStatusCode Status;
public string StatusDescription;
public readonly List<WebSocketExtension> WebSocketExtensions;
public WebSocketHttpResponse()
{
Headers = new Headers<ResponseHeader>();
Cookies = new CookieCollection();
WebSocketExtensions = new List<WebSocketExtension>();
Status = HttpStatusCode.SwitchingProtocols;
StatusDescription = "Web Socket Protocol Handshake";
}
public void ThrowIfInvalid(string computedHandshake)
{
if (computedHandshake == null)
{
throw new ArgumentNullException("computedHandshake");
}
string b = Headers[ResponseHeader.Upgrade];
if (!string.Equals("websocket", b, StringComparison.OrdinalIgnoreCase))
{
throw new WebSocketException("Missing or wrong " + Headers<ResponseHeader>.GetHeaderName(ResponseHeader.Upgrade) + " header in response.");
}
if (!Headers.GetValues(ResponseHeader.Connection).Contains("Upgrade", StringComparison.OrdinalIgnoreCase))
{
throw new WebSocketException("Missing or wrong " + Headers<ResponseHeader>.GetHeaderName(ResponseHeader.Connection) + " header in response.");
}
string b2 = Headers[ResponseHeader.WebSocketAccept];
if (!string.Equals(computedHandshake, b2, StringComparison.OrdinalIgnoreCase))
{
throw new WebSocketException("Missing or wrong " + Headers<ResponseHeader>.GetHeaderName(ResponseHeader.WebSocketAccept) + " header in response.");
}
}
public override string ToString()
{
return $"{Status} {StatusDescription}";
}
}
public interface ILogger
{
bool IsDebugEnabled { get; }
bool IsWarningEnabled { get; }
bool IsErrorEnabled { get; }
void Debug(string message, Exception error = null);
void Warning(string message, Exception error = null);
void Error(string message, Exception error = null);
}
public sealed class NullLogger : ILogger
{
public static readonly NullLogger Instance = new NullLogger();
public bool IsDebugEnabled => false;
public bool IsWarningEnabled => false;
public bool IsErrorEnabled => false;
public void Debug(string message, Exception error = null)
{
}
public void Warning(string message, Exception error = null)
{
}
public void Error(string message, Exception error = null)
{
}
}
public enum PingMode
{
Manual,
LatencyControl,
BandwidthSaving
}
public abstract class WebSocketMessageReadStream : WebSocketMessageStream
{
public abstract WebSocketMessageType MessageType { get; }
public abstract WebSocketExtensionFlags Flags { get; }
public sealed override bool CanRead => true;
[Obsolete("Writing to the read stream is not allowed", true)]
public sealed override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
throw new NotSupportedException();
}
}
public abstract class WebSocketMessageStream : Stream
{
public override bool CanRead => false;
public sealed override bool CanSeek => false;
public override bool CanWrite => false;
public sealed override long Length
{
get
{
throw new NotSupportedException();
}
}
public sealed override long Position
{
get
{
throw new NotSupportedException();
}
set
{
throw new NotSupportedException();
}
}
internal abstract WebSocketListenerOptions Options { get; }
public override Task FlushAsync(CancellationToken cancellationToken)
{
return TaskHelper.CompletedTask;
}
public abstract Task CloseAsync();
public abstract override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken);
public abstract override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken);
public sealed override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
if (buffer == null)
{
throw new ArgumentNullException("buffer");
}
if (offset < 0 || offset > buffer.Length)
{
throw new ArgumentOutOfRangeException("offset");
}
if (count < 0 || offset + count > buffer.Length)
{
throw new ArgumentOutOfRangeException("count");
}
if (callback != null || state != null)
{
TaskCompletionSource<int> taskCompletionSource = new TaskCompletionSource<int>(state);
ReadAsync(buffer, offset, count, CancellationToken.None).PropagateResultTo(taskCompletionSource);
if (callback != null)
{
taskCompletionSource.Task.ContinueWith(delegate(Task<int> t, object s)
{
((AsyncCallback)s)(t);
}, callback, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}
return taskCompletionSource.Task;
}
return ReadAsync(buffer, offset, count, CancellationToken.None);
}
public sealed override int EndRead(IAsyncResult asyncResult)
{
return ((Task<int>)asyncResult).Result;
}
public sealed override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
if (buffer == null)
{
throw new ArgumentNullException("buffer");
}
if (offset < 0 || offset > buffer.Length)
{
throw new ArgumentOutOfRangeException("offset");
}
if (count < 0 || offset + count > buffer.Length)
{
throw new ArgumentOutOfRangeException("count");
}
if (callback != null || state != null)
{
TaskCompletionSource<bool> taskCompletionSource = new TaskCompletionSource<bool>(state);
WriteAsync(buffer, offset, count, CancellationToken.None).PropagateResultTo(taskCompletionSource);
if (callback != null)
{
taskCompletionSource.Task.ContinueWith(delegate(Task<bool> t, object s)
{
((AsyncCallback)s)(t);
}, callback, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}
return taskCompletionSource.Task;
}
return WriteAsync(buffer, offset, count, CancellationToken.None);
}
public sealed override void EndWrite(IAsyncResult asyncResult)
{
((Task)asyncResult).Wait();
}
public sealed override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public sealed override void SetLength(long value)
{
throw new NotSupportedException();
}
[Obsolete("Do not use synchronous IO operation on network streams. Use ReadAsync() instead.")]
public sealed override int ReadByte()
{
throw new NotSupportedException();
}
[Obsolete("Do not use synchronous IO operation on network streams. Use ReadAsync() instead.")]
public override int Read(byte[] buffer, int offset, int count)
{
return ReadAsync(buffer, offset, count, CancellationToken.None).Result;
}
[Obsolete("Do not use synchronous IO operation on network streams. Use WriteAsync() instead.")]
public sealed override void WriteByte(byte value)
{
throw new NotSupportedException();
}
[Obsolete("Do not use synchronous IO operation on network streams. Use WriteAsync() instead.")]
public override void Write(byte[] buffer, int offset, int count)
{
WriteAsync(buffer, offset, count).Wait();
}
[Obsolete("Do not use synchronous IO operation on network streams. Use FlushAsync() instead.")]
public override void Flush()
{
}
[Obsolete("Do not use synchronous IO operation on network streams. Use CloseAsync() instead.")]
public override void Close()
{
base.Close();
}
}
public abstract class WebSocketMessageWriteStream : WebSocketMessageStream
{
public sealed override bool CanWrite => true;
[NotNull]
public WebSocketExtensionFlags ExtensionFlags { get; }
protected WebSocketMessageWriteStream()
{
ExtensionFlags = new WebSocketExtensionFlags();
}
[Obsolete("Reading from the write stream is not allowed", true)]
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
throw new NotSupportedException();
}
public abstract Task WriteAndCloseAsync([NotNull] byte[] buffer, int offset, int count, CancellationToken cancellationToken);
}
internal static class SafeEnd
{
public static void Dispose<T>([CanBeNull] T disposable, ILogger log = null) where T : class, IDisposable
{
if (log == null)
{
log = NullLogger.Instance;
}
try
{
disposable?.Dispose();
}
catch (Exception error)
{
if (log.IsDebugEnabled)
{
log.Debug($"{typeof(T)} dispose cause error.", error);
}
}
}
public static void ReleaseSemaphore(SemaphoreSlim semaphore, ILogger log = null)
{
try
{
semaphore.Release();
}
catch (ObjectDisposedException)
{
}
catch (Exception error)
{
if (log != null && log.IsDebugEnabled)
{
log.Debug("Semaphore release cause error.", error);
}
}
}
}
[PublicAPI]
public abstract class WebSocket : IDisposable
{
[NotNull]
public WebSocketHttpRequest HttpRequest { get; }
[NotNull]
public WebSocketHttpResponse HttpResponse { get; }
public abstract bool IsConnected { get; }
public abstract EndPoint RemoteEndpoint { get; }
public abstract EndPoint LocalEndpoint { get; }
public abstract TimeSpan Latency { get; }
public abstract string SubProtocol { get; }
public abstract WebSocketCloseReason? CloseReason { get; }
protected WebSocket([NotNull] WebSocketHttpRequest request, [NotNull] WebSocketHttpResponse response)
{
if (request == null)
{
throw new ArgumentNullException("request");
}
if (response == null)
{
throw new ArgumentNullException("response");
}
HttpRequest = request;
HttpResponse = response;
}
[NotNull]
[ItemCanBeNull]
public abstract Task<WebSocketMessageReadStream> ReadMessageAsync(CancellationToken token);
[NotNull]
public abstract WebSocketMessageWriteStream CreateMessageWriter(WebSocketMessageType messageType);
public Task SendPingAsync()
{
return SendPingAsync(null, 0, 0);
}
public abstract Task SendPingAsync(byte[] data, int offset, int count);
public abstract Task CloseAsync();
public abstract Task CloseAsync(WebSocketCloseReason closeCode);
public abstract void Dispose();
public override string ToString()
{
return $"{GetType().Name}, remote: {RemoteEndpoint}, connected: {IsConnected}";
}
}
[PublicAPI]
public sealed class WebSocketClient
{
private const string WEB_SOCKET_HTTP_VERSION = "HTTP/1.1";
private readonly ILogger log;
private readonly AsyncConditionSource closeEvent;
private readonly CancellationTokenSource workCancellationSource;
private readonly WebSocketListenerOptions options;
private readonly ConcurrentDictionary<WebSocketHandshake, Task<WebSocket>> pendingRequests;
private readonly CancellationQueue negotiationsTimeoutQueue;
private readonly PingQueue pingQueue;
public bool HasPendingRequests => !pendingRequests.IsEmpty;
public WebSocketClient([NotNull] WebSocketListenerOptions options)
{
if (options == null)
{
throw new ArgumentNullException("options");
}
if (options.Standards.Count == 0)
{
throw new ArgumentException("Empty list of WebSocket standards.", "options");
}
options.CheckCoherence();
this.options = options.Clone();
this.options.SetUsed(isUsed: true);
if (this.options.NegotiationTimeout > TimeSpan.Zero)
{
negotiationsTimeoutQueue = new CancellationQueue(this.options.NegotiationTimeout);
}
if (this.options.PingMode != 0)
{
pingQueue = new PingQueue(options.PingInterval);
}
log = this.options.Logger;
closeEvent = new AsyncConditionSource(isSet: true)
{
ContinueOnCapturedContext = false
};
workCancellationSource = new CancellationTokenSource();
pendingRequests = new ConcurrentDictionary<WebSocketHandshake, Task<WebSocket>>();
if (this.options.BufferManager == null)
{
this.options.BufferManager = BufferManager.CreateBufferManager(this.options.SendBufferSize * 2 * 100, this.options.SendBufferSize * 2);
}
if (this.options.CertificateValidationHandler == null)
{
this.options.CertificateValidationHandler = ValidateRemoteCertificate;
}
}
private bool ValidateRemoteCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
{
return true;
}
if (log.IsWarningEnabled)
{
log.Warning($"Certificate validation error: {sslPolicyErrors}.");
}
return false;
}
public Task<WebSocket> ConnectAsync([NotNull] Uri address, CancellationToken cancellation = default(CancellationToken))
{
return ConnectAsync(address, null, cancellation);
}
public async Task<WebSocket> ConnectAsync([NotNull] Uri address, Headers<RequestHeader> requestHeaders = null, CancellationToken cancellation = default(CancellationToken))
{
_ = 1;
try
{
cancellation.ThrowIfCancellationRequested();
if (workCancellationSource.IsCancellationRequested)
{
throw new WebSocketException("Client is currently closing or closed.");
}
CancellationToken workCancellation = workCancellationSource.Token;
CancellationToken negotiationCancellation = negotiationsTimeoutQueue?.GetSubscriptionList().Token ?? CancellationToken.None;
if (cancellation.CanBeCanceled || workCancellation.CanBeCanceled || negotiationCancellation.CanBeCanceled)
{
cancellation = CancellationTokenSource.CreateLinkedTokenSource(cancellation, workCancellation, negotiationCancellation).Token;
}
WebSocketHttpRequest webSocketHttpRequest = new WebSocketHttpRequest(HttpRequestDirection.Outgoing)
{
RequestUri = address
};
if (requestHeaders != null)
{
webSocketHttpRequest.Headers.AddMany(requestHeaders);
}
WebSocketHandshake handshake = new WebSocketHandshake(webSocketHttpRequest);
Task<WebSocket> value = OpenConnectionAsync(handshake, cancellation);
pendingRequests.TryAdd(handshake, value);
WebSocket disposable = await value.IgnoreFaultOrCancellation().ConfigureAwait(continueOnCapturedContext: false);
if (!workCancellation.IsCancellationRequested && negotiationCancellation.IsCancellationRequested)
{
SafeEnd.Dispose(disposable, log);
throw new WebSocketException("Negotiation timeout.");
}
if (pendingRequests.TryRemove(handshake, out value) && workCancellationSource.IsCancellationRequested && pendingRequests.IsEmpty)
{
closeEvent.Set();
}
disposable = await value.ConfigureAwait(continueOnCapturedContext: false);
pingQueue?.GetSubscriptionList().Add(disposable);
return disposable;
}
catch (Exception exception) when (!(exception.Unwrap() is ThreadAbortException) && !(exception.Unwrap() is OperationCanceledException) && !(exception.Unwrap() is WebSocketException))
{
throw new WebSocketException($"An unknown error occurred while connection to '{address}'. More detailed information in inner exception.", exception.Unwrap());
}
}
public async Task CloseAsync()
{
workCancellationSource.Cancel(throwOnFirstException: false);
await closeEvent;
SafeEnd.Dispose(pingQueue, log);
SafeEnd.Dispose(negotiationsTimeoutQueue, log);
SafeEnd.Dispose(workCancellationSource, log);
options.SetUsed(isUsed: false);
}
private async Task<WebSocket> OpenConnectionAsync(WebSocketHandshake handshake, CancellationToken cancellation)
{
if (handshake == null)
{
throw new ArgumentNullException("handshake");
}
NetworkConnection connection = null;
WebSocket webSocket = null;
try
{
cancellation.ThrowIfCancellationRequested();
Uri requestUri = handshake.Request.RequestUri;
WebSocketTransport transport = null;
if (!options.Transports.TryGetWebSocketTransport(requestUri, out transport))
{
throw new WebSocketException($"Unable to find transport for '{requestUri}'. " + "Available transports are: " + string.Join(", ", options.Transports.SelectMany((WebSocketTransport t) => t.Schemes).Distinct()) + ".");
}
connection = await transport.ConnectAsync(requestUri, options, cancellation).ConfigureAwait(continueOnCapturedContext: false);
handshake.Request.IsSecure = transport.ShouldUseSsl(requestUri);
handshake.Request.LocalEndPoint = connection.LocalEndPoint ?? WebSocketHttpRequest.NoAddress;
handshake.Request.RemoteEndPoint = connection.RemoteEndPoint ?? WebSocketHttpRequest.NoAddress;
webSocket = await NegotiateRequestAsync(handshake, connection, cancellation).ConfigureAwait(continueOnCapturedContext: false);
return webSocket;
}
finally
{
if (webSocket == null)
{
SafeEnd.Dispose(connection, log);
}
}
}
private async Task<WebSocket> NegotiateRequestAsync(WebSocketHandshake handshake, NetworkConnection connection, CancellationToken cancellation)
{
if (handshake == null)
{
throw new ArgumentNullException("handshake");
}
if (connection == null)
{
throw new ArgumentNullException("connection");
}
cancellation.ThrowIfCancellationRequested();
Stream stream = connection.AsStream();
if (handshake.Request.IsSecure)
{
SslProtocols supportedSslProtocols = options.SupportedSslProtocols;
string dnsSafeHost = handshake.Request.RequestUri.DnsSafeHost;
SslStream secureStream = new SslStream(stream, leaveInnerStreamOpen: false, options.CertificateValidationHandler);
await secureStream.AuthenticateAsClientAsync(dnsSafeHost, null, supportedSslProtocols, checkCertificateRevocation: false).ConfigureAwait(continueOnCapturedContext: false);
connection = new SslNetworkConnection(secureStream, connection);
stream = secureStream;
}
handshake.Factory = options.Standards.GetLast();
await WriteRequestAsync(handshake, stream).ConfigureAwait(continueOnCapturedContext: false);
cancellation.ThrowIfCancellationRequested();
await ReadResponseAsync(handshake, stream).ConfigureAwait(continueOnCapturedContext: false);
cancellation.ThrowIfCancellationRequested();
if (await (options.HttpAuthenticationHandler?.Invoke(handshake.Request, handshake.Response) ?? Task.FromResult(result: false)).ConfigureAwait(continueOnCapturedContext: false))
{
throw new WebSocketException("HTTP authentication failed.");
}
return handshake.Factory.CreateWebSocket(connection, options, handshake.Request, handshake.Response, handshake.NegotiatedMessageExtensions);
}
private async Task WriteRequestAsync(WebSocketHandshake handshake, Stream stream)
{
Uri url = handshake.Request.RequestUri;
string value2 = handshake.GenerateClientNonce();
int largeBufferSize = options.BufferManager.LargeBufferSize;
using StreamWriter writer = new StreamWriter(stream, Encoding.ASCII, largeBufferSize, leaveOpen: true);
Headers<RequestHeader> requestHeaders = handshake.Request.Headers;
requestHeaders[RequestHeader.Host] = url.DnsSafeHost;
requestHeaders[RequestHeader.Upgrade] = "websocket";
requestHeaders[RequestHeader.Connection] = "keep-alive, Upgrade";
requestHeaders[RequestHeader.WebSocketKey] = value2;
requestHeaders[RequestHeader.WebSocketVersion] = handshake.Factory.Version.ToString();
requestHeaders[RequestHeader.CacheControl] = "no-cache";
requestHeaders[RequestHeader.Pragma] = "no-cache";
foreach (IWebSocketMessageExtension messageExtension in handshake.Factory.MessageExtensions)
{
requestHeaders.Add(RequestHeader.WebSocketExtensions, messageExtension.ToString());
}
string[] subProtocols = options.SubProtocols;
foreach (string value3 in subProtocols)
{
requestHeaders.Add(RequestHeader.WebSocketProtocol, value3);
}
writer.NewLine = "\r\n";
await writer.WriteAsync("GET ").ConfigureAwait(continueOnCapturedContext: false);
await writer.WriteAsync(url.PathAndQuery).ConfigureAwait(continueOnCapturedContext: false);
await writer.WriteLineAsync(" HTTP/1.1").ConfigureAwait(continueOnCapturedContext: false);
foreach (KeyValuePair<string, Headers<RequestHeader>.ValueCollection> item in requestHeaders)
{
string headerName = item.Key;
foreach (string value in item.Value)
{
await writer.WriteAsync(headerName).ConfigureAwait(continueOnCapturedContext: false);
await writer.WriteAsync(": ").ConfigureAwait(continueOnCapturedContext: false);
await writer.WriteLineAsync(value).ConfigureAwait(continueOnCapturedContext: false);
}
}
await writer.WriteLineAsync().ConfigureAwait(continueOnCapturedContext: false);
await writer.FlushAsync().ConfigureAwait(continueOnCapturedContext: false);
}
private async Task ReadResponseAsync(WebSocketHandshake handshake, Stream stream)
{
int largeBufferSize = options.BufferManager.LargeBufferSize;
using (StreamReader reader = new StreamReader(stream, Encoding.ASCII, detectEncodingFromByteOrderMarks: false, largeBufferSize, leaveOpen: true))
{
Headers<ResponseHeader> responseHeaders = handshake.Response.Headers;
string text = (await reader.ReadLineAsync().ConfigureAwait(continueOnCapturedContext: false)) ?? string.Empty;
if (!HttpHelper.TryParseHttpResponse(text, out handshake.Response.Status, out handshake.Response.StatusDescription))
{
if (string.IsNullOrEmpty(text))
{
throw new WebSocketException("Empty response. Probably connection is closed by remote party.");
}
throw new WebSocketException("Invalid handshake response: " + text + ".");
}
if (handshake.Response.Status != HttpStatusCode.SwitchingProtocols)
{
throw new WebSocketException("Invalid handshake response: " + text + ".");
}
string text2 = await reader.ReadLineAsync().ConfigureAwait(continueOnCapturedContext: false);
while (!string.IsNullOrEmpty(text2))
{
responseHeaders.TryParseAndAdd(text2);
text2 = await reader.ReadLineAsync().ConfigureAwait(continueOnCapturedContext: false);
}
handshake.Response.ThrowIfInvalid(handshake.ComputeHandshake());
}
ParseWebSocketExtensions(handshake);
SelectExtensions(handshake);
}
private void SelectExtensions(WebSocketHandshake handshake)
{
if (handshake == null)
{
throw new ArgumentNullException("handshake");
}
foreach (WebSocketExtension responseExtension in handshake.Response.WebSocketExtensions)
{
IWebSocketMessageExtension webSocketMessageExtension = handshake.Factory.MessageExtensions.SingleOrDefault((IWebSocketMessageExtension x) => x.Name.Equals(responseExtension.Name, StringComparison.OrdinalIgnoreCase));
if (webSocketMessageExtension != null && webSocketMessageExtension.TryNegotiate(handshake.Request, out var _, out var context))
{
handshake.NegotiatedMessageExtensions.Add(context);
}
}
}
private void ParseWebSocketExtensions(WebSocketHandshake handshake)
{
if (handshake == null)
{
throw new ArgumentNullException("handshake");
}
Headers<ResponseHeader> headers = handshake.Response.Headers;
if (!headers.Contains(ResponseHeader.WebSocketExtensions))
{
return;
}
foreach (string value in headers.GetValues(ResponseHeader.WebSocketExtensions))
{
List<WebSocketExtensionOption> list = new List<WebSocketExtensionOption>();
string text = null;
foreach (KeyValuePair<string, string> item in HeadersHelper.SplitAndTrimKeyValue(value, ';', '=', StringSplitOptions.RemoveEmptyEntries))
{
if (text == null)
{
text = item.Value;
}
else if (string.IsNullOrEmpty(item.Key))
{
list.Add(new WebSocketExtensionOption(item.Value, clientAvailableOption: true));
}
else
{
list.Add(new WebSocketExtensionOption(item.Key, item.Value));
}
}
if (string.IsNullOrEmpty(text))
{
throw new WebSocketException("Wrong value '" + headers[ResponseHeader.WebSocketExtensions] + "' of " + Headers<ResponseHeader>.GetHeaderName(ResponseHeader.WebSocketExtensions) + " header in request.");
}
handshake.Response.WebSocketExtensions.Add(new WebSocketExtension(text, list));
}
}
}
public enum WebSocketCloseReason : short
{
NormalClose = 1000,
GoingAway = 1001,
ProtocolError = 1002,
UnacceptableDataType = 1003,
InvalidData = 1007,
MessageViolatesPolicy = 1008,
MessageToLarge = 1009,
ExtensionRequired = 1010,
UnexpectedCondition = 1011,
TLSFailure = 105
}
public class WebSocketException : Exception
{
public WebSocketException(string message)
: base(message)
{
}
public WebSocketException(string message, Exception inner)
: base(message, inner)
{
}
}
[PublicAPI]
public abstract class WebSocketFactory
{
public abstract short Version { get; }
public WebSocketMessageExtensionCollection MessageExtensions { get; private set; }
protected WebSocketFactory()
{
MessageExtensions = new WebSocketMessageExtensionCollection();
}
public abstract WebSocket CreateWebSocket(NetworkConnection networkConnection, WebSocketListenerOptions options, WebSocketHttpRequest httpRequest, WebSocketHttpResponse httpResponse, List<IWebSocketMessageExtensionContext> negotiatedExtensions);
public virtual WebSocketFactory Clone()
{
WebSocketFactory webSocketFactory = (WebSocketFactory)MemberwiseClone();
webSocketFactory.MessageExtensions = new WebSocketMessageExtensionCollection();
foreach (IWebSocketMessageExtension messageExtension in MessageExtensions)
{
webSocketFactory.MessageExtensions.Add(messageExtension.Clone());
}
return webSocketFactory;
}
}
public sealed class WebSocketListener : IDisposable
{
private const int STATE_STOPPED = 0;
private const int STATE_STARTING = 1;
private const int STATE_STARTED = 2;
private const int STATE_STOPPING = 3;
private const int STATE_DISPOSED = 5;
private static readonly Listener[] EmptyListeners = new Listener[0];
private static readonly EndPoint[] EmptyEndPoints = new EndPoint[0];
private readonly ILogger log;
private readonly HttpNegotiationQueue negotiationQueue;
private readonly WebSocketListenerOptions options;
private readonly Uri[] listenEndPoints;
private volatile AsyncConditionSource stopConditionSource;
private volatile Listener[] listeners;
private volatile EndPoint[] localEndPoints;
private volatile int state;
public bool IsStarted => state == 2;
public IReadOnlyCollection<EndPoint> LocalEndpoints => (IReadOnlyCollection<EndPoint>)(object)localEndPoints;
public WebSocketListener(IPEndPoint endpoint)
: this(endpoint, new WebSocketListenerOptions())
{
}
public WebSocketListener(IPEndPoint endpoint, WebSocketListenerOptions options)
: this(new Uri[1]
{
new Uri("tcp://" + endpoint)
}, options)
{
}
public WebSocketListener(Uri[] listenEndPoints, WebSocketListenerOptions options)
{
if (listenEndPoints == null)
{
throw new ArgumentNullException("listenEndPoints");
}
if (listenEndPoints.Length == 0)
{
throw new ArgumentException("At least one prefix should be specified.", "listenEndPoints");
}
if (listenEndPoints.Any((Uri p) => p == null))
{
throw new ArgumentException("Null objects passed in array.", "listenEndPoints");
}
if (options == null)
{
throw new ArgumentNullException("options");
}
options.CheckCoherence();
this.options = options.Clone();
if (this.options.BufferManager == null)
{
this.options.BufferManager = BufferManager.CreateBufferManager(this.options.SendBufferSize * 100, this.options.SendBufferSize);
}
if (this.options.Logger == null)
{
this.options.Logger = NullLogger.Instance;
}
log = this.options.Logger;
listeners = EmptyListeners;
localEndPoints = EmptyEndPoints;
this.listenEndPoints = listenEndPoints;
negotiationQueue = new HttpNegotiationQueue(options.Standards, options.ConnectionExtensions, this.options);
}
public async Task StartAsync()
{
if (options.Standards.Count <= 0)
{
throw new WebSocketException("There are no WebSocket standards. Please, register standards using WebSocketListenerOptions.Standards.");
}
if (options.Transports.Count <= 0)
{
throw new WebSocketException("There are no WebSocket transports. Please, register transports using WebSocketListenerOptions.Transports.");
}
if (Interlocked.CompareExchange(ref state, 1, 0) != 0)
{
throw new WebSocketException("Failed to start listener from current state. Maybe it is disposed or already started.");
}
options.SetUsed(isUsed: true);
Listener[] listeners = null;
try
{
if (log.IsDebugEnabled)
{
log.Debug("WebSocketListener is starting.");
}
Tuple<Uri, WebSocketTransport>[] endPoints = new Tuple<Uri, WebSocketTransport>[listenEndPoints.Length];
for (int j = 0; j < listenEndPoints.Length; j++)
{
Uri uri = listenEndPoints[j];
WebSocketTransport transport = null;
if (!options.Transports.TryGetWebSocketTransport(uri, out transport))
{
string arg = string.Join(", ", options.Transports.SelectMany((WebSocketTransport t) => t.Schemes).Distinct());
throw new WebSocketException($"Unable to find transport for '{uri}'. Available transports are: {arg}.");
}
endPoints[j] = Tuple.Create(uri, transport);
}
listeners = new Listener[endPoints.Length];
for (int i = 0; i < endPoints.Length; i++)
{
Listener[] array = listeners;
int num = i;
array[num] = await endPoints[i].Item2.ListenAsync(endPoints[i].Item1, options).ConfigureAwait(continueOnCapturedContext: false);
}
this.listeners = listeners;
localEndPoints = this.listeners.SelectMany((Listener l) => l.LocalEndpoints).ToArray();
stopConditionSource = new AsyncConditionSource(isSet: true)
{
ContinueOnCapturedContext = false
};
if (Interlocked.CompareExchange(ref state, 2, 1) != 1)
{
throw new WebSocketException("Failed to start listener from current state. Maybe it is disposed.");
}
AcceptConnectionsAsync().LogFault(log, null, "StartAsync", "D:\\dev\\projects\\vtortola.WebSocketListener\\vtortola.WebSockets\\WebSocketListener.cs", 117);
if (log.IsDebugEnabled)
{
log.Debug("WebSocketListener is started.");
}
listeners = null;
}
catch
{
options.SetUsed(isUsed: false);
throw;
}
finally
{
Interlocked.CompareExchange(ref state, 0, 1);
if (listeners != null)
{
Listener[] array2 = listeners;
for (int k = 0; k < array2.Length; k++)
{
SafeEnd.Dispose(array2[k]);
}
this.listeners = EmptyListeners;
localEndPoints = EmptyEndPoints;
stopConditionSource = null;
}
}
}
public async Task StopAsync()
{
if (Interlocked.CompareExchange(ref state, 3, 2) != 2)
{
throw new WebSocketException("Failed to stop listener from current state. Maybe it is disposed or not started.");
}
options.SetUsed(isUsed: false);
AsyncConditionSource asyncConditionSource = stopConditionSource;
if (log.IsDebugEnabled)
{
log.Debug("WebSocketListener is stopping.");
}
localEndPoints = EmptyEndPoints;
Listener[] array = Interlocked.Exchange(ref listeners, EmptyListeners);
for (int i = 0; i < array.Length; i++)
{
SafeEnd.Dispose(array[i], log);
}
if (asyncConditionSource != null)
{
await asyncConditionSource;
}
if (Interlocked.CompareExchange(ref state, 0, 3) != 3)
{
throw new WebSocketException("Failed to stop listener from current state. Maybe it is disposed.");
}
if (log.IsDebugEnabled)
{
log.Debug("WebSocketListener is stopped.");
}
}
private async Task AcceptConnectionsAsync()
{
await Task.Yield();
Listener[] listeners = this.listeners;
Task<NetworkConnection>[] acceptTasks = new Task<NetworkConnection>[listeners.Length];
int acceptOffset = 0;
try
{
while (IsStarted)
{
for (int i = 0; i < acceptTasks.Length; i++)
{
if (acceptTasks[i] == null)
{
try
{
acceptTasks[i] = listeners[i].AcceptConnectionAsync();
}
catch (Exception ex) when (!(ex is ThreadAbortException))
{
acceptTasks[i] = TaskHelper.FailedTask<NetworkConnection>(ex);
}
}
}
await Task.WhenAny(acceptTasks).ConfigureAwait(continueOnCapturedContext: false);
if (acceptOffset == 65535)
{
acceptOffset = 0;
}
acceptOffset++;
for (int j = 0; j < acceptTasks.Length; j++)
{
int num = (acceptOffset + j) % acceptTasks.Length;
Task<NetworkConnection> task = acceptTasks[num];
if (task != null && task.IsCompleted)
{
acceptTasks[num] = null;
AcceptNewConnection(task, listeners[num]);
}
}
}
}
finally
{
CleanupPendingConnections(acceptTasks);
}
}
private void CleanupPendingConnections(Task<NetworkConnection>[] acceptTasks)
{
if (acceptTasks == null)
{
throw new ArgumentNullException("acceptTasks");
}
for (int i = 0; i < acceptTasks.Length; i++)
{
acceptTasks[i]?.ContinueWith(delegate(Task<NetworkConnection> t)
{
SafeEnd.Dispose(t.Result, log);
}, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Current).LogFault(log, null, "CleanupPendingConnections", "D:\\dev\\projects\\vtortola.WebSocketListener\\vtortola.WebSockets\\WebSocketListener.cs", 234);
}
Array.Clear(acceptTasks, 0, acceptTasks.Length);
}
private void AcceptNewConnection(Task<NetworkConnection> acceptTask, Listener listener)
{
if (acceptTask == null)
{
throw new ArgumentNullException("acceptTask");
}
if (listener == null)
{
throw new ArgumentNullException("listener");
}
Exception ex = acceptTask.Exception.Unwrap();
if (acceptTask.Status != TaskStatus.RanToCompletion)
{
if (log.IsDebugEnabled && ex != null && !(ex is OperationCanceledException))
{
log.Debug($"Accept from '{listener}' has failed.", ex);
}
return;
}
NetworkConnection result = acceptTask.Result;
if (log.IsDebugEnabled)
{
log.Debug($"New client from '{result}' is connected.");
}
negotiationQueue.Queue(result);
}
public async Task<WebSocket> AcceptWebSocketAsync(CancellationToken token)
{
try
{
WebSocketNegotiationResult webSocketNegotiationResult = await negotiationQueue.DequeueAsync(token).ConfigureAwait(continueOnCapturedContext: false);
if (webSocketNegotiationResult.Error != null)
{
if (log.IsDebugEnabled && !(webSocketNegotiationResult.Error.SourceException.Unwrap() is OperationCanceledException))
{
log.Debug("AcceptWebSocketAsync is complete with error.", webSocketNegotiationResult.Error.SourceException);
}
webSocketNegotiationResult.Error.Throw();
return null;
}
return webSocketNegotiationResult.Result;
}
catch (OperationCanceledException)
{
return null;
}
}
public void Dispose()
{
if (Interlocked.Exchange(ref state, 5) != 5)
{
stopConditionSource?.Set();
localEndPoints = EmptyEndPoints;
Listener[] array = Interlocked.Exchange(ref listeners, EmptyListeners);
for (int i = 0; i < array.Length; i++)
{
SafeEnd.Dispose(array[i], log);
}
SafeEnd.Dispose(negotiationQueue, log);
}
}
}
[PublicAPI]
public sealed class WebSocketListenerOptions
{
public const int DEFAULT_SEND_BUFFER_SIZE = 8192;
public static readonly string[] NoSubProtocols = new string[0];
[NotNull]
public WebSocketTransportCollection Transports { get; private set; }
[NotNull]
public WebSocketFactoryCollection Standards { get; private set; }
[NotNull]
public WebSocketConnectionExtensionCollection ConnectionExtensions { get; private set; }
public TimeSpan PingTimeout { get; set; }
public TimeSpan PingInterval
{
get
{
if (!(PingTimeout > TimeSpan.Zero))
{
return TimeSpan.FromSeconds(5.0);
}
return TimeSpan.FromTicks(PingTimeout.Ticks / 3);
}
}
public int NegotiationQueueCapacity { get; set; }
public int ParallelNegotiations { get; set; }
public TimeSpan NegotiationTimeout { get; set; }
public int SendBufferSize { get; set; }
public string[] SubProtocols { get; set; }
public BufferManager BufferManager { get; set; }
public HttpAuthenticationCallback HttpAuthenticationHandler { get; set; }
public RemoteCertificateValidationCallback CertificateValidationHandler { get; set; }
public SslProtocols SupportedSslProtocols { get; set; }
public PingMode PingMode { get; set; }
public IHttpFallback HttpFallback { get; set; }
public ILogger Logger { get; set; }
public WebSocketListenerOptions()
{
PingTimeout = TimeSpan.FromSeconds(5.0);
Transports = new WebSocketTransportCollection();
Standards = new WebSocketFactoryCollection();
ConnectionExtensions = new WebSocketConnectionExtensionCollection();
NegotiationQueueCapacity = Environment.ProcessorCount * 10;
ParallelNegotiations = Environment.ProcessorCount * 2;
NegotiationTimeout = TimeSpan.FromSeconds(5.0);
SendBufferSize = 8192;
SubProtocols = NoSubProtocols;
HttpAuthenticationHandler = null;
CertificateValidationHandler = null;
PingMode = PingMode.LatencyControl;
SupportedSslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
Logger = NullLogger.Instance;
}
public void CheckCoherence()
{
if (PingTimeout <= TimeSpan.Zero)
{
PingTimeout = Timeout.InfiniteTimeSpan;
}
if (PingTimeout <= TimeSpan.FromSeconds(1.0))
{
PingTimeout = TimeSpan.FromSeconds(1.0);
}
if (NegotiationQueueCapacity < 0)
{
throw new WebSocketException(string.Format("{0} must be 0 or more. Actual value: {1}", "NegotiationQueueCapacity", NegotiationQueueCapacity));
}
if (ParallelNegotiations < 1)
{
throw new WebSocketException(string.Format("{0} cannot be less than 1. Actual value: {1}", "ParallelNegotiations", ParallelNegotiations));
}
if (NegotiationTimeout == TimeSpan.Zero)
{
NegotiationTimeout = Timeout.InfiniteTimeSpan;
}
if (SendBufferSize < 1024)
{
t
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
[assembly: AssemblyCompany("Microsoft Corporation")]
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyFileVersion("4.0.0.0")]
[assembly: AssemblyInformationalVersion("4.0.0.0")]
[assembly: AssemblyTitle("System.Runtime.CompilerServices.Unsafe")]
[assembly: AssemblyDescription("System.Runtime.CompilerServices.Unsafe")]
[assembly: AssemblyMetadata(".NETFrameworkAssembly", "")]
[assembly: AssemblyMetadata("Serviceable", "True")]
[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")]
[assembly: AssemblyProduct("Microsoft® .NET Framework")]
[assembly: CLSCompliant(false)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyVersion("4.0.4.1")]
namespace System.Runtime.CompilerServices
{
public static class Unsafe
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public unsafe static T Read<T>(void* source)
{
return Unsafe.Read<T>(source);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public unsafe static T ReadUnaligned<T>(void* source)
{
return Unsafe.ReadUnaligned<T>(source);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public static T ReadUnaligned<T>(ref byte source)
{
return Unsafe.ReadUnaligned<T>(ref source);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public unsafe static void Write<T>(void* destination, T value)
{
Unsafe.Write(destination, value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public unsafe static void WriteUnaligned<T>(void* destination, T value)
{
Unsafe.WriteUnaligned(destination, value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public static void WriteUnaligned<T>(ref byte destination, T value)
{
Unsafe.WriteUnaligned(ref destination, value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public unsafe static void Copy<T>(void* destination, ref T source)
{
Unsafe.Write(destination, source);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public unsafe static void Copy<T>(ref T destination, void* source)
{
destination = Unsafe.Read<T>(source);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public unsafe static void* AsPointer<T>(ref T value)
{
return Unsafe.AsPointer(ref value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public static int SizeOf<T>()
{
return Unsafe.SizeOf<T>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public unsafe static void CopyBlock(void* destination, void* source, uint byteCount)
{
// IL cpblk instruction
Unsafe.CopyBlock(destination, source, byteCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public static void CopyBlock(ref byte destination, ref byte source, uint byteCount)
{
// IL cpblk instruction
Unsafe.CopyBlock(ref destination, ref source, byteCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public unsafe static void CopyBlockUnaligned(void* destination, void* source, uint byteCount)
{
// IL cpblk instruction
Unsafe.CopyBlockUnaligned(destination, source, byteCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public static void CopyBlockUnaligned(ref byte destination, ref byte source, uint byteCount)
{
// IL cpblk instruction
Unsafe.CopyBlockUnaligned(ref destination, ref source, byteCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public unsafe static void InitBlock(void* startAddress, byte value, uint byteCount)
{
// IL initblk instruction
Unsafe.InitBlock(startAddress, value, byteCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public static void InitBlock(ref byte startAddress, byte value, uint byteCount)
{
// IL initblk instruction
Unsafe.InitBlock(ref startAddress, value, byteCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public unsafe static void InitBlockUnaligned(void* startAddress, byte value, uint byteCount)
{
// IL initblk instruction
Unsafe.InitBlockUnaligned(startAddress, value, byteCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public static void InitBlockUnaligned(ref byte startAddress, byte value, uint byteCount)
{
// IL initblk instruction
Unsafe.InitBlockUnaligned(ref startAddress, value, byteCount);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public static T As<T>(object o) where T : class
{
return (T)o;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public unsafe static ref T AsRef<T>(void* source)
{
return ref *(T*)source;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public static ref T AsRef<T>(in T source)
{
return ref source;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public static ref TTo As<TFrom, TTo>(ref TFrom source)
{
return ref Unsafe.As<TFrom, TTo>(ref source);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public static ref T Add<T>(ref T source, int elementOffset)
{
return ref Unsafe.Add(ref source, elementOffset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public unsafe static void* Add<T>(void* source, int elementOffset)
{
return (byte*)source + (nint)elementOffset * (nint)Unsafe.SizeOf<T>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public static ref T Add<T>(ref T source, IntPtr elementOffset)
{
return ref Unsafe.Add(ref source, elementOffset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public static ref T AddByteOffset<T>(ref T source, IntPtr byteOffset)
{
return ref Unsafe.AddByteOffset(ref source, byteOffset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public static ref T Subtract<T>(ref T source, int elementOffset)
{
return ref Unsafe.Subtract(ref source, elementOffset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public unsafe static void* Subtract<T>(void* source, int elementOffset)
{
return (byte*)source - (nint)elementOffset * (nint)Unsafe.SizeOf<T>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public static ref T Subtract<T>(ref T source, IntPtr elementOffset)
{
return ref Unsafe.Subtract(ref source, elementOffset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public static ref T SubtractByteOffset<T>(ref T source, IntPtr byteOffset)
{
return ref Unsafe.SubtractByteOffset(ref source, byteOffset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public static IntPtr ByteOffset<T>(ref T origin, ref T target)
{
return Unsafe.ByteOffset(target: ref target, origin: ref origin);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public static bool AreSame<T>(ref T left, ref T right)
{
return Unsafe.AreSame(ref left, ref right);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public static bool IsAddressGreaterThan<T>(ref T left, ref T right)
{
return Unsafe.IsAddressGreaterThan(ref left, ref right);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Runtime.Versioning.NonVersionable]
public static bool IsAddressLessThan<T>(ref T left, ref T right)
{
return Unsafe.IsAddressLessThan(ref left, ref right);
}
}
}
namespace System.Runtime.Versioning
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
internal sealed class NonVersionableAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
internal sealed class IsReadOnlyAttribute : Attribute
{
}
}
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Sources;
using FxResources.System.Threading.Channels;
using Internal;
using Microsoft.CodeAnalysis;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyMetadata(".NETFrameworkAssembly", "")]
[assembly: AssemblyMetadata("Serviceable", "True")]
[assembly: AssemblyMetadata("PreferInbox", "True")]
[assembly: AssemblyDefaultAlias("System.Threading.Channels")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: CLSCompliant(true)]
[assembly: AssemblyMetadata("IsTrimmable", "True")]
[assembly: DefaultDllImportSearchPaths(DllImportSearchPath.System32 | DllImportSearchPath.AssemblyDirectory)]
[assembly: AssemblyCompany("Microsoft Corporation")]
[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")]
[assembly: AssemblyDescription("Provides types for passing data between producers and consumers.\r\n\r\nCommonly Used Types:\r\nSystem.Threading.Channel\r\nSystem.Threading.Channel<T>")]
[assembly: AssemblyFileVersion("7.0.22.51805")]
[assembly: AssemblyInformationalVersion("7.0.0+d099f075e45d2aa6007a22b71b45a08758559f80")]
[assembly: AssemblyProduct("Microsoft® .NET")]
[assembly: AssemblyTitle("System.Threading.Channels")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/dotnet/runtime")]
[assembly: AssemblyVersion("7.0.0.0")]
[module: RefSafetyRules(11)]
[module: System.Runtime.CompilerServices.NullablePublicOnly(false)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class NullablePublicOnlyAttribute : Attribute
{
public readonly bool IncludesInternals;
public NullablePublicOnlyAttribute(bool P_0)
{
IncludesInternals = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace FxResources.System.Threading.Channels
{
internal static class SR
{
}
}
namespace Internal
{
internal static class PaddingHelpers
{
internal const int CACHE_LINE_SIZE = 128;
}
[StructLayout(LayoutKind.Explicit, Size = 124)]
internal struct PaddingFor32
{
}
}
namespace System
{
[StructLayout(LayoutKind.Sequential, Size = 1)]
internal struct VoidResult
{
}
internal static class SR
{
private static readonly bool s_usingResourceKeys = AppContext.TryGetSwitch("System.Resources.UseSystemResourceKeys", out var isEnabled) && isEnabled;
private static ResourceManager s_resourceManager;
internal static ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new ResourceManager(typeof(SR)));
internal static string ChannelClosedException_DefaultMessage => GetResourceString("ChannelClosedException_DefaultMessage");
internal static string InvalidOperation_IncompleteAsyncOperation => GetResourceString("InvalidOperation_IncompleteAsyncOperation");
internal static string InvalidOperation_MultipleContinuations => GetResourceString("InvalidOperation_MultipleContinuations");
internal static string InvalidOperation_IncorrectToken => GetResourceString("InvalidOperation_IncorrectToken");
private static bool UsingResourceKeys()
{
return s_usingResourceKeys;
}
internal static string GetResourceString(string resourceKey)
{
if (UsingResourceKeys())
{
return resourceKey;
}
string result = null;
try
{
result = ResourceManager.GetString(resourceKey);
}
catch (MissingManifestResourceException)
{
}
return result;
}
internal static string GetResourceString(string resourceKey, string defaultString)
{
string resourceString = GetResourceString(resourceKey);
if (!(resourceKey == resourceString) && resourceString != null)
{
return resourceString;
}
return defaultString;
}
internal static string Format(string resourceFormat, object p1)
{
if (UsingResourceKeys())
{
return string.Join(", ", resourceFormat, p1);
}
return string.Format(resourceFormat, p1);
}
internal static string Format(string resourceFormat, object p1, object p2)
{
if (UsingResourceKeys())
{
return string.Join(", ", resourceFormat, p1, p2);
}
return string.Format(resourceFormat, p1, p2);
}
internal static string Format(string resourceFormat, object p1, object p2, object p3)
{
if (UsingResourceKeys())
{
return string.Join(", ", resourceFormat, p1, p2, p3);
}
return string.Format(resourceFormat, p1, p2, p3);
}
internal static string Format(string resourceFormat, params object[] args)
{
if (args != null)
{
if (UsingResourceKeys())
{
return resourceFormat + ", " + string.Join(", ", args);
}
return string.Format(resourceFormat, args);
}
return resourceFormat;
}
internal static string Format(IFormatProvider provider, string resourceFormat, object p1)
{
if (UsingResourceKeys())
{
return string.Join(", ", resourceFormat, p1);
}
return string.Format(provider, resourceFormat, p1);
}
internal static string Format(IFormatProvider provider, string resourceFormat, object p1, object p2)
{
if (UsingResourceKeys())
{
return string.Join(", ", resourceFormat, p1, p2);
}
return string.Format(provider, resourceFormat, p1, p2);
}
internal static string Format(IFormatProvider provider, string resourceFormat, object p1, object p2, object p3)
{
if (UsingResourceKeys())
{
return string.Join(", ", resourceFormat, p1, p2, p3);
}
return string.Format(provider, resourceFormat, p1, p2, p3);
}
internal static string Format(IFormatProvider provider, string resourceFormat, params object[] args)
{
if (args != null)
{
if (UsingResourceKeys())
{
return resourceFormat + ", " + string.Join(", ", args);
}
return string.Format(provider, resourceFormat, args);
}
return resourceFormat;
}
}
}
namespace System.Diagnostics.CodeAnalysis
{
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)]
internal sealed class AllowNullAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)]
internal sealed class DisallowNullAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue, Inherited = false)]
internal sealed class MaybeNullAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue, Inherited = false)]
internal sealed class NotNullAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
internal sealed class MaybeNullWhenAttribute : Attribute
{
public bool ReturnValue { get; }
public MaybeNullWhenAttribute(bool returnValue)
{
ReturnValue = returnValue;
}
}
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
internal sealed class NotNullWhenAttribute : Attribute
{
public bool ReturnValue { get; }
public NotNullWhenAttribute(bool returnValue)
{
ReturnValue = returnValue;
}
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)]
internal sealed class NotNullIfNotNullAttribute : Attribute
{
public string ParameterName { get; }
public NotNullIfNotNullAttribute(string parameterName)
{
ParameterName = parameterName;
}
}
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
internal sealed class DoesNotReturnAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
internal sealed class DoesNotReturnIfAttribute : Attribute
{
public bool ParameterValue { get; }
public DoesNotReturnIfAttribute(bool parameterValue)
{
ParameterValue = parameterValue;
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
internal sealed class MemberNotNullAttribute : Attribute
{
public string[] Members { get; }
public MemberNotNullAttribute(string member)
{
Members = new string[1] { member };
}
public MemberNotNullAttribute(params string[] members)
{
Members = members;
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
internal sealed class MemberNotNullWhenAttribute : Attribute
{
public bool ReturnValue { get; }
public string[] Members { get; }
public MemberNotNullWhenAttribute(bool returnValue, string member)
{
ReturnValue = returnValue;
Members = new string[1] { member };
}
public MemberNotNullWhenAttribute(bool returnValue, params string[] members)
{
ReturnValue = returnValue;
Members = members;
}
}
}
namespace System.Runtime.InteropServices
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
internal sealed class LibraryImportAttribute : Attribute
{
public string LibraryName { get; }
public string EntryPoint { get; set; }
public StringMarshalling StringMarshalling { get; set; }
public Type StringMarshallingCustomType { get; set; }
public bool SetLastError { get; set; }
public LibraryImportAttribute(string libraryName)
{
LibraryName = libraryName;
}
}
internal enum StringMarshalling
{
Custom,
Utf8,
Utf16
}
}
namespace System.Collections.Generic
{
[DebuggerDisplay("Count = {_size}")]
internal sealed class Deque<T>
{
private T[] _array = Array.Empty<T>();
private int _head;
private int _tail;
private int _size;
public int Count => _size;
public bool IsEmpty => _size == 0;
public void EnqueueTail(T item)
{
if (_size == _array.Length)
{
Grow();
}
_array[_tail] = item;
if (++_tail == _array.Length)
{
_tail = 0;
}
_size++;
}
public T DequeueHead()
{
T result = _array[_head];
_array[_head] = default(T);
if (++_head == _array.Length)
{
_head = 0;
}
_size--;
return result;
}
public T PeekHead()
{
return _array[_head];
}
public T PeekTail()
{
int num = _tail - 1;
if (num == -1)
{
num = _array.Length - 1;
}
return _array[num];
}
public T DequeueTail()
{
if (--_tail == -1)
{
_tail = _array.Length - 1;
}
T result = _array[_tail];
_array[_tail] = default(T);
_size--;
return result;
}
public IEnumerator<T> GetEnumerator()
{
int pos = _head;
int count = _size;
while (count-- > 0)
{
yield return _array[pos];
pos = (pos + 1) % _array.Length;
}
}
private void Grow()
{
int num = (int)((long)_array.Length * 2L);
if (num < _array.Length + 4)
{
num = _array.Length + 4;
}
T[] array = new T[num];
if (_head == 0)
{
Array.Copy(_array, array, _size);
}
else
{
Array.Copy(_array, _head, array, 0, _array.Length - _head);
Array.Copy(_array, 0, array, _array.Length - _head, _tail);
}
_array = array;
_head = 0;
_tail = _size;
}
}
}
namespace System.Collections.Concurrent
{
[DebuggerDisplay("Count = {Count}")]
[DebuggerTypeProxy(typeof(SingleProducerSingleConsumerQueue<>.SingleProducerSingleConsumerQueue_DebugView))]
internal sealed class SingleProducerSingleConsumerQueue<T> : IEnumerable<T>, IEnumerable
{
[StructLayout(LayoutKind.Sequential)]
private sealed class Segment
{
internal Segment _next;
internal readonly T[] _array;
internal SegmentState _state;
internal Segment(int size)
{
_array = new T[size];
}
}
private struct SegmentState
{
internal Internal.PaddingFor32 _pad0;
internal volatile int _first;
internal int _lastCopy;
internal Internal.PaddingFor32 _pad1;
internal int _firstCopy;
internal volatile int _last;
internal Internal.PaddingFor32 _pad2;
}
private sealed class SingleProducerSingleConsumerQueue_DebugView
{
private readonly SingleProducerSingleConsumerQueue<T> _queue;
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public T[] Items => new List<T>(_queue).ToArray();
public SingleProducerSingleConsumerQueue_DebugView(SingleProducerSingleConsumerQueue<T> queue)
{
_queue = queue;
}
}
private const int InitialSegmentSize = 32;
private const int MaxSegmentSize = 16777216;
private volatile Segment _head;
private volatile Segment _tail;
public bool IsEmpty
{
get
{
Segment head = _head;
if (head._state._first != head._state._lastCopy)
{
return false;
}
if (head._state._first != head._state._last)
{
return false;
}
return head._next == null;
}
}
internal int Count
{
get
{
int num = 0;
for (Segment segment = _head; segment != null; segment = segment._next)
{
int num2 = segment._array.Length;
int first;
int last;
do
{
first = segment._state._first;
last = segment._state._last;
}
while (first != segment._state._first);
num += (last - first) & (num2 - 1);
}
return num;
}
}
public SingleProducerSingleConsumerQueue()
{
_head = (_tail = new Segment(32));
}
public void Enqueue(T item)
{
Segment segment = _tail;
T[] array = segment._array;
int last = segment._state._last;
int num = (last + 1) & (array.Length - 1);
if (num != segment._state._firstCopy)
{
array[last] = item;
segment._state._last = num;
}
else
{
EnqueueSlow(item, ref segment);
}
}
private void EnqueueSlow(T item, ref Segment segment)
{
if (segment._state._firstCopy != segment._state._first)
{
segment._state._firstCopy = segment._state._first;
Enqueue(item);
return;
}
int num = _tail._array.Length << 1;
if (num > 16777216)
{
num = 16777216;
}
Segment segment2 = new Segment(num);
segment2._array[0] = item;
segment2._state._last = 1;
segment2._state._lastCopy = 1;
try
{
}
finally
{
Volatile.Write(ref _tail._next, segment2);
_tail = segment2;
}
}
public bool TryDequeue([MaybeNullWhen(false)] out T result)
{
Segment head = _head;
T[] array = head._array;
int first = head._state._first;
if (first != head._state._lastCopy)
{
result = array[first];
array[first] = default(T);
head._state._first = (first + 1) & (array.Length - 1);
return true;
}
return TryDequeueSlow(head, array, peek: false, out result);
}
public bool TryPeek([MaybeNullWhen(false)] out T result)
{
Segment head = _head;
T[] array = head._array;
int first = head._state._first;
if (first != head._state._lastCopy)
{
result = array[first];
return true;
}
return TryDequeueSlow(head, array, peek: true, out result);
}
private bool TryDequeueSlow(Segment segment, T[] array, bool peek, [MaybeNullWhen(false)] out T result)
{
if (segment._state._last != segment._state._lastCopy)
{
segment._state._lastCopy = segment._state._last;
if (!peek)
{
return TryDequeue(out result);
}
return TryPeek(out result);
}
if (segment._next != null && segment._state._first == segment._state._last)
{
segment = segment._next;
array = segment._array;
_head = segment;
}
int first = segment._state._first;
if (first == segment._state._last)
{
result = default(T);
return false;
}
result = array[first];
if (!peek)
{
array[first] = default(T);
segment._state._first = (first + 1) & (segment._array.Length - 1);
segment._state._lastCopy = segment._state._last;
}
return true;
}
public IEnumerator<T> GetEnumerator()
{
for (Segment segment = _head; segment != null; segment = segment._next)
{
for (int pt = segment._state._first; pt != segment._state._last; pt = (pt + 1) & (segment._array.Length - 1))
{
yield return segment._array[pt];
}
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
namespace System.Threading.Channels
{
internal abstract class AsyncOperation
{
protected static readonly Action<object> s_availableSentinel = AvailableSentinel;
protected static readonly Action<object> s_completedSentinel = CompletedSentinel;
private static void AvailableSentinel(object s)
{
}
private static void CompletedSentinel(object s)
{
}
protected static void ThrowIncompleteOperationException()
{
throw new InvalidOperationException(System.SR.InvalidOperation_IncompleteAsyncOperation);
}
protected static void ThrowMultipleContinuations()
{
throw new InvalidOperationException(System.SR.InvalidOperation_MultipleContinuations);
}
protected static void ThrowIncorrectCurrentIdException()
{
throw new InvalidOperationException(System.SR.InvalidOperation_IncorrectToken);
}
}
internal class AsyncOperation<TResult> : AsyncOperation, IValueTaskSource, IValueTaskSource<TResult>
{
private readonly CancellationTokenRegistration _registration;
private readonly bool _pooled;
private readonly bool _runContinuationsAsynchronously;
private volatile int _completionReserved;
private TResult _result;
private ExceptionDispatchInfo _error;
private Action<object> _continuation;
private object _continuationState;
private object _schedulingContext;
private ExecutionContext _executionContext;
private short _currentId;
public AsyncOperation<TResult> Next { get; set; }
public CancellationToken CancellationToken { get; }
public ValueTask ValueTask => new ValueTask(this, _currentId);
public ValueTask<TResult> ValueTaskOfT => new ValueTask<TResult>(this, _currentId);
internal bool IsCompleted => (object)_continuation == AsyncOperation.s_completedSentinel;
public AsyncOperation(bool runContinuationsAsynchronously, CancellationToken cancellationToken = default(CancellationToken), bool pooled = false)
{
_continuation = (pooled ? AsyncOperation.s_availableSentinel : null);
_pooled = pooled;
_runContinuationsAsynchronously = runContinuationsAsynchronously;
if (cancellationToken.CanBeCanceled)
{
CancellationToken = cancellationToken;
_registration = UnsafeRegister(cancellationToken, delegate(object s)
{
AsyncOperation<TResult> asyncOperation = (AsyncOperation<TResult>)s;
asyncOperation.TrySetCanceled(asyncOperation.CancellationToken);
}, this);
}
}
public ValueTaskSourceStatus GetStatus(short token)
{
if (_currentId != token)
{
AsyncOperation.ThrowIncorrectCurrentIdException();
}
if (IsCompleted)
{
if (_error != null)
{
if (!(_error.SourceException is OperationCanceledException))
{
return ValueTaskSourceStatus.Faulted;
}
return ValueTaskSourceStatus.Canceled;
}
return ValueTaskSourceStatus.Succeeded;
}
return ValueTaskSourceStatus.Pending;
}
public TResult GetResult(short token)
{
if (_currentId != token)
{
AsyncOperation.ThrowIncorrectCurrentIdException();
}
if (!IsCompleted)
{
AsyncOperation.ThrowIncompleteOperationException();
}
ExceptionDispatchInfo error = _error;
TResult result = _result;
_currentId++;
if (_pooled)
{
Volatile.Write(ref _continuation, AsyncOperation.s_availableSentinel);
}
error?.Throw();
return result;
}
void IValueTaskSource.GetResult(short token)
{
if (_currentId != token)
{
AsyncOperation.ThrowIncorrectCurrentIdException();
}
if (!IsCompleted)
{
AsyncOperation.ThrowIncompleteOperationException();
}
ExceptionDispatchInfo error = _error;
_currentId++;
if (_pooled)
{
Volatile.Write(ref _continuation, AsyncOperation.s_availableSentinel);
}
error?.Throw();
}
public bool TryOwnAndReset()
{
if ((object)Interlocked.CompareExchange(ref _continuation, null, AsyncOperation.s_availableSentinel) == AsyncOperation.s_availableSentinel)
{
_continuationState = null;
_result = default(TResult);
_error = null;
_schedulingContext = null;
_executionContext = null;
return true;
}
return false;
}
public void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags)
{
if (_currentId != token)
{
AsyncOperation.ThrowIncorrectCurrentIdException();
}
if (_continuationState != null)
{
AsyncOperation.ThrowMultipleContinuations();
}
_continuationState = state;
if ((flags & ValueTaskSourceOnCompletedFlags.FlowExecutionContext) != 0)
{
_executionContext = ExecutionContext.Capture();
}
SynchronizationContext synchronizationContext = null;
TaskScheduler taskScheduler = null;
if ((flags & ValueTaskSourceOnCompletedFlags.UseSchedulingContext) != 0)
{
synchronizationContext = SynchronizationContext.Current;
if (synchronizationContext != null && synchronizationContext.GetType() != typeof(SynchronizationContext))
{
_schedulingContext = synchronizationContext;
}
else
{
synchronizationContext = null;
taskScheduler = TaskScheduler.Current;
if (taskScheduler != TaskScheduler.Default)
{
_schedulingContext = taskScheduler;
}
}
}
Action<object> action = Interlocked.CompareExchange(ref _continuation, continuation, null);
if (action == null)
{
return;
}
if ((object)action != AsyncOperation.s_completedSentinel)
{
AsyncOperation.ThrowMultipleContinuations();
}
if (_schedulingContext == null)
{
if (_executionContext == null)
{
UnsafeQueueUserWorkItem(continuation, state);
}
else
{
QueueUserWorkItem(continuation, state);
}
}
else if (synchronizationContext != null)
{
synchronizationContext.Post(delegate(object s)
{
KeyValuePair<Action<object>, object> keyValuePair = (KeyValuePair<Action<object>, object>)s;
keyValuePair.Key(keyValuePair.Value);
}, new KeyValuePair<Action<object>, object>(continuation, state));
}
else
{
Task.Factory.StartNew(continuation, state, CancellationToken.None, TaskCreationOptions.DenyChildAttach, taskScheduler);
}
}
public bool UnregisterCancellation()
{
if (CancellationToken.CanBeCanceled)
{
CancellationTokenRegistration registration = _registration;
registration.Dispose();
return _completionReserved == 0;
}
return true;
}
public bool TrySetResult(TResult item)
{
UnregisterCancellation();
if (TryReserveCompletionIfCancelable())
{
_result = item;
SignalCompletion();
return true;
}
return false;
}
public bool TrySetException(Exception exception)
{
UnregisterCancellation();
if (TryReserveCompletionIfCancelable())
{
_error = ExceptionDispatchInfo.Capture(exception);
SignalCompletion();
return true;
}
return false;
}
public bool TrySetCanceled(CancellationToken cancellationToken = default(CancellationToken))
{
if (TryReserveCompletionIfCancelable())
{
_error = ExceptionDispatchInfo.Capture(new OperationCanceledException(cancellationToken));
SignalCompletion();
return true;
}
return false;
}
private bool TryReserveCompletionIfCancelable()
{
if (CancellationToken.CanBeCanceled)
{
return Interlocked.CompareExchange(ref _completionReserved, 1, 0) == 0;
}
return true;
}
private void SignalCompletion()
{
if (_continuation == null && Interlocked.CompareExchange(ref _continuation, AsyncOperation.s_completedSentinel, null) == null)
{
return;
}
if (_schedulingContext == null)
{
if (_runContinuationsAsynchronously)
{
UnsafeQueueSetCompletionAndInvokeContinuation();
return;
}
}
else if (_schedulingContext is SynchronizationContext synchronizationContext)
{
if (_runContinuationsAsynchronously || synchronizationContext != SynchronizationContext.Current)
{
synchronizationContext.Post(delegate(object s)
{
((AsyncOperation<TResult>)s).SetCompletionAndInvokeContinuation();
}, this);
return;
}
}
else
{
TaskScheduler taskScheduler = (TaskScheduler)_schedulingContext;
if (_runContinuationsAsynchronously || taskScheduler != TaskScheduler.Current)
{
Task.Factory.StartNew(delegate(object s)
{
((AsyncOperation<TResult>)s).SetCompletionAndInvokeContinuation();
}, this, CancellationToken.None, TaskCreationOptions.DenyChildAttach, taskScheduler);
return;
}
}
SetCompletionAndInvokeContinuation();
}
private void SetCompletionAndInvokeContinuation()
{
if (_executionContext == null)
{
Action<object> continuation = _continuation;
_continuation = AsyncOperation.s_completedSentinel;
continuation(_continuationState);
return;
}
ExecutionContext.Run(_executionContext, delegate(object s)
{
AsyncOperation<TResult> asyncOperation = (AsyncOperation<TResult>)s;
Action<object> continuation2 = asyncOperation._continuation;
asyncOperation._continuation = AsyncOperation.s_completedSentinel;
continuation2(asyncOperation._continuationState);
}, this);
}
private void UnsafeQueueSetCompletionAndInvokeContinuation()
{
ThreadPool.UnsafeQueueUserWorkItem(delegate(object s)
{
((AsyncOperation<TResult>)s).SetCompletionAndInvokeContinuation();
}, this);
}
private static void UnsafeQueueUserWorkItem(Action<object> action, object state)
{
QueueUserWorkItem(action, state);
}
private static void QueueUserWorkItem(Action<object> action, object state)
{
Task.Factory.StartNew(action, state, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
}
private static CancellationTokenRegistration UnsafeRegister(CancellationToken cancellationToken, Action<object> action, object state)
{
return cancellationToken.Register(action, state);
}
}
internal sealed class VoidAsyncOperationWithData<TData> : AsyncOperation<VoidResult>
{
public TData Item { get; set; }
public VoidAsyncOperationWithData(bool runContinuationsAsynchronously, CancellationToken cancellationToken = default(CancellationToken), bool pooled = false)
: base(runContinuationsAsynchronously, cancellationToken, pooled)
{
}
}
[DebuggerDisplay("Items={ItemsCountForDebugger}, Capacity={_bufferedCapacity}, Mode={_mode}, Closed={ChannelIsClosedForDebugger}")]
[DebuggerTypeProxy(typeof(DebugEnumeratorDebugView<>))]
internal sealed class BoundedChannel<T> : Channel<T>, IDebugEnumerable<T>
{
[DebuggerDisplay("Items={ItemsCountForDebugger}")]
[DebuggerTypeProxy(typeof(DebugEnumeratorDebugView<>))]
private sealed class BoundedChannelReader : ChannelReader<T>, IDebugEnumerable<T>
{
internal readonly BoundedChannel<T> _parent;
private readonly AsyncOperation<T> _readerSingleton;
private readonly AsyncOperation<bool> _waiterSingleton;
public override Task Completion => _parent._completion.Task;
public override bool CanCount => true;
public override bool CanPeek => true;
public override int Count
{
get
{
BoundedChannel<T> parent = _parent;
lock (parent.SyncObj)
{
return parent._items.Count;
}
}
}
private int ItemsCountForDebugger => _parent._items.Count;
internal BoundedChannelReader(BoundedChannel<T> parent)
{
_parent = parent;
_readerSingleton = new AsyncOperation<T>(parent._runContinuationsAsynchronously, default(CancellationToken), pooled: true);
_waiterSingleton = new AsyncOperation<bool>(parent._runContinuationsAsynchronously, default(CancellationToken), pooled: true);
}
public override bool TryRead([MaybeNullWhen(false)] out T item)
{
BoundedChannel<T> parent = _parent;
lock (parent.SyncObj)
{
if (!parent._items.IsEmpty)
{
item = DequeueItemAndPostProcess();
return true;
}
}
item = default(T);
return false;
}
public override bool TryPeek([MaybeNullWhen(false)] out T item)
{
BoundedChannel<T> parent = _parent;
lock (parent.SyncObj)
{
if (!parent._items.IsEmpty)
{
item = parent._items.PeekHead();
return true;
}
}
item = default(T);
return false;
}
public override ValueTask<T> ReadAsync(CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return new ValueTask<T>(Task.FromCanceled<T>(cancellationToken));
}
BoundedChannel<T> parent = _parent;
lock (parent.SyncObj)
{
if (!parent._items.IsEmpty)
{
return new ValueTask<T>(DequeueItemAndPostProcess());
}
if (parent._doneWriting != null)
{
return ChannelUtilities.GetInvalidCompletionValueTask<T>(parent._doneWriting);
}
if (!cancellationToken.CanBeCanceled)
{
AsyncOperation<T> readerSingleton = _readerSingleton;
if (readerSingleton.TryOwnAndReset())
{
parent._blockedReaders.EnqueueTail(readerSingleton);
return readerSingleton.ValueTaskOfT;
}
}
AsyncOperation<T> asyncOperation = new AsyncOperation<T>(parent._runContinuationsAsynchronously | cancellationToken.CanBeCanceled, cancellationToken);
parent._blockedReaders.EnqueueTail(asyncOperation);
return asyncOperation.ValueTaskOfT;
}
}
public override ValueTask<bool> WaitToReadAsync(CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return new ValueTask<bool>(Task.FromCanceled<bool>(cancellationToken));
}
BoundedChannel<T> parent = _parent;
lock (parent.SyncObj)
{
if (!parent._items.IsEmpty)
{
return new ValueTask<bool>(result: true);
}
if (parent._doneWriting != null)
{
return (parent._doneWriting != ChannelUtilities.s_doneWritingSentinel) ? new ValueTask<bool>(Task.FromException<bool>(parent._doneWriting)) : default(ValueTask<bool>);
}
if (!cancellationToken.CanBeCanceled)
{
AsyncOperation<bool> waiterSingleton = _waiterSingleton;
if (waiterSingleton.TryOwnAndReset())
{
ChannelUtilities.QueueWaiter(ref parent._waitingReadersTail, waiterSingleton);
return waiterSingleton.ValueTaskOfT;
}
}
AsyncOperation<bool> asyncOperation = new AsyncOperation<bool>(parent._runContinuationsAsynchronously | cancellationToken.CanBeCanceled, cancellationToken);
ChannelUtilities.QueueWaiter(ref _parent._waitingReadersTail, asyncOperation);
return asyncOperation.ValueTaskOfT;
}
}
private T DequeueItemAndPostProcess()
{
BoundedChannel<T> parent = _parent;
T result = parent._items.DequeueHead();
if (parent._doneWriting != null)
{
if (parent._items.IsEmpty)
{
ChannelUtilities.Complete(parent._completion, parent._doneWriting);
}
}
else
{
while (!parent._blockedWriters.IsEmpty)
{
VoidAsyncOperationWithData<T> voidAsyncOperationWithData = parent._blockedWriters.DequeueHead();
if (voidAsyncOperationWithData.TrySetResult(default(VoidResult)))
{
parent._items.EnqueueTail(voidAsyncOperationWithData.Item);
return result;
}
}
ChannelUtilities.WakeUpWaiters(ref parent._waitingWritersTail, result: true);
}
return result;
}
IEnumerator<T> IDebugEnumerable<T>.GetEnumerator()
{
return _parent._items.GetEnumerator();
}
}
[DebuggerDisplay("Items={ItemsCountForDebugger}, Capacity={CapacityForDebugger}")]
[DebuggerTypeProxy(typeof(DebugEnumeratorDebugView<>))]
private sealed class BoundedChannelWriter : ChannelWriter<T>, IDebugEnumerable<T>
{
internal readonly BoundedChannel<T> _parent;
private readonly VoidAsyncOperationWithData<T> _writerSingleton;
private readonly AsyncOperation<bool> _waiterSingleton;
private int ItemsCountForDebugger => _parent._items.Count;
private int CapacityForDebugger => _parent._bufferedCapacity;
internal BoundedChannelWriter(BoundedChannel<T> parent)
{
_parent = parent;
_writerSingleton = new VoidAsyncOperationWithData<T>(runContinuationsAsynchronously: true, default(CancellationToken), pooled: true);
_waiterSingleton = new AsyncOperation<bool>(runContinuationsAsynchronously: true, default(CancellationToken), pooled: true);
}
public override bool TryComplete(Exception error)
{
BoundedChannel<T> parent = _parent;
bool isEmpty;
lock (parent.SyncObj)
{
if (parent._doneWriting != null)
{
return false;
}
parent._doneWriting = error ?? ChannelUtilities.s_doneWritingSentinel;
isEmpty = parent._items.IsEmpty;
}
if (isEmpty)
{
ChannelUtilities.Complete(parent._completion, error);
}
ChannelUtilities.FailOperations<AsyncOperation<T>, T>(parent._blockedReaders, ChannelUtilities.CreateInvalidCompletionException(error));
ChannelUtilities.FailOperations<VoidAsyncOperationWithData<T>, VoidResult>(parent._blockedWriters, ChannelUtilities.CreateInvalidCompletionException(error));
ChannelUtilities.WakeUpWaiters(ref parent._waitingReadersTail, result: false, error);
ChannelUtilities.WakeUpWaiters(ref parent._waitingWritersTail, result: false, error);
return true;
}
public override bool TryWrite(T item)
{
AsyncOperation<T> asyncOperation = null;
AsyncOperation<bool> listTail = null;
BoundedChannel<T> parent = _parent;
bool lockTaken = false;
try
{
Monitor.Enter(parent.SyncObj, ref lockTaken);
if (parent._doneWriting != null)
{
return false;
}
int count = parent._items.Count;
if (count != 0)
{
if (count < parent._bufferedCapacity)
{
parent._items.EnqueueTail(item);
return true;
}
if (parent._mode == BoundedChannelFullMode.Wait)
{
return false;
}
if (parent._mode == BoundedChannelFullMode.DropWrite)
{
Monitor.Exit(parent.SyncObj);
lockTaken = false;
parent._itemDropped?.Invoke(item);
return true;
}
T obj = ((parent._mode == BoundedChannelFullMode.DropNewest) ? parent._items.DequeueTail() : parent._items.DequeueHead());
parent._items.EnqueueTail(item);
Monitor.Exit(parent.SyncObj);
lockTaken = false;
parent._itemDropped?.Invoke(obj);
return true;
}
while (!parent._blockedReaders.IsEmpty)
{
AsyncOperation<T> asyncOperation2 = parent._blockedReaders.DequeueHead();
if (asyncOperation2.UnregisterCancellation())
{
asyncOperation = asyncOperation2;
break;
}
}
if (asyncOperation == null)
{
parent._items.EnqueueTail(item);
listTail = parent._waitingReadersTail;
if (listTail == null)
{
return true;
}
parent._waitingReadersTail = null;
}
}
finally
{
if (lockTaken)
{
Monitor.Exit(parent.SyncObj);
}
}
if (asyncOperation != null)
{
bool flag = asyncOperation.TrySetResult(item);
}
else
{
ChannelUtilities.WakeUpWaiters(ref listTail, result: true);
}
return true;
}
public override ValueTask<bool> WaitToWriteAsync(CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return new ValueTask<bool>(Task.FromCanceled<bool>(cancellationToken));
}
BoundedChannel<T> parent = _parent;
lock (parent.SyncObj)
{
if (parent._doneWriting != null)
{
return (parent._doneWriting != ChannelUtilities.s_doneWritingSentinel) ? new ValueTask<bool>(Task.FromException<bool>(parent._doneWriting)) : default(ValueTask<bool>);
}
if (parent._items.Count < parent._bufferedCapacity || parent._mode != 0)
{
return new ValueTask<bool>(result: true);
}
if (!cancellationToken.CanBeCanceled)
{
AsyncOperation<bool> waiterSingleton = _waiterSingleton;
if (waiterSingleton.TryOwnAndReset())
{
ChannelUtilities.QueueWaiter(ref parent._waitingWritersTail, waiterSingleton);
return waiterSingleton.ValueTaskOfT;
}
}
AsyncOperation<bool> asyncOperation = new AsyncOperation<bool>(runContinuationsAsynchronously: true, cancellationToken);
ChannelUtilities.QueueWaiter(ref parent._waitingWritersTail, asyncOperation);
return asyncOperation.ValueTaskOfT;
}
}
public override ValueTask WriteAsync(T item, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return new ValueTask(Task.FromCanceled(cancellationToken));
}
AsyncOperation<T> asyncOperation = null;
AsyncOperation<bool> listTail = null;
BoundedChannel<T> parent = _parent;
bool lockTaken = false;
try
{
Monitor.Enter(parent.SyncObj, ref lockTaken);
if (parent._doneWriting != null)
{
return new ValueTask(Task.FromException(ChannelUtilities.CreateInvalidCompletionException(parent._doneWriting)));
}
int count = parent._items.Count;
if (count != 0)
{
if (count < parent._bufferedCapacity)
{
parent._items.EnqueueTail(item);
return default(ValueTask);
}
if (parent._mode == BoundedChannelFullMode.Wait)
{
if (!cancellationToken.CanBeCanceled)
{
VoidAsyncOperationWithData<T> writerSingleton = _writerSingleton;
if (writerSingleton.TryOwnAndReset())
{
writerSingleton.Item = item;
parent._blockedWriters.EnqueueTail(writerSingleton);
return writerSingleton.ValueTask;
}
}
VoidAsyncOperationWithData<T> voidAsyncOperationWithData = new VoidAsyncOperationWithData<T>(runContinuationsAsynchronously: true, cancellationToken);
voidAsyncOperationWithData.Item = item;
parent._blockedWriters.EnqueueTail(voidAsyncOperationWithData);
return voidAsyncOperationWithData.ValueTask;
}
if (parent._mode == BoundedChannelFullMode.DropWrite)
{
Monitor.Exit(parent.SyncObj);
lockTaken = false;
parent._itemDropped?.Invoke(item);
return default(ValueTask);
}
T obj = ((parent._mode == BoundedChannelFullMode.DropNewest) ? parent._items.DequeueTail() : parent._items.DequeueHead());
parent._items.EnqueueTail(item);
Monitor.Exit(parent.SyncObj);
lockTaken = false;
parent._itemDropped?.Invoke(obj);
return default(ValueTask);
}
while (!parent._blockedReaders.IsEmpty)
{
AsyncOperation<T> asyncOperation2 = parent._blockedReaders.DequeueHead();
if (asyncOperation2.UnregisterCancellation())
{
asyncOperation = asyncOperation2;
break;
}
}
if (asyncOperation == null)
{
parent._items.EnqueueTail(item);
listTail = parent._waitingReadersTail;
if (listTail == null)
{
return default(ValueTask);
}
parent._waitingReadersTail = null;
}
}
finally
{
if (lockTaken)
{
Monitor.Exit(parent.SyncObj);
}
}
if (asyncOperation != null)
{
bool flag = asyncOperation.TrySetResult(item);
}
else
{
ChannelUtilities.WakeUpWaiters(ref listTail, result: true);
}
return default(ValueTask);
}
IEnumerator<T> IDebugEnumerable<T>.GetEnumerator()
{
return _parent._items.GetEnumerator();
}
}
private readonly BoundedChannelFullMode _mode;
private readonly Action<T> _itemDropped;
private readonly TaskCompletionSource _completion;
private readonly int _bufferedCapacity;
private readonly Deque<T> _items = new Deque<T>();
private readonly Deque<AsyncOperation<T>> _blockedReaders = new Deque<AsyncOperation<T>>();
private readonly Deque<VoidAsyncOperationWithData<T>> _blockedWriters = new Deque<VoidAsyncOperationWithData<T>>();
private AsyncOperation<bool> _waitingReadersTail;
private AsyncOperation<bool> _waitingWritersTail;
private readonly bool _runContinuationsAsynchronously;
private Exception _doneWriting;
private object SyncObj => _items;
private int ItemsCountForDebugger => _items.Count;
private bool ChannelIsClosedForDebugger => _doneWriting != null;
internal BoundedChannel(int bufferedCapacity, BoundedChannelFullMode mode, bool runContinuationsAsynchronously, Action<T> itemDropped)
{
_bufferedCapacity = bufferedCapacity;
_mode = mode;
_runContinuationsAsynchronously = runContinuationsAsynchronously;
_itemDropped = itemDropped;
_completion = new TaskCompletionSource(runContinuationsAsynchronously ? TaskCreationOptions.RunContinuationsAsynchronously : TaskCreationOptions.None);
base.Reader = new BoundedChannelReader(this);
base.Writer = new BoundedChannelWriter(this);
}
[Conditional("DEBUG")]
private void AssertInvariants()
{
_ = _items.IsEmpty;
_ = _items.Count;
_ = _bufferedCapacity;
_ = _blockedReaders.IsEmpty;
_ = _blockedWriters.IsEmpty;
_ = _completion.Task.IsCompleted;
}
IEnumerator<T> IDebugEnumerable<T>.GetEnumerator()
{
return _items.GetEnumerator();
}
}
public enum BoundedChannelFullMode
{
Wait,
DropNewest,
DropOldest,
DropWrite
}
public static class Channel
{
public static Channel<T> CreateUnbounded<T>()
{
return new UnboundedChannel<T>(runContinuationsAsynchronously: true);
}
public static Channel<T> CreateUnbounded<T>(UnboundedChannelOptions options)
{
if (options == null)
{
throw new ArgumentNullException("options");
}
if (options.SingleReader)
{
return new SingleConsumerUnboundedChannel<T>(!options.AllowSynchronousContinuations);
}
return new UnboundedChannel<T>(!options.AllowSynchronousContinuations);
}
public static Channel<T> CreateBounded<T>(int capacity)
{
if (capacity < 1)
{
throw new ArgumentOutOfRangeException("capacity");
}
return new BoundedChannel<T>(capacity, BoundedChannelFullMode.Wait, runContinuationsAsynchronously: true, null);
}
public static Channel<T> CreateBounded<T>(BoundedChannelOptions options)
{
return CreateBounded<T>(options, null);
}
public static Channel<T> CreateBounded<T>(BoundedChannelOptions options, Action<T>? itemDropped)
{
if (options == null)
{
throw new ArgumentNullException("options");
}
return new BoundedChannel<T>(options.Capacity, options.FullMode, !options.AllowSynchronousContinuations, itemDropped);
}
}
public class ChannelClosedException : InvalidOperationException
{
public ChannelClosedException()
: base(System.SR.ChannelClosedException_DefaultMessage)
{
}
public ChannelClosedException(string? message)
: base(message)
{
}
public ChannelClosedException(Exception? innerException)
: base(System.SR.ChannelClosedException_DefaultMessage, innerException)
{
}
public ChannelClosedException(string? message, Exception? innerException)
: base(message, innerException)
{
}
}
public abstract class ChannelOptions
{
public bool SingleWriter { get; set; }
public bool SingleReader { get; set; }
public bool AllowSynchronousContinuations { get; set; }
}
public sealed class BoundedChannelOptions : ChannelOptions
{
private int _capacity;
private BoundedChannelFullMode _mode;
public int Capacity
{
get
{
return _capacity;
}
set
{
if (value < 1)
{
throw new ArgumentOutOfRangeException("value");
}
_capacity = value;
}
}
public BoundedChannelFullMode FullMode
{
get
{
return _mode;
}
set
{
if ((uint)value <= 3u)
{
_mode = value;
return;
}
throw new ArgumentOutOfRangeException("value");
}
}
public BoundedChannelOptions(int capacity)
{
if (capacity < 1)
{
throw new ArgumentOutOfRangeException("capacity");
}
_capacity = capacity;
}
}
public sealed class UnboundedChannelOptions : ChannelOptions
{
}
public abstract class ChannelReader<T>
{
public virtual Task Completion => ChannelUtilities.s_neverCompletingTask;
public virtual bool CanCount => false;
public virtual bool CanPeek => false;
public virtual int Count
{
get
{
throw new NotSupportedException();
}
}
public abstract bool TryRead([MaybeNullWhen(false)] out T item);
public virtual bool TryPeek([MaybeNullWhen(false)] out T item)
{
item = default(T);
return false;
}
public abstract ValueTask<bool> WaitToReadAsync(CancellationToken cancellationToken = default(CancellationToken));
public virtual ValueTask<T> ReadAsync(CancellationToken cancellationToken = default(CancellationToken))
{
if (cancellationToken.IsCancellationRequested)
{
return new ValueTask<T>(Task.FromCanceled<T>(cancellationToken));
}
try
{
if (TryRead(out var item))
{
return new ValueTask<T>(item);
}
}
catch (Exception ex) when (!(ex is ChannelClosedException) && !(ex is OperationCanceledException))
{
return new ValueTask<T>(Task.FromException<T>(ex));
}
return ReadAsyncCore(cancellationToken);
async ValueTask<T> ReadAsyncCore(CancellationToken ct)
{
T item2;
do
{
if (!(await WaitToReadAsync(ct).ConfigureAwait(continueOnCapturedContext: false)))
{
throw new ChannelClosedException();
}
}
while (!TryRead(out item2));
return item2;
}
}
}
internal static class ChannelUtilities
{
internal static readonly Exception s_doneWritingSentinel = new Exception("s_doneWritingSentinel");
internal static readonly Task<bool> s_trueTask = Task.FromResult(result: true);
internal static readonly Task<bool> s_falseTask = Task.FromResult(result: false);
internal static readonly Task s_neverCompletingTask = new TaskCompletionSource<bool>().Task;
internal static void Complete(TaskCompletionSource tcs, Exception error = null)
{
if (error is OperationCanceledException ex)
{
tcs.TrySetCanceled(ex.CancellationToken);
}
else if (error != null && error != s_doneWritingSentinel)
{
tcs.TrySetException(error);
}
else
{
tcs.TrySetResult();
}
}
internal static ValueTask<T> GetInvalidCompletionValueTask<T>(Exception error)
{
Task<T> task = ((error == s_doneWritingSentinel) ? Task.FromException<T>(CreateInvalidCompletionException()) : ((error is OperationCanceledException ex) ? Task.FromCanceled<T>(ex.CancellationToken.IsCancellationRequested ? ex.CancellationToken : new CancellationToken(canceled: true)) : Task.FromException<T>(CreateInvalidCompletionException(error))));
return new ValueTask<T>(task);
}
internal static void QueueWaiter(ref AsyncOperation<bool> tail, AsyncOperation<bool> waiter)
{
AsyncOperation<bool> asyncOperation = tail;
if (asyncOperation == null)
{
waiter.Next = waiter;
}
else
{
waiter.Next = asyncOperation.Next;
asyncOperation.Next = waiter;
}
tail = waiter;
}
internal static void WakeUpWaiters(ref AsyncOperation<bool> listTail, bool result, Exception error = null)
{
AsyncOperation<bool> asyncOperation = listTail;
if (asyncOperation != null)
{
listTail = null;
AsyncOperation<bool> next = asyncOperation.Next;
AsyncOperation<bool> asyncOperation2 = next;
do
{
AsyncOperation<bool> next2 = asyncOperation2.Next;
asyncOperation2.Next = null;
bool flag = ((error != null) ? asyncOperation2.TrySetException(error) : asyncOperation2.TrySetResult(result));
asyncOperation2 = next2;
}
while (asyncOperation2 != next);
}
}
internal static void FailOperations<T, TInner>(Deque<T> operations, Exception error) where T : AsyncOperation<TInner>
{
while (!operations.IsEmpty)
{
operations.DequeueHead().TrySetException(error);
}
}
internal static Exception CreateInvalidCompletionException(Exception inner = null)
{
if (!(inner is OperationCanceledException))
{
if (inner == null || inner == s_doneWritingSentinel)
{
return new ChannelClosedException();
}
return new ChannelClosedException(inner);
}
return inner;
}
}
public abstract class ChannelWriter<T>
{
public virtual bool TryComplete(Exception? error = null)
{
return false;
}
public abstract bool TryWrite(T item);
public abstract ValueTask<bool> WaitToWriteAsync(CancellationToken cancellationToken = default(CancellationToken));
public virtual ValueTask WriteAsync(T item, CancellationToken cancellationToken = default(CancellationToken))
{
try
{
return cancellationToken.IsCancellationRequested ? new ValueTask(Task.FromCanceled<T>(cancellationToken)) : (TryWrite(item) ? default(ValueTask) : WriteAsyncCore(item, cancellationToken));
}
catch (Exception exception)
{
return new ValueTask(Task.FromException(exception));
}
}
private async ValueTask WriteAsyncCore(T innerItem, CancellationToken ct)
{
while (await WaitToWriteAsync(ct).ConfigureAwait(continueOnCapturedContext: false))
{
if (TryWrite(innerItem))
{
return;
}
}
throw ChannelUtilities.CreateInvalidCompletionException();
}
public void Complete(Exception? error = null)
{
if (!TryComplete(error))
{
throw ChannelUtilities.CreateInvalidCompletionException();
}
}
}
public abstract class Channel<T> : Channel<T, T>
{
}
public abstract class Channel<TWrite, TRead>
{
public ChannelReader<TRead> Reader { get; protected set; }
public ChannelWriter<TWrite> Writer { get; protected set; }
public static implicit operator ChannelReader<TRead>(Channel<TWrite, TRead> channel)
{
return channel.Reader;
}
public static implicit operator ChannelWriter<TWrite>(Channel<TWrite, TRead> channel)
{
return channel.Writer;
}
}
internal interface IDebugEnumerable<T>
{
IEnumerator<T> GetEnumerator();
}
internal sealed class DebugEnumeratorDebugView<T>
{
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public T[] Items { get; }
public DebugEnumeratorDebugView(IDebugEnumerable<T> enumerable)
{
List<T> list = new List<T>();
foreach (T item in enumerable)
{
list.Add(item);
}
Items = list.ToArray();
}
}
[DebuggerDisplay("Items={ItemsCountForDebugger}, Closed={ChannelIsClosedForDebugger}")]
[DebuggerTypeProxy(typeof(DebugEnumeratorDebugView<>))]
internal sealed class SingleConsumerUnboundedChannel<T> : Channel<T>, IDebugEnumerable<T>
{
[DebuggerDisplay("Items={ItemsCountForDebugger}")]
[DebuggerTypeProxy(typeof(DebugEnumeratorDebugView<>))]
private sealed class UnboundedChannelReader : ChannelReader<T>, IDebugEnumerable<T>
{
internal readonly SingleConsumerUnboundedChannel<T> _parent;
private readonly AsyncOperation<T> _readerSingleton;
private readonly AsyncOperation<bool> _waiterSingleton;
public override Task Completion => _parent._completion.Task;
public override bool CanPeek => true;
private int ItemsCountForDebugger => _parent._items.Count;
internal UnboundedChannelReader(SingleConsumerUnboundedChannel<T> parent)
{
_parent = parent;
_readerSingleton = new AsyncOperation<T>(parent._runContinuationsAsynchronously, default(CancellationToken), pooled: true);
_waiterSingleton = new AsyncOperation<bool>(parent._runContinuationsAsynchronously, default(CancellationToken), pooled: true);
}
public override ValueTask<T> ReadAsync(CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return new ValueTask<T>(Task.FromCanceled<T>(cancellationToken));
}
if (TryRead(out var item))
{
return new ValueTask<T>(item);
}
SingleConsumerUnboundedChannel<T> parent = _parent;
AsyncOperation<T> asyncOperation;
AsyncOperation<T> asyncOperation2;
lock (parent.SyncObj)
{
if (TryRead(out item))
{
return new ValueTask<T>(item);
}
if (parent._doneWriting != null)
{
return ChannelUtilities.GetInvalidCompletionValueTask<T>(parent._doneWriting);
}
asyncOperation = parent._blockedReader;
if (!cancellationToken.CanBeCanceled && _readerSingleton.TryOwnAndReset())
{
asyncOperation2 = _readerSingleton;
if (asyncOperation2 == asyncOperation)
{
asyncOperation = null;
}
}
else
{
asyncOperation2 = new AsyncOperation<T>(_parent._runContinuationsAsynchronously, cancellationToken);
}
parent._blockedReader = asyncOperation2;
}
asyncOperation?.TrySetCanceled();
return asyncOperation2.ValueTaskOfT;
}
public override bool TryRead([MaybeNullWhen(false)] out T item)
{
SingleConsumerUnboundedChannel<T> parent = _parent;
if (parent._items.TryDequeue(out item))
{
if (parent._doneWriting != null && parent._items.IsEmpty)
{
ChannelUtilities.Complete(parent._completion, parent._doneWriting);
}
return true;
}
return false;
}
public override bool TryPeek([MaybeNullWhen(false)] out T item)
{
return _parent._items.TryPeek(out item);
}
public override ValueTask<bool> WaitToReadAsync(CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return new ValueTask<bool>(Task.FromCanceled<bool>(cancellationToken));
}
if (!_parent._items.IsEmpty)
{
return new ValueTask<bool>(result: true);
}
SingleConsumerUnboundedChannel<T> parent = _parent;
AsyncOperation<bool> asyncOperation = null;
AsyncOperation<bool> asyncOperation2;
lock (parent.SyncObj)
{
if (!parent._items.IsEmpty)
{
return new ValueTask<bool>(result: true);
}
if (parent._doneWriting != null)
{
return (parent._doneWriting != ChannelUtilities.s_doneWritingSentinel) ? new ValueTask<bool>(Task.FromException<bool>(parent._doneWriting)) : default(ValueTask<bool>);
}
asyncOperation = parent._waitingReader;
if (!cancellationToken.CanBeCanceled && _waiterSingleton.TryOwnAndReset())
{
asyncOperation2 = _waiterSingleton;
if (asyncOperation2 == asyncOperation)
{
asyncOperation = null;
}
}
else
{
asyncOperation2 = new AsyncOperation<bool>(_parent._runContinuationsAsynchronously, cancellationToken);
}
parent._waitingReader = asyncOperation2;
}
asyncOperation?.TrySetCanceled();
return asyncOperation2.ValueTaskOfT;
}
IEnumerator<T> IDebugEnumerable<T>.GetEnumerator()
{
return _parent._items.GetEnumerator();
}
}
[DebuggerDisplay("Items={ItemsCountForDebugger}")]
[DebuggerTypeProxy(typeof(DebugEnumeratorDebugView<>))]
private sealed class UnboundedChannelWriter : ChannelWriter<T>, IDebugEnumerable<T>
{
internal readonly SingleConsumerUnboundedChannel<T> _parent;
private int ItemsCountForDebugger => _parent._items.Count;
internal UnboundedChannelWriter(SingleConsumerUnboundedChannel<T> parent)
{
_parent = parent;
}
public override bool TryComplete(Exception error)
{
AsyncOperation<T> asyncOperation = null;
AsyncOperation<bool> asyncOperation2 = null;
bool flag = false;
SingleConsumerUnboundedChannel<T> parent = _parent;
lock (parent.SyncObj)
{
if (parent._doneWriting != null)
{
return false;
}
parent._doneWriting = error ?? ChannelUtilities.s_doneWritingSentinel;
if (parent._items.IsEmpty)
{
flag = true;
if (parent._blockedReader != null)
{
asyncOperation = parent._blockedReader;
parent._blockedReader = null;
}
if (parent._waitingReader != null)
{
asyncOperation2 = parent._waitingReader;
parent._waitingReader = null;
}
}
}
if (flag)
{
ChannelUtilities.Complete(parent._completion, error);
}
if (asyncOperation != null)
{
error = ChannelUtilities.CreateInvalidCompletionException(error);
asyncOperation.TrySetException(error);
}
if (asyncOperation2 != null)
{
if (error != null)
{
asyncOperation2.TrySetException(error);
}
else
{
asyncOperation2.TrySetResult(item: false);
}
}
return true;
}
public override bool TryWrite(T item)
{
SingleConsumerUnboundedChannel<T> parent = _parent;
AsyncOperation<T> asyncOperation;
do
{
asyncOperation = null;
AsyncOperation<bool> asyncOperation2 = null;
lock (parent.SyncObj)
{
if (parent._doneWriting != null)
{
return false;
}
asyncOperation = parent._blockedReader;
if (asyncOperation != null)
{
parent._blockedReader = null;
}
else
{
parent._items.Enqueue(item);
asyncOperation2 = parent._waitingReader;
if (asyncOperation2 == null)
{
return true;
}
parent._waitingReader = null;
}
}
if (asyncOperation2 != null)
{
asyncOperation2.TrySetResult(item: true);
return true;
}
}
while (!asyncOperation.TrySetResult(item));
return true;
}
public override ValueTask<bool> WaitToWriteAsync(CancellationToken cancellationToken)
{
Exception doneWriting = _parent._doneWriting;
if (!cancellationToken.IsCancellationRequested)
{
if (doneWriting != null)
{
if (doneWriting == ChannelUtilities.s_doneWritingSentinel)
{
return default(ValueTask<bool>);
}
return new ValueTask<bool>(Task.FromException<bool>(doneWriting));
}
return new ValueTask<bool>(result: true);
}
return new ValueTask<bool>(Task.FromCanceled<bool>(cancellationToken));
}
public override ValueTask WriteAsync(T item, CancellationToken cancellationToken)
{
if (!cancellationToken.IsCancellationRequested)
{
if (!TryWrite(item))
{
return new ValueTask(Task.FromException(ChannelUtilities.CreateInvalidCompletionException(_parent._doneWriting)));
}
return default(ValueTask);
}
return new ValueTask(Task.FromCanceled(cancellationToken));
}
IEnumerator<T> IDebugEnumerable<T>.GetEnumerator()
{
return _parent._items.GetEnumerator();
}
}
private readonly TaskCompletionSource _completion;
private readonly SingleProducerSingleConsumerQueue<T> _items = new SingleProducerSingleConsumerQueue<T>();
private readonly bool _runContinuationsAsynchronously;
private volatile Exception _doneWriting;
private AsyncOperation<T> _blockedReader;
private AsyncOperation<bool> _waitingReader;
private object SyncObj => _items;
private int ItemsCountForDebugger => _items.Count;
private bool ChannelIsClosedForDebugger => _doneWriting != null;
internal SingleConsumerUnboundedChannel(bool runContinuationsAsynchronously)
{
_runContinuationsAsynchronously = runContinuationsAsynchronously;
_completion = new TaskCompletionSource(runContinuationsAsynchronously ? TaskCreationOptions.RunContinuationsAsynchronously : TaskCreationOptions.None);
base.Reader = new UnboundedChannelReader(this);
base.Writer = new UnboundedChannelWriter(this);
}
IEnumerator<T> IDebugEnumerable<T>.GetEnumerator()
{
return _items.GetEnumerator();
}
}
internal sealed class TaskCompletionSource : TaskCompletionSource<VoidResult>
{
public TaskCompletionSource(TaskCreationOptions creationOptions)
: base(creationOptions)
{
}
public bool TrySetResult()
{
return TrySetResult(default(VoidResult));
}
}
[DebuggerDisplay("Items={ItemsCountForDebugger}, Closed={ChannelIsClosedForDebugger}")]
[DebuggerTypeProxy(typeof(DebugEnumeratorDebugView<>))]
internal sealed class UnboundedChannel<T> : Channel<T>, IDebugEnumerable<T>
{
[DebuggerDisplay("Items={Count}")]
[DebuggerTypeProxy(typeof(DebugEnumeratorDebugView<>))]
private sealed class UnboundedChannelReader : ChannelReader<T>, IDebugEnumerable<T>
{
internal readonly UnboundedChannel<T> _parent;
private readonly AsyncOperation<T> _readerSingleton;
private readonly AsyncOperation<bool> _waiterSingleton;
public override Task Completion => _parent._completion.Task;
public override bool CanCount => true;
public override bool CanPeek => true;
public override int Count => _parent._items.Count;
internal UnboundedChannelReader(UnboundedChannel<T> parent)
{
_parent = parent;
_readerSingleton = new AsyncOperation<T>(parent._runContinuationsAsynchronously, default(CancellationToken), pooled: true);
_waiterSingleton = new AsyncOperation<bool>(parent._runContinuationsAsynchronously, default(CancellationToken), pooled: true);
}
public override ValueTask<T> ReadAsync(CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return new ValueTask<T>(Task.FromCanceled<T>(cancellationToken));
}
UnboundedChannel<T> parent = _parent;
if (parent._items.TryDequeue(out var result))
{
CompleteIfDone(parent);
return new ValueTask<T>(result);
}
lock (parent.SyncObj)
{
if (parent._items.TryDequeue(out result))
{
CompleteIfDone(parent);
return new ValueTask<T>(result);
}
if (parent._doneWriting != null)
{
return ChannelUtilities.GetInvalidCompletionValueTask<T>(parent._doneWriting);
}
if (!cancellationToken.CanBeCanceled)
{
AsyncOperation<T> readerSingleton = _readerSingleton;
if (readerSingleton.TryOwnAndReset())
{
parent._blockedReaders.EnqueueTail(readerSingleton);
return readerSingleton.ValueTaskOfT;
}
}
AsyncOperation<T> asyncOperation = new AsyncOperation<T>(parent._runContinuationsAsynchronously, cancellationToken);
parent._blockedReaders.EnqueueTail(asyncOperation);
return asyncOperation.ValueTaskOfT;
}
}
public override bool TryRead([MaybeNullWhen(false)] out T item)
{
UnboundedChannel<T> parent = _parent;
if (parent._items.TryDequeue(out item))
{
CompleteIfDone(parent);
return true;
}
item = default(T);
return false;
}
public override bool TryPeek([MaybeNullWhen(false)] out T item)
{
return _parent._items.TryPeek(out item);
}
private static void CompleteIfDone(UnboundedChannel<T> parent)
{
if (parent._doneWriting != null && parent._items.IsEmpty)
{
ChannelUtilities.Complete(parent._completion, parent._doneWriting);
}
}
public override ValueTask<bool> WaitToReadAsync(CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return new ValueTask<bool>(Task.FromCanceled<bool>(cancellationToken));
}
if (!_parent._items.IsEmpty)
{
return new ValueTask<bool>(result: true);
}
UnboundedChannel<T> parent = _parent;
lock (parent.SyncObj)
{
if (!parent._items.IsEmpty)
{
return new ValueTask<bool>(result: true);
}
if (parent._doneWriting != null)
{
return (parent._doneWriting != ChannelUtilities.s_doneWritingSentinel) ? new ValueTask<bool>(Task.FromException<bool>(parent._doneWriting)) : default(ValueTask<bool>);
}
if (!cancellationToken.CanBeCanceled)
{
AsyncOperation<bool> waiterSingleton = _waiterSingleton;
if (waiterSingleton.TryOwnAndReset())
{
ChannelUtilities.QueueWaiter(ref parent._waitingReadersTail, waiterSingleton);
return waiterSingleton.ValueTaskOfT;
}
}
AsyncOperation<bool> asyncOperation = new AsyncOperation<bool>(parent._runContinuationsAsynchronously, cancellationToken);
ChannelUtilities.QueueWaiter(ref parent._waitingReadersTail, asyncOperation);
return asyncOperation.ValueTaskOfT;
}
}
IEnumerator<T> IDebugEnumerable<T>.GetEnumerator()
{
return _parent._items.GetEnumerator();
}
}
[DebuggerDisplay("Items={ItemsCountForDebugger}")]
[DebuggerTypeProxy(typeof(DebugEnumeratorDebugView<>))]
private sealed class UnboundedChannelWriter : ChannelWriter<T>, IDebugEnumerable<T>
{
internal readonly UnboundedChannel<T> _parent;
private int ItemsCountForDebugger => _parent._items.Count;
internal UnboundedChannelWriter(UnboundedChannel<T> parent)
{
_parent = parent;
}
public override bool TryComplete(Exception error)
{
UnboundedChannel<T> parent = _parent;
bool isEmpty;
lock (parent.SyncObj)
{
if (parent._doneWriting != null)
{
return false;
}
parent._doneWriting = error ?? ChannelUtilities.s_doneWritingSentinel;
isEmpty = parent._items.IsEmpty;
}
if (isEmpty)
{
ChannelUtilities.Complete(parent._completion, error);
}
ChannelUtilities.FailOperations<AsyncOperation<T>, T>(parent._blockedReaders, ChannelUtilities.CreateInvalidCompletionException(error));
ChannelUtilities.WakeUpWaiters(ref parent._waitingReadersTail, result: false, error);
return true;
}
public override bool TryWrite(T item)
{
UnboundedChannel<T> parent = _parent;
AsyncOperation<bool> listTail;
while (true)
{
AsyncOperation<T> asyncOperation = null;
listTail = null;
lock (parent.SyncObj)
{
if (parent._doneWriting != null)
{
return false;
}
if (parent._blockedReaders.IsEmpty)
{
parent._items.Enqueue(item);
listTail = parent._waitingReadersTail;
if (listTail == null)
{
return true;
}
parent._waitingReadersTail = null;
}
else
{
asyncOperation = parent._blockedReaders.DequeueHead();
}
}
if (asyncOperation == null)
{
break;
}
if (asyncOperation.TrySetResult(item))
{
return true;
}
}
ChannelUtilities.WakeUpWaiters(ref listTail, result: true);
return true;
}
public override ValueTask<bool> WaitToWriteAsync(CancellationToken cancellationToken)
{
Exception doneWriting = _parent._doneWriting;
if (!cancellationToken.IsCancellationRequested)
{
if (doneWriting != null)
{
if (doneWriting == ChannelUtilities.s_doneWritingSentinel)
{
return default(ValueTask<bool>);
}
return new ValueTask<bool>(Task.FromException<bool>(doneWriting));
}
return new ValueTask<bool>(result: true);
}
return new ValueTask<bool>(Task.FromCanceled<bool>(cancellationToken));
}
public override ValueTask WriteAsync(T item, CancellationToken cancellationToken)
{
if (!cancellationToken.IsCancellationRequested)
{
if (!TryWrite(item))
{
return new ValueTask(Task.FromException(ChannelUtilities.CreateInvalidCompletionException(_parent._doneWriting)));
}
return default(ValueTask);
}
return new ValueTask(Task.FromCanceled(cancellationToken));
}
IEnumerator<T> IDebugEnumerable<T>.GetEnumerator()
{
return _parent._items.GetEnumerator();
}
}
private readonly TaskCompletionSource _completion;
private readonly ConcurrentQueue<T> _items = new ConcurrentQueue<T>();
private readonly Deque<AsyncOperation<T>> _blockedReaders = new Deque<AsyncOperation<T>>();
private readonly bool _runContinuationsAsynchronously;
private AsyncOperation<bool> _waitingReadersTail;
private Exception _doneWriting;
private object SyncObj => _items;
private int ItemsCountForDebugger => _items.Count;
private bool ChannelIsClosedForDebugger => _doneWriting != null;
internal UnboundedChannel(bool runContinuationsAsynchronously)
{
_runContinuationsAsynchronously = runContinuationsAsynchronously;
_completion = new TaskCompletionSource(runContinuationsAsynchronously ? TaskCreationOptions.RunContinuationsAsynchronously : TaskCreationOptions.None);
base.Reader = new UnboundedChannelReader(this);
base.Writer = new UnboundedChannelWriter(this);
}
[Conditional("DEBUG")]
private void AssertInvariants()
{
if (!_items.IsEmpty)
{
_ = _runContinuationsAsynchronously;
}
if (!_blockedReaders.IsEmpty || _waitingReadersTail != null)
{
_ = _runContinuationsAsynchronously;
}
_ = _completion.Task.IsCompleted;
}
IEnumerator<T> IDebugEnumerable<T>.GetEnumerator()
{
return _items.GetEnumerator();
}
}
}
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Threading.Tasks;
using System.Threading.Tasks.Sources;
using Microsoft.CodeAnalysis;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("System.Threading.Tasks.Extensions")]
[assembly: AssemblyDescription("System.Threading.Tasks.Extensions")]
[assembly: AssemblyDefaultAlias("System.Threading.Tasks.Extensions")]
[assembly: AssemblyCompany("Microsoft Corporation")]
[assembly: AssemblyProduct("Microsoft® .NET Framework")]
[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")]
[assembly: AssemblyFileVersion("4.6.28619.01")]
[assembly: AssemblyInformationalVersion("4.6.28619.01 @BuiltBy: dlab14-DDVSOWINAGE069 @Branch: release/2.1 @SrcCode: https://github.com/dotnet/corefx/tree/7601f4f6225089ffb291dc7d58293c7bbf5c5d4f")]
[assembly: CLSCompliant(true)]
[assembly: AssemblyMetadata(".NETFrameworkAssembly", "")]
[assembly: AssemblyMetadata("Serviceable", "True")]
[assembly: AssemblyMetadata("PreferInbox", "True")]
[assembly: AssemblyVersion("4.2.0.1")]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class IsReadOnlyAttribute : Attribute
{
}
}
namespace System
{
internal static class ThrowHelper
{
internal static void ThrowArgumentNullException(System.ExceptionArgument argument)
{
throw GetArgumentNullException(argument);
}
internal static void ThrowArgumentOutOfRangeException(System.ExceptionArgument argument)
{
throw GetArgumentOutOfRangeException(argument);
}
private static ArgumentNullException GetArgumentNullException(System.ExceptionArgument argument)
{
return new ArgumentNullException(GetArgumentName(argument));
}
private static ArgumentOutOfRangeException GetArgumentOutOfRangeException(System.ExceptionArgument argument)
{
return new ArgumentOutOfRangeException(GetArgumentName(argument));
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static string GetArgumentName(System.ExceptionArgument argument)
{
return argument.ToString();
}
}
internal enum ExceptionArgument
{
task,
source,
state
}
}
namespace System.Threading.Tasks
{
[StructLayout(LayoutKind.Auto)]
[AsyncMethodBuilder(typeof(AsyncValueTaskMethodBuilder))]
public readonly struct ValueTask : IEquatable<ValueTask>
{
private sealed class ValueTaskSourceAsTask : TaskCompletionSource<bool>
{
private static readonly Action<object> s_completionAction = delegate(object state)
{
IValueTaskSource source;
if (!(state is ValueTaskSourceAsTask valueTaskSourceAsTask) || (source = valueTaskSourceAsTask._source) == null)
{
System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.state);
return;
}
valueTaskSourceAsTask._source = null;
ValueTaskSourceStatus status = source.GetStatus(valueTaskSourceAsTask._token);
try
{
source.GetResult(valueTaskSourceAsTask._token);
valueTaskSourceAsTask.TrySetResult(result: false);
}
catch (Exception exception)
{
if (status == ValueTaskSourceStatus.Canceled)
{
valueTaskSourceAsTask.TrySetCanceled();
}
else
{
valueTaskSourceAsTask.TrySetException(exception);
}
}
};
private IValueTaskSource _source;
private readonly short _token;
public ValueTaskSourceAsTask(IValueTaskSource source, short token)
{
_token = token;
_source = source;
source.OnCompleted(s_completionAction, this, token, ValueTaskSourceOnCompletedFlags.None);
}
}
private static readonly Task s_canceledTask = Task.Delay(-1, new CancellationToken(canceled: true));
internal readonly object _obj;
internal readonly short _token;
internal readonly bool _continueOnCapturedContext;
internal static Task CompletedTask { get; } = Task.Delay(0);
public bool IsCompleted
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
object obj = _obj;
if (obj == null)
{
return true;
}
if (obj is Task task)
{
return task.IsCompleted;
}
return System.Runtime.CompilerServices.Unsafe.As<IValueTaskSource>(obj).GetStatus(_token) != ValueTaskSourceStatus.Pending;
}
}
public bool IsCompletedSuccessfully
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
object obj = _obj;
if (obj == null)
{
return true;
}
if (obj is Task task)
{
return task.Status == TaskStatus.RanToCompletion;
}
return System.Runtime.CompilerServices.Unsafe.As<IValueTaskSource>(obj).GetStatus(_token) == ValueTaskSourceStatus.Succeeded;
}
}
public bool IsFaulted
{
get
{
object obj = _obj;
if (obj == null)
{
return false;
}
if (obj is Task task)
{
return task.IsFaulted;
}
return System.Runtime.CompilerServices.Unsafe.As<IValueTaskSource>(obj).GetStatus(_token) == ValueTaskSourceStatus.Faulted;
}
}
public bool IsCanceled
{
get
{
object obj = _obj;
if (obj == null)
{
return false;
}
if (obj is Task task)
{
return task.IsCanceled;
}
return System.Runtime.CompilerServices.Unsafe.As<IValueTaskSource>(obj).GetStatus(_token) == ValueTaskSourceStatus.Canceled;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueTask(Task task)
{
if (task == null)
{
System.ThrowHelper.ThrowArgumentNullException(System.ExceptionArgument.task);
}
_obj = task;
_continueOnCapturedContext = true;
_token = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueTask(IValueTaskSource source, short token)
{
if (source == null)
{
System.ThrowHelper.ThrowArgumentNullException(System.ExceptionArgument.source);
}
_obj = source;
_token = token;
_continueOnCapturedContext = true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ValueTask(object obj, short token, bool continueOnCapturedContext)
{
_obj = obj;
_token = token;
_continueOnCapturedContext = continueOnCapturedContext;
}
public override int GetHashCode()
{
return _obj?.GetHashCode() ?? 0;
}
public override bool Equals(object obj)
{
if (obj is ValueTask)
{
return Equals((ValueTask)obj);
}
return false;
}
public bool Equals(ValueTask other)
{
if (_obj == other._obj)
{
return _token == other._token;
}
return false;
}
public static bool operator ==(ValueTask left, ValueTask right)
{
return left.Equals(right);
}
public static bool operator !=(ValueTask left, ValueTask right)
{
return !left.Equals(right);
}
public Task AsTask()
{
object obj = _obj;
object obj2;
if (obj != null)
{
obj2 = obj as Task;
if (obj2 == null)
{
return GetTaskForValueTaskSource(System.Runtime.CompilerServices.Unsafe.As<IValueTaskSource>(obj));
}
}
else
{
obj2 = CompletedTask;
}
return (Task)obj2;
}
public ValueTask Preserve()
{
if (_obj != null)
{
return new ValueTask(AsTask());
}
return this;
}
private Task GetTaskForValueTaskSource(IValueTaskSource t)
{
ValueTaskSourceStatus status = t.GetStatus(_token);
if (status != 0)
{
try
{
t.GetResult(_token);
return CompletedTask;
}
catch (Exception exception)
{
if (status == ValueTaskSourceStatus.Canceled)
{
return s_canceledTask;
}
TaskCompletionSource<bool> taskCompletionSource = new TaskCompletionSource<bool>();
taskCompletionSource.TrySetException(exception);
return taskCompletionSource.Task;
}
}
ValueTaskSourceAsTask valueTaskSourceAsTask = new ValueTaskSourceAsTask(t, _token);
return valueTaskSourceAsTask.Task;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[StackTraceHidden]
internal void ThrowIfCompletedUnsuccessfully()
{
object obj = _obj;
if (obj != null)
{
if (obj is Task task)
{
task.GetAwaiter().GetResult();
}
else
{
System.Runtime.CompilerServices.Unsafe.As<IValueTaskSource>(obj).GetResult(_token);
}
}
}
public ValueTaskAwaiter GetAwaiter()
{
return new ValueTaskAwaiter(this);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ConfiguredValueTaskAwaitable ConfigureAwait(bool continueOnCapturedContext)
{
return new ConfiguredValueTaskAwaitable(new ValueTask(_obj, _token, continueOnCapturedContext));
}
}
[StructLayout(LayoutKind.Auto)]
[AsyncMethodBuilder(typeof(AsyncValueTaskMethodBuilder<>))]
public readonly struct ValueTask<TResult> : IEquatable<ValueTask<TResult>>
{
private sealed class ValueTaskSourceAsTask : TaskCompletionSource<TResult>
{
private static readonly Action<object> s_completionAction = delegate(object state)
{
IValueTaskSource<TResult> source;
if (!(state is ValueTaskSourceAsTask valueTaskSourceAsTask) || (source = valueTaskSourceAsTask._source) == null)
{
System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.state);
return;
}
valueTaskSourceAsTask._source = null;
ValueTaskSourceStatus status = source.GetStatus(valueTaskSourceAsTask._token);
try
{
valueTaskSourceAsTask.TrySetResult(source.GetResult(valueTaskSourceAsTask._token));
}
catch (Exception exception)
{
if (status == ValueTaskSourceStatus.Canceled)
{
valueTaskSourceAsTask.TrySetCanceled();
}
else
{
valueTaskSourceAsTask.TrySetException(exception);
}
}
};
private IValueTaskSource<TResult> _source;
private readonly short _token;
public ValueTaskSourceAsTask(IValueTaskSource<TResult> source, short token)
{
_source = source;
_token = token;
source.OnCompleted(s_completionAction, this, token, ValueTaskSourceOnCompletedFlags.None);
}
}
private static Task<TResult> s_canceledTask;
internal readonly object _obj;
internal readonly TResult _result;
internal readonly short _token;
internal readonly bool _continueOnCapturedContext;
public bool IsCompleted
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
object obj = _obj;
if (obj == null)
{
return true;
}
if (obj is Task<TResult> task)
{
return task.IsCompleted;
}
return System.Runtime.CompilerServices.Unsafe.As<IValueTaskSource<TResult>>(obj).GetStatus(_token) != ValueTaskSourceStatus.Pending;
}
}
public bool IsCompletedSuccessfully
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
object obj = _obj;
if (obj == null)
{
return true;
}
if (obj is Task<TResult> task)
{
return task.Status == TaskStatus.RanToCompletion;
}
return System.Runtime.CompilerServices.Unsafe.As<IValueTaskSource<TResult>>(obj).GetStatus(_token) == ValueTaskSourceStatus.Succeeded;
}
}
public bool IsFaulted
{
get
{
object obj = _obj;
if (obj == null)
{
return false;
}
if (obj is Task<TResult> task)
{
return task.IsFaulted;
}
return System.Runtime.CompilerServices.Unsafe.As<IValueTaskSource<TResult>>(obj).GetStatus(_token) == ValueTaskSourceStatus.Faulted;
}
}
public bool IsCanceled
{
get
{
object obj = _obj;
if (obj == null)
{
return false;
}
if (obj is Task<TResult> task)
{
return task.IsCanceled;
}
return System.Runtime.CompilerServices.Unsafe.As<IValueTaskSource<TResult>>(obj).GetStatus(_token) == ValueTaskSourceStatus.Canceled;
}
}
public TResult Result
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
object obj = _obj;
if (obj == null)
{
return _result;
}
if (obj is Task<TResult> task)
{
return task.GetAwaiter().GetResult();
}
return System.Runtime.CompilerServices.Unsafe.As<IValueTaskSource<TResult>>(obj).GetResult(_token);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueTask(TResult result)
{
_result = result;
_obj = null;
_continueOnCapturedContext = true;
_token = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueTask(Task<TResult> task)
{
if (task == null)
{
System.ThrowHelper.ThrowArgumentNullException(System.ExceptionArgument.task);
}
_obj = task;
_result = default(TResult);
_continueOnCapturedContext = true;
_token = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueTask(IValueTaskSource<TResult> source, short token)
{
if (source == null)
{
System.ThrowHelper.ThrowArgumentNullException(System.ExceptionArgument.source);
}
_obj = source;
_token = token;
_result = default(TResult);
_continueOnCapturedContext = true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ValueTask(object obj, TResult result, short token, bool continueOnCapturedContext)
{
_obj = obj;
_result = result;
_token = token;
_continueOnCapturedContext = continueOnCapturedContext;
}
public override int GetHashCode()
{
if (_obj == null)
{
if (_result == null)
{
return 0;
}
return _result.GetHashCode();
}
return _obj.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj is ValueTask<TResult>)
{
return Equals((ValueTask<TResult>)obj);
}
return false;
}
public bool Equals(ValueTask<TResult> other)
{
if (_obj == null && other._obj == null)
{
return EqualityComparer<TResult>.Default.Equals(_result, other._result);
}
if (_obj == other._obj)
{
return _token == other._token;
}
return false;
}
public static bool operator ==(ValueTask<TResult> left, ValueTask<TResult> right)
{
return left.Equals(right);
}
public static bool operator !=(ValueTask<TResult> left, ValueTask<TResult> right)
{
return !left.Equals(right);
}
public Task<TResult> AsTask()
{
object obj = _obj;
if (obj == null)
{
return Task.FromResult(_result);
}
if (obj is Task<TResult> result)
{
return result;
}
return GetTaskForValueTaskSource(System.Runtime.CompilerServices.Unsafe.As<IValueTaskSource<TResult>>(obj));
}
public ValueTask<TResult> Preserve()
{
if (_obj != null)
{
return new ValueTask<TResult>(AsTask());
}
return this;
}
private Task<TResult> GetTaskForValueTaskSource(IValueTaskSource<TResult> t)
{
ValueTaskSourceStatus status = t.GetStatus(_token);
if (status != 0)
{
try
{
return Task.FromResult(t.GetResult(_token));
}
catch (Exception exception)
{
if (status == ValueTaskSourceStatus.Canceled)
{
Task<TResult> task = s_canceledTask;
if (task == null)
{
TaskCompletionSource<TResult> taskCompletionSource = new TaskCompletionSource<TResult>();
taskCompletionSource.TrySetCanceled();
task = (s_canceledTask = taskCompletionSource.Task);
}
return task;
}
TaskCompletionSource<TResult> taskCompletionSource2 = new TaskCompletionSource<TResult>();
taskCompletionSource2.TrySetException(exception);
return taskCompletionSource2.Task;
}
}
ValueTaskSourceAsTask valueTaskSourceAsTask = new ValueTaskSourceAsTask(t, _token);
return valueTaskSourceAsTask.Task;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueTaskAwaiter<TResult> GetAwaiter()
{
return new ValueTaskAwaiter<TResult>(this);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ConfiguredValueTaskAwaitable<TResult> ConfigureAwait(bool continueOnCapturedContext)
{
return new ConfiguredValueTaskAwaitable<TResult>(new ValueTask<TResult>(_obj, _result, _token, continueOnCapturedContext));
}
public override string ToString()
{
if (IsCompletedSuccessfully)
{
TResult result = Result;
if (result != null)
{
return result.ToString();
}
}
return string.Empty;
}
}
}
namespace System.Threading.Tasks.Sources
{
[Flags]
public enum ValueTaskSourceOnCompletedFlags
{
None = 0,
UseSchedulingContext = 1,
FlowExecutionContext = 2
}
public enum ValueTaskSourceStatus
{
Pending,
Succeeded,
Faulted,
Canceled
}
public interface IValueTaskSource
{
ValueTaskSourceStatus GetStatus(short token);
void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags);
void GetResult(short token);
}
public interface IValueTaskSource<out TResult>
{
ValueTaskSourceStatus GetStatus(short token);
void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags);
TResult GetResult(short token);
}
}
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false, AllowMultiple = false)]
public sealed class AsyncMethodBuilderAttribute : Attribute
{
public Type BuilderType { get; }
public AsyncMethodBuilderAttribute(Type builderType)
{
BuilderType = builderType;
}
}
[StructLayout(LayoutKind.Auto)]
public struct AsyncValueTaskMethodBuilder
{
private AsyncTaskMethodBuilder _methodBuilder;
private bool _haveResult;
private bool _useBuilder;
public ValueTask Task
{
get
{
if (_haveResult)
{
return default(ValueTask);
}
_useBuilder = true;
return new ValueTask(_methodBuilder.Task);
}
}
public static AsyncValueTaskMethodBuilder Create()
{
return default(AsyncValueTaskMethodBuilder);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
{
_methodBuilder.Start(ref stateMachine);
}
public void SetStateMachine(IAsyncStateMachine stateMachine)
{
_methodBuilder.SetStateMachine(stateMachine);
}
public void SetResult()
{
if (_useBuilder)
{
_methodBuilder.SetResult();
}
else
{
_haveResult = true;
}
}
public void SetException(Exception exception)
{
_methodBuilder.SetException(exception);
}
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine
{
_useBuilder = true;
_methodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine);
}
[SecuritySafeCritical]
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine
{
_useBuilder = true;
_methodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
}
}
[StructLayout(LayoutKind.Auto)]
public struct AsyncValueTaskMethodBuilder<TResult>
{
private AsyncTaskMethodBuilder<TResult> _methodBuilder;
private TResult _result;
private bool _haveResult;
private bool _useBuilder;
public ValueTask<TResult> Task
{
get
{
if (_haveResult)
{
return new ValueTask<TResult>(_result);
}
_useBuilder = true;
return new ValueTask<TResult>(_methodBuilder.Task);
}
}
public static AsyncValueTaskMethodBuilder<TResult> Create()
{
return default(AsyncValueTaskMethodBuilder<TResult>);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
{
_methodBuilder.Start(ref stateMachine);
}
public void SetStateMachine(IAsyncStateMachine stateMachine)
{
_methodBuilder.SetStateMachine(stateMachine);
}
public void SetResult(TResult result)
{
if (_useBuilder)
{
_methodBuilder.SetResult(result);
return;
}
_result = result;
_haveResult = true;
}
public void SetException(Exception exception)
{
_methodBuilder.SetException(exception);
}
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine
{
_useBuilder = true;
_methodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine);
}
[SecuritySafeCritical]
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine
{
_useBuilder = true;
_methodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
}
}
[StructLayout(LayoutKind.Auto)]
public readonly struct ConfiguredValueTaskAwaitable
{
[StructLayout(LayoutKind.Auto)]
public readonly struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion, INotifyCompletion
{
private readonly ValueTask _value;
public bool IsCompleted
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return _value.IsCompleted;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ConfiguredValueTaskAwaiter(ValueTask value)
{
_value = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[StackTraceHidden]
public void GetResult()
{
_value.ThrowIfCompletedUnsuccessfully();
}
public void OnCompleted(Action continuation)
{
object obj = _value._obj;
if (obj is Task task)
{
task.ConfigureAwait(_value._continueOnCapturedContext).GetAwaiter().OnCompleted(continuation);
}
else if (obj != null)
{
System.Runtime.CompilerServices.Unsafe.As<IValueTaskSource>(obj).OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, _value._token, ValueTaskSourceOnCompletedFlags.FlowExecutionContext | (_value._continueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None));
}
else
{
ValueTask.CompletedTask.ConfigureAwait(_value._continueOnCapturedContext).GetAwaiter().OnCompleted(continuation);
}
}
public void UnsafeOnCompleted(Action continuation)
{
object obj = _value._obj;
if (obj is Task task)
{
task.ConfigureAwait(_value._continueOnCapturedContext).GetAwaiter().UnsafeOnCompleted(continuation);
}
else if (obj != null)
{
System.Runtime.CompilerServices.Unsafe.As<IValueTaskSource>(obj).OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, _value._token, _value._continueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None);
}
else
{
ValueTask.CompletedTask.ConfigureAwait(_value._continueOnCapturedContext).GetAwaiter().UnsafeOnCompleted(continuation);
}
}
}
private readonly ValueTask _value;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ConfiguredValueTaskAwaitable(ValueTask value)
{
_value = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ConfiguredValueTaskAwaiter GetAwaiter()
{
return new ConfiguredValueTaskAwaiter(_value);
}
}
[StructLayout(LayoutKind.Auto)]
public readonly struct ConfiguredValueTaskAwaitable<TResult>
{
[StructLayout(LayoutKind.Auto)]
public readonly struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion, INotifyCompletion
{
private readonly ValueTask<TResult> _value;
public bool IsCompleted
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return _value.IsCompleted;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ConfiguredValueTaskAwaiter(ValueTask<TResult> value)
{
_value = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[StackTraceHidden]
public TResult GetResult()
{
return _value.Result;
}
public void OnCompleted(Action continuation)
{
object obj = _value._obj;
if (obj is Task<TResult> task)
{
task.ConfigureAwait(_value._continueOnCapturedContext).GetAwaiter().OnCompleted(continuation);
}
else if (obj != null)
{
System.Runtime.CompilerServices.Unsafe.As<IValueTaskSource<TResult>>(obj).OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, _value._token, ValueTaskSourceOnCompletedFlags.FlowExecutionContext | (_value._continueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None));
}
else
{
ValueTask.CompletedTask.ConfigureAwait(_value._continueOnCapturedContext).GetAwaiter().OnCompleted(continuation);
}
}
public void UnsafeOnCompleted(Action continuation)
{
object obj = _value._obj;
if (obj is Task<TResult> task)
{
task.ConfigureAwait(_value._continueOnCapturedContext).GetAwaiter().UnsafeOnCompleted(continuation);
}
else if (obj != null)
{
System.Runtime.CompilerServices.Unsafe.As<IValueTaskSource<TResult>>(obj).OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, _value._token, _value._continueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None);
}
else
{
ValueTask.CompletedTask.ConfigureAwait(_value._continueOnCapturedContext).GetAwaiter().UnsafeOnCompleted(continuation);
}
}
}
private readonly ValueTask<TResult> _value;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ConfiguredValueTaskAwaitable(ValueTask<TResult> value)
{
_value = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ConfiguredValueTaskAwaiter GetAwaiter()
{
return new ConfiguredValueTaskAwaiter(_value);
}
}
public readonly struct ValueTaskAwaiter : ICriticalNotifyCompletion, INotifyCompletion
{
internal static readonly Action<object> s_invokeActionDelegate = delegate(object state)
{
if (!(state is Action action))
{
System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.state);
}
else
{
action();
}
};
private readonly ValueTask _value;
public bool IsCompleted
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return _value.IsCompleted;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ValueTaskAwaiter(ValueTask value)
{
_value = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[StackTraceHidden]
public void GetResult()
{
_value.ThrowIfCompletedUnsuccessfully();
}
public void OnCompleted(Action continuation)
{
object obj = _value._obj;
if (obj is Task task)
{
task.GetAwaiter().OnCompleted(continuation);
}
else if (obj != null)
{
System.Runtime.CompilerServices.Unsafe.As<IValueTaskSource>(obj).OnCompleted(s_invokeActionDelegate, continuation, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext | ValueTaskSourceOnCompletedFlags.FlowExecutionContext);
}
else
{
ValueTask.CompletedTask.GetAwaiter().OnCompleted(continuation);
}
}
public void UnsafeOnCompleted(Action continuation)
{
object obj = _value._obj;
if (obj is Task task)
{
task.GetAwaiter().UnsafeOnCompleted(continuation);
}
else if (obj != null)
{
System.Runtime.CompilerServices.Unsafe.As<IValueTaskSource>(obj).OnCompleted(s_invokeActionDelegate, continuation, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext);
}
else
{
ValueTask.CompletedTask.GetAwaiter().UnsafeOnCompleted(continuation);
}
}
}
public readonly struct ValueTaskAwaiter<TResult> : ICriticalNotifyCompletion, INotifyCompletion
{
private readonly ValueTask<TResult> _value;
public bool IsCompleted
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return _value.IsCompleted;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ValueTaskAwaiter(ValueTask<TResult> value)
{
_value = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[StackTraceHidden]
public TResult GetResult()
{
return _value.Result;
}
public void OnCompleted(Action continuation)
{
object obj = _value._obj;
if (obj is Task<TResult> task)
{
task.GetAwaiter().OnCompleted(continuation);
}
else if (obj != null)
{
System.Runtime.CompilerServices.Unsafe.As<IValueTaskSource<TResult>>(obj).OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext | ValueTaskSourceOnCompletedFlags.FlowExecutionContext);
}
else
{
ValueTask.CompletedTask.GetAwaiter().OnCompleted(continuation);
}
}
public void UnsafeOnCompleted(Action continuation)
{
object obj = _value._obj;
if (obj is Task<TResult> task)
{
task.GetAwaiter().UnsafeOnCompleted(continuation);
}
else if (obj != null)
{
System.Runtime.CompilerServices.Unsafe.As<IValueTaskSource<TResult>>(obj).OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext);
}
else
{
ValueTask.CompletedTask.GetAwaiter().UnsafeOnCompleted(continuation);
}
}
}
}
namespace System.Diagnostics
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method, Inherited = false)]
internal sealed class StackTraceHiddenAttribute : Attribute
{
}
}
using System;
using System.Diagnostics;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using FxResources.System.ValueTuple;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: AssemblyTitle("System.ValueTuple")]
[assembly: AssemblyDescription("System.ValueTuple")]
[assembly: AssemblyDefaultAlias("System.ValueTuple")]
[assembly: AssemblyCompany("Microsoft Corporation")]
[assembly: AssemblyProduct("Microsoft® .NET Framework")]
[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")]
[assembly: AssemblyFileVersion("4.6.26515.06")]
[assembly: AssemblyInformationalVersion("4.6.26515.06 @BuiltBy: dlab-DDVSOWINAGE059 @Branch: release/2.1 @SrcCode: https://github.com/dotnet/corefx/tree/30ab651fcb4354552bd4891619a0bdd81e0ebdbf")]
[assembly: CLSCompliant(true)]
[assembly: AssemblyMetadata(".NETFrameworkAssembly", "")]
[assembly: AssemblyMetadata("Serviceable", "True")]
[assembly: AssemblyMetadata("PreferInbox", "True")]
[assembly: AssemblyVersion("4.0.3.0")]
[assembly: TypeForwardedTo(typeof(TupleElementNamesAttribute))]
[assembly: TypeForwardedTo(typeof(TupleExtensions))]
[assembly: TypeForwardedTo(typeof(ValueTuple))]
[assembly: TypeForwardedTo(typeof(ValueTuple<>))]
[assembly: TypeForwardedTo(typeof(ValueTuple<, >))]
[assembly: TypeForwardedTo(typeof(ValueTuple<, , >))]
[assembly: TypeForwardedTo(typeof(ValueTuple<, , , >))]
[assembly: TypeForwardedTo(typeof(ValueTuple<, , , , >))]
[assembly: TypeForwardedTo(typeof(ValueTuple<, , , , , >))]
[assembly: TypeForwardedTo(typeof(ValueTuple<, , , , , , >))]
[assembly: TypeForwardedTo(typeof(ValueTuple<, , , , , , , >))]
namespace FxResources.System.ValueTuple
{
internal static class SR
{
}
}
namespace System
{
internal static class SR
{
private static ResourceManager s_resourceManager;
private static ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new ResourceManager(ResourceType));
internal static Type ResourceType { get; } = typeof(SR);
internal static string ArgumentException_ValueTupleIncorrectType => GetResourceString("ArgumentException_ValueTupleIncorrectType", null);
internal static string ArgumentException_ValueTupleLastArgumentNotAValueTuple => GetResourceString("ArgumentException_ValueTupleLastArgumentNotAValueTuple", null);
[MethodImpl(MethodImplOptions.NoInlining)]
private static bool UsingResourceKeys()
{
return false;
}
internal static string GetResourceString(string resourceKey, string defaultString)
{
string text = null;
try
{
text = ResourceManager.GetString(resourceKey);
}
catch (MissingManifestResourceException)
{
}
if (defaultString != null && resourceKey.Equals(text, StringComparison.Ordinal))
{
return defaultString;
}
return text;
}
internal static string Format(string resourceFormat, params object[] args)
{
if (args != null)
{
if (UsingResourceKeys())
{
return resourceFormat + string.Join(", ", args);
}
return string.Format(resourceFormat, args);
}
return resourceFormat;
}
internal static string Format(string resourceFormat, object p1)
{
if (UsingResourceKeys())
{
return string.Join(", ", resourceFormat, p1);
}
return string.Format(resourceFormat, p1);
}
internal static string Format(string resourceFormat, object p1, object p2)
{
if (UsingResourceKeys())
{
return string.Join(", ", resourceFormat, p1, p2);
}
return string.Format(resourceFormat, p1, p2);
}
internal static string Format(string resourceFormat, object p1, object p2, object p3)
{
if (UsingResourceKeys())
{
return string.Join(", ", resourceFormat, p1, p2, p3);
}
return string.Format(resourceFormat, p1, p2, p3);
}
}
}
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Buttplug.Client;
using Buttplug.Client.Connectors.WebsocketConnector;
using Buttplug.Core;
using IL;
using Microsoft.CodeAnalysis;
using Mono.Cecil.Cil;
using MonoMod.Cil;
using On;
using UnityEngine;
using ViralTremors.Buttplug;
using ViralTremors.Utils;
using Zorro.Core;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyMetadata("ContentWarning.VanillaCompatible", "true")]
[assembly: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("ViralTremors")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("buttplug.io integration for Content Warning")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ViralTremors")]
[assembly: AssemblyTitle("ViralTremors")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace ViralTremors
{
[BepInPlugin("ViralTremors", "ViralTremors", "1.0.0")]
public class ViralTremors : BaseUnityPlugin
{
public static ViralTremors Instance { get; private set; }
internal static ManualLogSource Logger { get; private set; }
internal static DeviceManager DeviceManager { get; private set; }
private void Awake()
{
Logger = ((BaseUnityPlugin)this).Logger;
Instance = this;
DeviceManager = new DeviceManager("ViralTremors");
DeviceManager.ConnectDevices();
Hook();
Logger.LogInfo((object)"ViralTremors v1.0.0 has loaded!");
}
private static void Hook()
{
(MethodInfo, Attribute)[] methodsWithAttribute = ReflectionUtility.GetMethodsWithAttribute<PatchInitAttribute>();
(MethodInfo, Attribute)[] array = methodsWithAttribute;
for (int i = 0; i < array.Length; i++)
{
var (methodInfo, _) = array[i];
methodInfo.Invoke(null, Array.Empty<object>());
}
Logger.LogInfo((object)"Hooking finished");
}
}
public static class MyPluginInfo
{
public const string PLUGIN_GUID = "ViralTremors";
public const string PLUGIN_NAME = "ViralTremors";
public const string PLUGIN_VERSION = "1.0.0";
}
}
namespace ViralTremors.Utils
{
[AttributeUsage(AttributeTargets.Method)]
public class PatchInitAttribute : Attribute
{
}
}
namespace ViralTremors.Hooks
{
public class BombItemPatches
{
[CompilerGenerated]
private static class <>O
{
public static Manipulator <0>__BombItemOnUpdate;
}
[PatchInit]
public static void Init()
{
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Expected O, but got Unknown
ViralTremors.Logger.LogInfo((object)"Patching BombItem functions.");
object obj = <>O.<0>__BombItemOnUpdate;
if (obj == null)
{
Manipulator val = BombItemOnUpdate;
<>O.<0>__BombItemOnUpdate = val;
obj = (object)val;
}
BombItem.Update += (Manipulator)obj;
}
private static void BombItemOnUpdate(ILContext il)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Expected O, but got Unknown
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
ILCursor val = new ILCursor(il);
val.GotoNext((MoveType)0, new Func<Instruction, bool>[1]
{
(Instruction instruction) => ILPatternMatchingExt.MatchRet(instruction)
});
val.Emit(OpCodes.Call, (MethodBase)typeof(BombItemPatches).GetMethods().FirstOrDefault((MethodInfo x) => x.Name == "Vibrate"));
}
public static void Vibrate()
{
if (ViralTremors.DeviceManager.IsConnected() && Config.BombExplosion.Enabled.Value)
{
ViralTremors.DeviceManager.VibrateConnectedDevicesWithDuration(Config.BombExplosion.Strength.Value, Config.BombExplosion.Duration.Value);
}
}
}
public static class DivingBellPatches
{
[CompilerGenerated]
private static class <>O
{
public static hook_GoToSurface <0>__DivingBellOnGoToSurface;
public static hook_GoUnderground <1>__DivingBellOnGoUnderground;
}
[PatchInit]
public static void Init()
{
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Expected O, but got Unknown
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_004a: Expected O, but got Unknown
ViralTremors.Logger.LogInfo((object)"Patching DiveBell functions.");
object obj = <>O.<0>__DivingBellOnGoToSurface;
if (obj == null)
{
hook_GoToSurface val = DivingBellOnGoToSurface;
<>O.<0>__DivingBellOnGoToSurface = val;
obj = (object)val;
}
DivingBell.GoToSurface += (hook_GoToSurface)obj;
object obj2 = <>O.<1>__DivingBellOnGoUnderground;
if (obj2 == null)
{
hook_GoUnderground val2 = DivingBellOnGoUnderground;
<>O.<1>__DivingBellOnGoUnderground = val2;
obj2 = (object)val2;
}
DivingBell.GoUnderground += (hook_GoUnderground)obj2;
}
private static void DivingBellOnGoUnderground(orig_GoUnderground orig, DivingBell self)
{
orig.Invoke(self);
if (ViralTremors.DeviceManager.IsConnected() && Config.DivingBell.Traveling.Enabled.Value)
{
ViralTremors.DeviceManager.VibrateConnectedDevicesWithDuration(Config.DivingBell.Traveling.Strength.Value, Config.DivingBell.Traveling.Duration.Value);
}
}
private static void DivingBellOnGoToSurface(orig_GoToSurface orig, DivingBell self)
{
orig.Invoke(self);
if (ViralTremors.DeviceManager.IsConnected() && Config.DivingBell.Returning.Enabled.Value)
{
ViralTremors.DeviceManager.VibrateConnectedDevicesWithDuration(Config.DivingBell.Traveling.Strength.Value, Config.DivingBell.Returning.Duration.Value);
}
}
}
public static class JumpScareSoundPatches
{
[CompilerGenerated]
private static class <>O
{
public static hook_Scare <0>__JumpScareSoundOnScare;
}
[PatchInit]
public static void Init()
{
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Expected O, but got Unknown
ViralTremors.Logger.LogInfo((object)"Patching JumpScareSound functions.");
object obj = <>O.<0>__JumpScareSoundOnScare;
if (obj == null)
{
hook_Scare val = JumpScareSoundOnScare;
<>O.<0>__JumpScareSoundOnScare = val;
obj = (object)val;
}
JumpScareSound.Scare += (hook_Scare)obj;
}
private static void JumpScareSoundOnScare(orig_Scare orig, JumpScareSound self)
{
orig.Invoke(self);
if (ViralTremors.DeviceManager.IsConnected() && Config.Jumpscare.Enabled.Value)
{
ViralTremors.DeviceManager.VibrateConnectedDevicesWithDuration(Config.Jumpscare.Strength.Value, Config.Jumpscare.Duration.Value);
}
}
}
public static class PlayerPatches
{
[CompilerGenerated]
private static class <>O
{
public static hook_TakeDamage <0>__PlayerOnTakeDamage;
public static hook_Die <1>__PlayerOnDie;
public static hook_CallRevive <2>__PlayerOnCallRevive;
public static hook_CallHeal <3>__PlayerOnHeal;
}
[PatchInit]
public static void Init()
{
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Expected O, but got Unknown
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_004a: Expected O, but got Unknown
//IL_005f: Unknown result type (might be due to invalid IL or missing references)
//IL_0064: Unknown result type (might be due to invalid IL or missing references)
//IL_006a: Expected O, but got Unknown
//IL_007f: Unknown result type (might be due to invalid IL or missing references)
//IL_0084: Unknown result type (might be due to invalid IL or missing references)
//IL_008a: Expected O, but got Unknown
ViralTremors.Logger.LogInfo((object)"Patching Player functions.");
object obj = <>O.<0>__PlayerOnTakeDamage;
if (obj == null)
{
hook_TakeDamage val = PlayerOnTakeDamage;
<>O.<0>__PlayerOnTakeDamage = val;
obj = (object)val;
}
Player.TakeDamage += (hook_TakeDamage)obj;
object obj2 = <>O.<1>__PlayerOnDie;
if (obj2 == null)
{
hook_Die val2 = PlayerOnDie;
<>O.<1>__PlayerOnDie = val2;
obj2 = (object)val2;
}
Player.Die += (hook_Die)obj2;
object obj3 = <>O.<2>__PlayerOnCallRevive;
if (obj3 == null)
{
hook_CallRevive val3 = PlayerOnCallRevive;
<>O.<2>__PlayerOnCallRevive = val3;
obj3 = (object)val3;
}
Player.CallRevive += (hook_CallRevive)obj3;
object obj4 = <>O.<3>__PlayerOnHeal;
if (obj4 == null)
{
hook_CallHeal val4 = PlayerOnHeal;
<>O.<3>__PlayerOnHeal = val4;
obj4 = (object)val4;
}
Player.CallHeal += (hook_CallHeal)obj4;
}
private static bool PlayerOnHeal(orig_CallHeal orig, Player self, float healamount)
{
bool result = orig.Invoke(self, healamount);
if (!self.IsLocal)
{
return result;
}
if (ViralTremors.DeviceManager.IsConnected() && Config.Player.Heal.Enabled.Value)
{
ViralTremors.DeviceManager.VibrateConnectedDevicesWithDuration(Config.Player.Heal.Strength.Value, Config.Player.Heal.Duration.Value);
}
return result;
}
private static void PlayerOnCallRevive(orig_CallRevive orig, Player self)
{
orig.Invoke(self);
if (self.IsLocal && ViralTremors.DeviceManager.IsConnected() && Config.Player.Revive.Enabled.Value)
{
ViralTremors.DeviceManager.VibrateConnectedDevicesWithDuration(Config.Player.Revive.Strength.Value, Config.Player.Revive.Duration.Value);
}
}
private static void PlayerOnDie(orig_Die orig, Player self)
{
orig.Invoke(self);
if (self.IsLocal && ViralTremors.DeviceManager.IsConnected() && Config.Player.Death.Enabled.Value)
{
ViralTremors.DeviceManager.VibrateConnectedDevicesWithDuration(Config.Player.Death.Strength.Value, Config.Player.Death.Duration.Value);
}
}
private static void PlayerOnTakeDamage(orig_TakeDamage orig, Player self, float damage)
{
orig.Invoke(self, damage);
if (self.IsLocal && ViralTremors.DeviceManager.IsConnected() && Config.Player.DamageTaken.Enabled.Value)
{
ViralTremors.DeviceManager.VibrateConnectedDevicesWithDuration(damage / 100f, Config.Player.DamageTaken.Duration.Value);
}
}
}
public static class RoomStatsHolderPatches
{
[CompilerGenerated]
private static class <>O
{
public static hook_AddMoney <0>__RoomStatsHolderOnAddMoney;
}
[PatchInit]
public static void Init()
{
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Expected O, but got Unknown
ViralTremors.Logger.LogInfo((object)"Patching RoomStatsHolder functions.");
object obj = <>O.<0>__RoomStatsHolderOnAddMoney;
if (obj == null)
{
hook_AddMoney val = RoomStatsHolderOnAddMoney;
<>O.<0>__RoomStatsHolderOnAddMoney = val;
obj = (object)val;
}
RoomStatsHolder.AddMoney += (hook_AddMoney)obj;
}
private static void RoomStatsHolderOnAddMoney(orig_AddMoney orig, RoomStatsHolder self, int money)
{
orig.Invoke(self, money);
if (ViralTremors.DeviceManager.IsConnected() && Config.MoneyAdded.Enabled.Value)
{
ViralTremors.DeviceManager.VibrateConnectedDevicesWithDuration(Config.MoneyAdded.Strength.Value, Config.MoneyAdded.Duration.Value);
}
}
}
public static class ShockStickPatches
{
[CompilerGenerated]
private static class <>O
{
public static hook_OnShock <0>__ShockStickOnOnShock;
}
[PatchInit]
public static void Init()
{
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Expected O, but got Unknown
ViralTremors.Logger.LogInfo((object)"Patching ShockStick functions.");
object obj = <>O.<0>__ShockStickOnOnShock;
if (obj == null)
{
hook_OnShock val = ShockStickOnOnShock;
<>O.<0>__ShockStickOnOnShock = val;
obj = (object)val;
}
ShockStick.OnShock += (hook_OnShock)obj;
}
private static void ShockStickOnOnShock(orig_OnShock orig, ShockStick self, Player playertoshock)
{
orig.Invoke(self, playertoshock);
if (ViralTremors.DeviceManager.IsConnected() && Config.Item.ShockStick.Enabled.Value)
{
ViralTremors.DeviceManager.VibrateConnectedDevicesWithDuration(Config.Item.ShockStick.Strength.Value, Config.Item.ShockStick.Duration.Value);
}
}
}
public static class WeepingEnemyPatches
{
[CompilerGenerated]
private static class <>O
{
public static hook_TryCapturePlayer <0>__TryCapturePlayerPatch;
}
[PatchInit]
public static void Init()
{
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Expected O, but got Unknown
ViralTremors.Logger.LogInfo((object)"Patching Bot_Weeping functions.");
object obj = <>O.<0>__TryCapturePlayerPatch;
if (obj == null)
{
hook_TryCapturePlayer val = TryCapturePlayerPatch;
<>O.<0>__TryCapturePlayerPatch = val;
obj = (object)val;
}
Bot_Weeping.TryCapturePlayer += (hook_TryCapturePlayer)obj;
}
private static void TryCapturePlayerPatch(orig_TryCapturePlayer original, Bot_Weeping self)
{
original.Invoke(self);
if (ViralTremors.DeviceManager.IsConnected() && Config.Enemy.Weeping.Capture.Enabled.Value)
{
ViralTremors.DeviceManager.VibrateConnectedDevicesWithDuration(Config.Enemy.Weeping.Capture.Strength.Value, Config.Enemy.Weeping.Capture.Duration.Value);
}
}
}
}
namespace ViralTremors.Buttplug
{
internal static class Config
{
internal static class Player
{
internal static class DamageTaken
{
internal static ConfigEntry<bool>? Enabled { get; set; }
internal static ConfigEntry<float>? Duration { get; set; }
}
internal static class Death
{
internal static ConfigEntry<bool>? Enabled { get; set; }
internal static ConfigEntry<float>? Duration { get; set; }
internal static ConfigEntry<float>? Strength { get; set; }
}
internal static class Revive
{
internal static ConfigEntry<bool>? Enabled { get; set; }
internal static ConfigEntry<float>? Duration { get; set; }
internal static ConfigEntry<float>? Strength { get; set; }
}
internal static class Heal
{
internal static ConfigEntry<bool>? Enabled { get; set; }
internal static ConfigEntry<float>? Duration { get; set; }
internal static ConfigEntry<float>? Strength { get; set; }
}
}
internal static class Enemy
{
internal static class Weeping
{
internal static class Capture
{
internal static ConfigEntry<bool>? Enabled { get; set; }
internal static ConfigEntry<float>? Duration { get; set; }
internal static ConfigEntry<float>? Strength { get; set; }
}
}
}
internal static class Item
{
internal static class ShockStick
{
internal static ConfigEntry<bool>? Enabled { get; set; }
internal static ConfigEntry<float>? Duration { get; set; }
internal static ConfigEntry<float>? Strength { get; set; }
}
}
internal static class DivingBell
{
internal static class Returning
{
internal static ConfigEntry<bool>? Enabled { get; set; }
internal static ConfigEntry<float>? Duration { get; set; }
internal static ConfigEntry<float>? Strength { get; set; }
}
internal static class Traveling
{
internal static ConfigEntry<bool>? Enabled { get; set; }
internal static ConfigEntry<float>? Duration { get; set; }
internal static ConfigEntry<float>? Strength { get; set; }
}
}
internal static class MoneyAdded
{
internal static ConfigEntry<bool>? Enabled { get; set; }
internal static ConfigEntry<float>? Duration { get; set; }
internal static ConfigEntry<float>? Strength { get; set; }
}
internal static class Jumpscare
{
internal static ConfigEntry<bool>? Enabled { get; set; }
internal static ConfigEntry<float>? Duration { get; set; }
internal static ConfigEntry<float>? Strength { get; set; }
}
internal static class BombExplosion
{
internal static ConfigEntry<bool>? Enabled { get; set; }
internal static ConfigEntry<float>? Duration { get; set; }
internal static ConfigEntry<float>? Strength { get; set; }
}
private static ConfigFile ConfigFile { get; set; }
internal static ConfigEntry<string> ServerUri { get; set; }
static Config()
{
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_001a: Expected O, but got Unknown
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
//IL_005d: Unknown result type (might be due to invalid IL or missing references)
//IL_0067: Expected O, but got Unknown
//IL_0067: Expected O, but got Unknown
//IL_007b: Unknown result type (might be due to invalid IL or missing references)
//IL_0090: Unknown result type (might be due to invalid IL or missing references)
//IL_009a: Expected O, but got Unknown
//IL_009a: Expected O, but got Unknown
//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
//IL_00c9: Expected O, but got Unknown
//IL_00c9: Expected O, but got Unknown
//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
//IL_00fc: Expected O, but got Unknown
//IL_00fc: Expected O, but got Unknown
//IL_0110: Unknown result type (might be due to invalid IL or missing references)
//IL_0125: Unknown result type (might be due to invalid IL or missing references)
//IL_012f: Expected O, but got Unknown
//IL_012f: Expected O, but got Unknown
//IL_0143: Unknown result type (might be due to invalid IL or missing references)
//IL_0154: Unknown result type (might be due to invalid IL or missing references)
//IL_015e: Expected O, but got Unknown
//IL_015e: Expected O, but got Unknown
//IL_0172: Unknown result type (might be due to invalid IL or missing references)
//IL_0187: Unknown result type (might be due to invalid IL or missing references)
//IL_0191: Expected O, but got Unknown
//IL_0191: Expected O, but got Unknown
//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
//IL_01c4: Expected O, but got Unknown
//IL_01c4: Expected O, but got Unknown
//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
//IL_01f3: Expected O, but got Unknown
//IL_01f3: Expected O, but got Unknown
//IL_0207: Unknown result type (might be due to invalid IL or missing references)
//IL_021c: Unknown result type (might be due to invalid IL or missing references)
//IL_0226: Expected O, but got Unknown
//IL_0226: Expected O, but got Unknown
//IL_023a: Unknown result type (might be due to invalid IL or missing references)
//IL_024f: Unknown result type (might be due to invalid IL or missing references)
//IL_0259: Expected O, but got Unknown
//IL_0259: Expected O, but got Unknown
//IL_026d: Unknown result type (might be due to invalid IL or missing references)
//IL_027e: Unknown result type (might be due to invalid IL or missing references)
//IL_0288: Expected O, but got Unknown
//IL_0288: Expected O, but got Unknown
//IL_029c: Unknown result type (might be due to invalid IL or missing references)
//IL_02b1: Unknown result type (might be due to invalid IL or missing references)
//IL_02bb: Expected O, but got Unknown
//IL_02bb: Expected O, but got Unknown
//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
//IL_02e4: Unknown result type (might be due to invalid IL or missing references)
//IL_02ee: Expected O, but got Unknown
//IL_02ee: Expected O, but got Unknown
//IL_0302: Unknown result type (might be due to invalid IL or missing references)
//IL_0313: Unknown result type (might be due to invalid IL or missing references)
//IL_031d: Expected O, but got Unknown
//IL_031d: Expected O, but got Unknown
//IL_0331: Unknown result type (might be due to invalid IL or missing references)
//IL_0346: Unknown result type (might be due to invalid IL or missing references)
//IL_0350: Expected O, but got Unknown
//IL_0350: Expected O, but got Unknown
//IL_0364: Unknown result type (might be due to invalid IL or missing references)
//IL_0379: Unknown result type (might be due to invalid IL or missing references)
//IL_0383: Expected O, but got Unknown
//IL_0383: Expected O, but got Unknown
//IL_0397: Unknown result type (might be due to invalid IL or missing references)
//IL_03a8: Unknown result type (might be due to invalid IL or missing references)
//IL_03b2: Expected O, but got Unknown
//IL_03b2: Expected O, but got Unknown
//IL_03c6: Unknown result type (might be due to invalid IL or missing references)
//IL_03db: Unknown result type (might be due to invalid IL or missing references)
//IL_03e5: Expected O, but got Unknown
//IL_03e5: Expected O, but got Unknown
//IL_03f9: Unknown result type (might be due to invalid IL or missing references)
//IL_040e: Unknown result type (might be due to invalid IL or missing references)
//IL_0418: Expected O, but got Unknown
//IL_0418: Expected O, but got Unknown
//IL_042c: Unknown result type (might be due to invalid IL or missing references)
//IL_043d: Unknown result type (might be due to invalid IL or missing references)
//IL_0447: Expected O, but got Unknown
//IL_0447: Expected O, but got Unknown
//IL_045b: Unknown result type (might be due to invalid IL or missing references)
//IL_0470: Unknown result type (might be due to invalid IL or missing references)
//IL_047a: Expected O, but got Unknown
//IL_047a: Expected O, but got Unknown
//IL_048e: Unknown result type (might be due to invalid IL or missing references)
//IL_04a3: Unknown result type (might be due to invalid IL or missing references)
//IL_04ad: Expected O, but got Unknown
//IL_04ad: Expected O, but got Unknown
//IL_04c1: Unknown result type (might be due to invalid IL or missing references)
//IL_04d2: Unknown result type (might be due to invalid IL or missing references)
//IL_04dc: Expected O, but got Unknown
//IL_04dc: Expected O, but got Unknown
//IL_04f0: Unknown result type (might be due to invalid IL or missing references)
//IL_0505: Unknown result type (might be due to invalid IL or missing references)
//IL_050f: Expected O, but got Unknown
//IL_050f: Expected O, but got Unknown
//IL_0523: Unknown result type (might be due to invalid IL or missing references)
//IL_0538: Unknown result type (might be due to invalid IL or missing references)
//IL_0542: Expected O, but got Unknown
//IL_0542: Expected O, but got Unknown
//IL_0556: Unknown result type (might be due to invalid IL or missing references)
//IL_0567: Unknown result type (might be due to invalid IL or missing references)
//IL_0571: Expected O, but got Unknown
//IL_0571: Expected O, but got Unknown
//IL_0585: Unknown result type (might be due to invalid IL or missing references)
//IL_059a: Unknown result type (might be due to invalid IL or missing references)
//IL_05a4: Expected O, but got Unknown
//IL_05a4: Expected O, but got Unknown
//IL_05b8: Unknown result type (might be due to invalid IL or missing references)
//IL_05cd: Unknown result type (might be due to invalid IL or missing references)
//IL_05d7: Expected O, but got Unknown
//IL_05d7: Expected O, but got Unknown
//IL_05eb: Unknown result type (might be due to invalid IL or missing references)
//IL_05fc: Unknown result type (might be due to invalid IL or missing references)
//IL_0606: Expected O, but got Unknown
//IL_0606: Expected O, but got Unknown
//IL_061a: Unknown result type (might be due to invalid IL or missing references)
//IL_062f: Unknown result type (might be due to invalid IL or missing references)
//IL_0639: Expected O, but got Unknown
//IL_0639: Expected O, but got Unknown
//IL_064d: Unknown result type (might be due to invalid IL or missing references)
//IL_0662: Unknown result type (might be due to invalid IL or missing references)
//IL_066c: Expected O, but got Unknown
//IL_066c: Expected O, but got Unknown
ConfigFile = new ConfigFile(Paths.ConfigPath + "\\ViralTremors.cfg", true);
ServerUri = ConfigFile.Bind<string>("Devices", "Server Uri", "ws://localhost:12345", "URI of the Intiface server.");
Player.DamageTaken.Enabled = ConfigFile.Bind<bool>(new ConfigDefinition("Vibrations.Damage", "Enabled"), true, new ConfigDescription("Vibrate when you receive damage", (AcceptableValueBase)null, Array.Empty<object>()));
Player.DamageTaken.Duration = ConfigFile.Bind<float>(new ConfigDefinition("Vibrations.Damage", "Duration"), 1f, new ConfigDescription("Length of time to vibrate for", (AcceptableValueBase)null, Array.Empty<object>()));
Player.Death.Enabled = ConfigFile.Bind<bool>(new ConfigDefinition("Vibrations.Death", "Enabled"), true, new ConfigDescription("Vibrate when you die", (AcceptableValueBase)null, Array.Empty<object>()));
Player.Death.Duration = ConfigFile.Bind<float>(new ConfigDefinition("Vibrations.Death", "Duration"), 1f, new ConfigDescription("Length of time to vibrate for", (AcceptableValueBase)null, Array.Empty<object>()));
Player.Death.Strength = ConfigFile.Bind<float>(new ConfigDefinition("Vibrations.Death", "Strength"), 1f, new ConfigDescription("The strength of the vibration (value from 0.0 to 1.0)", (AcceptableValueBase)null, Array.Empty<object>()));
Player.Revive.Enabled = ConfigFile.Bind<bool>(new ConfigDefinition("Vibrations.Revive", "Enabled"), true, new ConfigDescription("Vibrate when you get revived", (AcceptableValueBase)null, Array.Empty<object>()));
Player.Revive.Duration = ConfigFile.Bind<float>(new ConfigDefinition("Vibrations.Revive", "Duration"), 1f, new ConfigDescription("Length of time to vibrate for", (AcceptableValueBase)null, Array.Empty<object>()));
Player.Revive.Strength = ConfigFile.Bind<float>(new ConfigDefinition("Vibrations.Revive", "Strength"), 1f, new ConfigDescription("The strength of the vibration (value from 0.0 to 1.0)", (AcceptableValueBase)null, Array.Empty<object>()));
Player.Heal.Enabled = ConfigFile.Bind<bool>(new ConfigDefinition("Vibrations.Heal", "Enabled"), true, new ConfigDescription("Vibrate when you get hugged", (AcceptableValueBase)null, Array.Empty<object>()));
Player.Heal.Duration = ConfigFile.Bind<float>(new ConfigDefinition("Vibrations.Heal", "Duration"), 1f, new ConfigDescription("Length of time to vibrate for", (AcceptableValueBase)null, Array.Empty<object>()));
Player.Heal.Strength = ConfigFile.Bind<float>(new ConfigDefinition("Vibrations.Heal", "Strength"), 1f, new ConfigDescription("The strength of the vibration (value from 0.0 to 1.0)", (AcceptableValueBase)null, Array.Empty<object>()));
Enemy.Weeping.Capture.Enabled = ConfigFile.Bind<bool>(new ConfigDefinition("Vibrations.WeepingEnemy.Capture", "Enabled"), true, new ConfigDescription("Vibrate when you get captured by the weeping enemy", (AcceptableValueBase)null, Array.Empty<object>()));
Enemy.Weeping.Capture.Duration = ConfigFile.Bind<float>(new ConfigDefinition("Vibrations.WeepingEnemy.Capture", "Duration"), 1f, new ConfigDescription("Length of time to vibrate for", (AcceptableValueBase)null, Array.Empty<object>()));
Enemy.Weeping.Capture.Strength = ConfigFile.Bind<float>(new ConfigDefinition("Vibrations.WeepingEnemy.Capture", "Strength"), 1f, new ConfigDescription("The strength of the vibration (value from 0.0 to 1.0)", (AcceptableValueBase)null, Array.Empty<object>()));
DivingBell.Traveling.Enabled = ConfigFile.Bind<bool>(new ConfigDefinition("Vibrations.DivingBell.Travelling", "Enabled"), true, new ConfigDescription("Vibrate when you go to the underworld", (AcceptableValueBase)null, Array.Empty<object>()));
DivingBell.Traveling.Duration = ConfigFile.Bind<float>(new ConfigDefinition("Vibrations.DivingBell.Travelling", "Duration"), 1f, new ConfigDescription("Length of time to vibrate for", (AcceptableValueBase)null, Array.Empty<object>()));
DivingBell.Traveling.Strength = ConfigFile.Bind<float>(new ConfigDefinition("Vibrations.DivingBell.Travelling", "Strength"), 1f, new ConfigDescription("The strength of the vibration (value from 0.0 to 1.0)", (AcceptableValueBase)null, Array.Empty<object>()));
DivingBell.Returning.Enabled = ConfigFile.Bind<bool>(new ConfigDefinition("Vibrations.DivingBell.Returning", "Enabled"), true, new ConfigDescription("Vibrate when you go to the surface", (AcceptableValueBase)null, Array.Empty<object>()));
DivingBell.Returning.Duration = ConfigFile.Bind<float>(new ConfigDefinition("Vibrations.DivingBell.Returning", "Duration"), 1f, new ConfigDescription("Length of time to vibrate for", (AcceptableValueBase)null, Array.Empty<object>()));
DivingBell.Returning.Strength = ConfigFile.Bind<float>(new ConfigDefinition("Vibrations.DivingBell.Returning", "Strength"), 1f, new ConfigDescription("The strength of the vibration (value from 0.0 to 1.0)", (AcceptableValueBase)null, Array.Empty<object>()));
Item.ShockStick.Enabled = ConfigFile.Bind<bool>(new ConfigDefinition("Vibrations.ShockStick", "Enabled"), true, new ConfigDescription("Vibrate when you shock something/someone", (AcceptableValueBase)null, Array.Empty<object>()));
Item.ShockStick.Duration = ConfigFile.Bind<float>(new ConfigDefinition("Vibrations.ShockStick", "Duration"), 1f, new ConfigDescription("Length of time to vibrate for", (AcceptableValueBase)null, Array.Empty<object>()));
Item.ShockStick.Strength = ConfigFile.Bind<float>(new ConfigDefinition("Vibrations.ShockStick", "Strength"), 1f, new ConfigDescription("The strength of the vibration (value from 0.0 to 1.0)", (AcceptableValueBase)null, Array.Empty<object>()));
MoneyAdded.Enabled = ConfigFile.Bind<bool>(new ConfigDefinition("Vibrations.MoneyAdded", "Enabled"), true, new ConfigDescription("Vibrate when you get money", (AcceptableValueBase)null, Array.Empty<object>()));
MoneyAdded.Duration = ConfigFile.Bind<float>(new ConfigDefinition("Vibrations.MoneyAdded", "Duration"), 1f, new ConfigDescription("Length of time to vibrate for", (AcceptableValueBase)null, Array.Empty<object>()));
MoneyAdded.Strength = ConfigFile.Bind<float>(new ConfigDefinition("Vibrations.MoneyAdded", "Strength"), 1f, new ConfigDescription("The strength of the vibration (value from 0.0 to 1.0)", (AcceptableValueBase)null, Array.Empty<object>()));
Jumpscare.Enabled = ConfigFile.Bind<bool>(new ConfigDefinition("Vibrations.Jumpscare", "Enabled"), true, new ConfigDescription("Vibrate when you get jumpscared", (AcceptableValueBase)null, Array.Empty<object>()));
Jumpscare.Duration = ConfigFile.Bind<float>(new ConfigDefinition("Vibrations.Jumpscare", "Duration"), 1f, new ConfigDescription("Length of time to vibrate for", (AcceptableValueBase)null, Array.Empty<object>()));
Jumpscare.Strength = ConfigFile.Bind<float>(new ConfigDefinition("Vibrations.Jumpscare", "Strength"), 1f, new ConfigDescription("The strength of the vibration (value from 0.0 to 1.0)", (AcceptableValueBase)null, Array.Empty<object>()));
BombExplosion.Enabled = ConfigFile.Bind<bool>(new ConfigDefinition("Vibrations.BombExplosion", "Enabled"), true, new ConfigDescription("Vibrate when a bomb explodes", (AcceptableValueBase)null, Array.Empty<object>()));
BombExplosion.Duration = ConfigFile.Bind<float>(new ConfigDefinition("Vibrations.BombExplosion", "Duration"), 1f, new ConfigDescription("Length of time to vibrate for", (AcceptableValueBase)null, Array.Empty<object>()));
BombExplosion.Strength = ConfigFile.Bind<float>(new ConfigDefinition("Vibrations.BombExplosion", "Strength"), 1f, new ConfigDescription("The strength of the vibration (value from 0.0 to 1.0)", (AcceptableValueBase)null, Array.Empty<object>()));
}
}
public class DeviceManager
{
private List<ButtplugClientDevice> ConnectedDevices { get; set; }
private ButtplugClient ButtplugClient { get; set; }
public DeviceManager(string clientName)
{
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_001d: Expected O, but got Unknown
ConnectedDevices = new List<ButtplugClientDevice>();
ButtplugClient = new ButtplugClient(clientName);
ViralTremors.Logger.LogInfo((object)("BP client created for " + clientName));
ButtplugClient.DeviceAdded += HandleDeviceAdded;
ButtplugClient.DeviceRemoved += HandleDeviceRemoved;
}
public bool IsConnected()
{
return ButtplugClient.Connected;
}
public async void ConnectDevices()
{
if (ButtplugClient.Connected)
{
return;
}
try
{
ViralTremors.Logger.LogInfo((object)("Attempting to connect to Intiface server at " + Config.ServerUri.Value));
await ButtplugClient.ConnectAsync((IButtplugClientConnector)new ButtplugWebsocketConnector(new Uri(Config.ServerUri.Value)), default(CancellationToken));
ViralTremors.Logger.LogInfo((object)"Connection successful. Beginning scan for devices");
await ButtplugClient.StartScanningAsync(default(CancellationToken));
}
catch (ButtplugException val)
{
ButtplugException arg = val;
ViralTremors.Logger.LogError((object)"Attempt to connect to devices failed. Ensure Intiface is running and attempt to reconnect from the 'Devices' section in the mod's in-game settings.");
ViralTremors.Logger.LogDebug((object)$"ButtplugIO error occured while connecting devices: {arg}");
}
}
public void VibrateConnectedDevicesWithDuration(float intensity, float time)
{
ConnectedDevices.ForEach(Action);
async void Action(ButtplugClientDevice device)
{
await device.VibrateAsync((double)Mathf.Clamp(intensity, 0f, 1f));
await Task.Delay((int)(time * 1000f));
await device.VibrateAsync(0.0);
}
}
public void VibrateConnectedDevices(double intensity)
{
ConnectedDevices.ForEach(Action);
async void Action(ButtplugClientDevice device)
{
await device.VibrateAsync((double)Mathf.Clamp((float)intensity, 0f, 1f));
}
}
public void StopConnectedDevices()
{
ConnectedDevices.ForEach(async delegate(ButtplugClientDevice device)
{
await device.Stop();
});
}
internal void CleanUp()
{
StopConnectedDevices();
}
private void HandleDeviceAdded(object sender, DeviceAddedEventArgs args)
{
if (!IsVibratableDevice(args.Device))
{
ViralTremors.Logger.LogInfo((object)(args.Device.Name + " was detected but ignored due to it not being vibratable."));
return;
}
ViralTremors.Logger.LogInfo((object)(args.Device.Name + " connected to client " + ButtplugClient.Name));
ConnectedDevices.Add(args.Device);
}
private void HandleDeviceRemoved(object sender, DeviceRemovedEventArgs args)
{
if (IsVibratableDevice(args.Device))
{
ViralTremors.Logger.LogInfo((object)(args.Device.Name + " disconnected from client " + ButtplugClient.Name));
ConnectedDevices.Remove(args.Device);
}
}
private static bool IsVibratableDevice(ButtplugClientDevice device)
{
return device.VibrateAttributes.Count > 0;
}
}
}
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class IgnoresAccessChecksToAttribute : Attribute
{
public IgnoresAccessChecksToAttribute(string assemblyName)
{
}
}
}